[m-rev.] for review: Thread I/O state through time routines that depend on the time zone.

Peter Wang novalazy at gmail.com
Sun Jun 5 14:57:15 AEST 2016


library/time.m:
	Deprecate non-pure functions `localtime/1' and `mktime/1'.
	They depend on the current time zone, which depends on the
	environment that the program is running in, and may also be
	changed at run time (e.g. by setting the environment variable
	"TZ").

	Add replacement predicates `localtime/4' and `mktime/4' that
	thread the I/O state.

	Deprecate the non-pure function `ctime/1'.  It does not seem
	necessary to add a pure replacement for it, being a minor
	convenience at best.

	Try to clarify some documentation.

library/calendar.m:
	Conform to above changes.

tests/hard_coded/dst_test.m:
tests/hard_coded/time_test.m
	Conform to above changes.

	Update code style.

extras/curs/samples/nibbles.m:
extras/graphics/easyx/samples/bounce.m:
extras/graphics/easyx/samples/mclock.m:
extras/log4m/log4m.m:
	Conform to above changes.

NEWS:
	Announce changes.
---
 NEWS                                   |  11 ++++
 extras/curs/samples/nibbles.m          |   2 +-
 extras/graphics/easyx/samples/bounce.m |   2 +-
 extras/graphics/easyx/samples/mclock.m |   2 +-
 extras/log4m/log4m.m                   |   2 +-
 library/calendar.m                     |   4 +-
 library/time.m                         |  79 ++++++++++++++++++------
 tests/hard_coded/dst_test.m            | 107 +++++++++++++++++----------------
 tests/hard_coded/time_test.m           |  24 ++++----
 9 files changed, 143 insertions(+), 90 deletions(-)

diff --git a/NEWS b/NEWS
index 5628bb5..3a721cd 100644
--- a/NEWS
+++ b/NEWS
@@ -336,6 +336,17 @@ Changes to the Mercury standard library:
 
    - is_empty/1
 
+* The following predicates have been added to the time module:
+
+   - localtime/4
+   - mktime/4
+
+  The following functions in the time module have been deprecated:
+
+   - localtime/1
+   - mktime/1
+   - ctime/1
+
 Changes to the Mercury compiler:
 
 * We have added a new option --warn-dead-preds. While the existing option
diff --git a/extras/curs/samples/nibbles.m b/extras/curs/samples/nibbles.m
index 11ccb42..d08f262 100644
--- a/extras/curs/samples/nibbles.m
+++ b/extras/curs/samples/nibbles.m
@@ -73,7 +73,7 @@
 
 main(!IO) :-
 	time.time(Now, !IO),
-	time.localtime(Now) = LocalNow,
+	time.localtime(Now, LocalNow, !IO),
 	random.init(LocalNow ^ tm_min * 60 + LocalNow ^ tm_sec, RS),
 	curs.start(!IO),
 	curs.nodelay(yes, !IO),
diff --git a/extras/graphics/easyx/samples/bounce.m b/extras/graphics/easyx/samples/bounce.m
index f73f3dc..ce1bd75 100644
--- a/extras/graphics/easyx/samples/bounce.m
+++ b/extras/graphics/easyx/samples/bounce.m
@@ -240,7 +240,7 @@ main(!IO) :-
     easyx.get_colour_from_name(Window, "yellow", Yellow, !IO),
     Colours = [Black, White, Red, Green, Blue, Yellow],
     time.time(Time, !IO),
-    TM = localtime(Time),
+    localtime(Time, TM, !IO),
     random.init(60 * TM^tm_min + TM^tm_sec, Rnd0),
     add_new_ball(Colours, [], Objects, Rnd0, Rnd),
     Paddle = new_paddle(White),
diff --git a/extras/graphics/easyx/samples/mclock.m b/extras/graphics/easyx/samples/mclock.m
index d91b782..526ab93 100644
--- a/extras/graphics/easyx/samples/mclock.m
+++ b/extras/graphics/easyx/samples/mclock.m
@@ -50,7 +50,7 @@ main(!IO) :-
 draw_clock(Window, Blue, White, !IO) :-
     
     time.time(Time, !IO),
-    TM   = localtime(Time),
+    localtime(Time, TM, !IO),
     Hour = TM^tm_hour,
     Min  = TM^tm_min,
 
diff --git a/extras/log4m/log4m.m b/extras/log4m/log4m.m
index f50da63..d70a825 100644
--- a/extras/log4m/log4m.m
+++ b/extras/log4m/log4m.m
@@ -661,7 +661,7 @@ set_log(Log, !IO) :-
 
 write_format(A, Id, Level, _Message, date, !IO) :-
     time(Time, !IO),
-    TM = localtime(Time),
+    localtime(Time, TM, !IO),
 	TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, _YD, _WD, _DST),
     Date = string__format("%4d-%02d-%02d %02d:%02d:%02d",
             [i(Yr+1900), i(Mnt+1), i(MD), i(Hrs), i(Min), i(Sec)]),
diff --git a/library/calendar.m b/library/calendar.m
index 9cc6cfb..9624043 100644
--- a/library/calendar.m
+++ b/library/calendar.m
@@ -1150,7 +1150,7 @@ unpack_date(date(Year, Month, Day, Hour, Minute, Second, MicroSecond),
 
 current_local_time(Now, !IO) :-
     time.time(TimeT, !IO),
-    TM = time.localtime(TimeT),
+    time.localtime(TimeT, TM, !IO),
     Now = tm_to_date(TM).
 
 current_utc_time(Now, !IO) :-
@@ -1172,7 +1172,7 @@ tm_to_date(TM) = Date :-
 
 local_time_offset(TZ, !IO) :-
     time.time(TimeT, !IO),
-    LocalTM = time.localtime(TimeT),
+    time.localtime(TimeT, LocalTM, !IO),
     GMTM = time.gmtime(TimeT),
     LocalTime = tm_to_date(LocalTM),
     GMTime = tm_to_date(GMTM),
diff --git a/library/time.m b/library/time.m
index ea9c569..13e3a1e 100644
--- a/library/time.m
+++ b/library/time.m
@@ -150,26 +150,39 @@
     %
 :- func difftime(time_t, time_t) = float.
 
-    % localtime(Time) = TM:
+    % localtime(Time, TM, !IO):
     %
-    % Converts the calendar time `Time' to a broken-down representation,
-    % expressed relative to the user's specified time zone.
+    % Converts the (simple) calendar time `Time' to a broken-down
+    % representation `TM', expressed relative to the user's specified time
+    % zone.
     %
+:- pred localtime(time_t::in, tm::out, io::di, io::uo) is det.
+
+    % This function is deprecated because the user's specified time zone is
+    % not reflected in its arguments.
+    %
+:- pragma obsolete(localtime/1).
 :- func localtime(time_t) = tm.
 
     % gmtime(Time) = TM:
     %
-    % Converts the calendar time `Time' to a broken-down representation,
-    % expressed as UTC (Universal Coordinated Time).
+    % Converts the (simple) calendar time `Time' to a broken-down
+    % representation `TM', expressed as UTC (Universal Coordinated Time).
     %
 :- func gmtime(time_t) = tm.
 
     % mktime(TM) = Time:
     %
-    % Converts the broken-down local time value to calendar time.
-    % It also normalises the value by filling in day of week and day of year
-    % based on the other components.
+    % Converts the broken-down time value `TM' to a (simple) calendar time
+    % `Time'. That is, `TM' is relative to the user's specified time zone.
+    % The `tm_wday' and `tm_yday' fields of `TM' are ignored.
+    %
+:- pred mktime(tm::in, time_t::out, io::di, io::uo) is det.
+
+    % This function is deprecated because the user's specified time zone is
+    % not reflected in its arguments.
     %
+:- pragma obsolete(mktime/1).
 :- func mktime(tm) = time_t.
 
 %---------------------------------------------------------------------------%
@@ -186,6 +199,13 @@
     % Converts the calendar time value `Time' to a string in a standard format
     % (i.e. same as "asctime (localtime (<time>))").
     %
+    % This function is deprecated because the user's specified time zone is
+    % not reflected in its arguments. New code should write:
+    %
+    %   localtime(Time, TM, !IO),
+    %   String = asctime(TM)
+    %
+:- pragma obsolete(ctime/1).
 :- func ctime(time_t) = string.
 
 %---------------------------------------------------------------------------%
@@ -575,17 +595,27 @@ time.difftime(time_t(T1), time_t(T0)) = Diff :-
 
 %---------------------------------------------------------------------------%
 
+:- pragma promise_pure(time.localtime/4).
+
+time.localtime(time_t(Time), TM, !IO) :-
+    semipure time.c_localtime(Time, Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, N),
+    TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, int_to_maybe_dst(N)).
+
+    % localtime/1 is not really pure, that's why it is deprecated.
+    %
+:- pragma promise_pure(time.localtime/1).
+
 time.localtime(time_t(Time)) = TM :-
-    time.c_localtime(Time, Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, N),
+    semipure time.c_localtime(Time, Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, N),
     TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, int_to_maybe_dst(N)).
 
-:- pred time.c_localtime(time_t_rep::in, int::out, int::out, int::out,
+:- semipure pred time.c_localtime(time_t_rep::in, int::out, int::out, int::out,
     int::out, int::out, int::out, int::out, int::out, int::out) is det.
 
 :- pragma foreign_proc("C",
     time.c_localtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
         Min::out, Sec::out, YD::out, WD::out, N::out),
-    [will_not_call_mercury, promise_pure, not_thread_safe],
+    [will_not_call_mercury, promise_semipure, not_thread_safe],
 "
     struct tm   *p;
     time_t      t;
@@ -609,7 +639,7 @@ time.localtime(time_t(Time)) = TM :-
 :- pragma foreign_proc("C#",
     time.c_localtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
         Min::out, Sec::out, YD::out, WD::out, N::out),
-    [will_not_call_mercury, promise_pure],
+    [will_not_call_mercury, promise_semipure],
 "{
     System.DateTime t = Time.ToLocalTime();
 
@@ -636,7 +666,7 @@ time.localtime(time_t(Time)) = TM :-
 :- pragma foreign_proc("Java",
     time.c_localtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
         Min::out, Sec::out, YD::out, WD::out, N::out),
-    [will_not_call_mercury, promise_pure, may_not_duplicate],
+    [will_not_call_mercury, promise_semipure, may_not_duplicate],
 "
     java.util.GregorianCalendar gc = new java.util.GregorianCalendar();
 
@@ -813,23 +843,32 @@ int_to_maybe_dst(N) = DST :-
 
 %---------------------------------------------------------------------------%
 
-%:- func time.mktime(tm) = time_t.
+:- pragma promise_pure(time.mktime/4).
+
+time.mktime(TM, time_t(Time), !IO) :-
+    TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, DST),
+    semipure time.c_mktime(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD,
+        maybe_dst_to_int(DST), Time).
+
+    % mktime/1 is not really pure, that's why it is deprecated.
+    %
+:- pragma promise_pure(time.mktime/1).
 
 time.mktime(TM) = time_t(Time) :-
     TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, DST),
-    time.c_mktime(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD,
+    semipure time.c_mktime(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD,
         maybe_dst_to_int(DST), Time).
 
     % NOTE: mktime() modifies tzname so is strictly impure.
     % We do not expose tzname through a Mercury interface, though.
     %
-:- pred time.c_mktime(int::in, int::in, int::in, int::in, int::in, int::in,
-    int::in, int::in, int::in, time_t_rep::out) is det.
+:- semipure pred time.c_mktime(int::in, int::in, int::in, int::in, int::in,
+    int::in, int::in, int::in, int::in, time_t_rep::out) is det.
 
 :- pragma foreign_proc("C",
     time.c_mktime(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in, Sec::in,
         YD::in, WD::in, N::in, Time::out),
-    [will_not_call_mercury, promise_pure, not_thread_safe],
+    [will_not_call_mercury, promise_semipure, not_thread_safe],
  "{
     struct tm t;
 
@@ -848,7 +887,7 @@ time.mktime(TM) = time_t(Time) :-
 :- pragma foreign_proc("C#",
     time.c_mktime(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in, Sec::in,
         _YD::in, _WD::in, _N::in, Time::out),
-    [will_not_call_mercury, promise_pure],
+    [will_not_call_mercury, promise_semipure],
  "{
     // We don't use YD, WD and N.
     // XXX Ignoring N the daylight savings time indicator is bad
@@ -864,7 +903,7 @@ time.mktime(TM) = time_t(Time) :-
 :- pragma foreign_proc("Java",
     time.c_mktime(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in, Sec::in,
         _YD::in, _WD::in, N::in, Time::out),
-    [will_not_call_mercury, promise_pure, may_not_duplicate],
+    [will_not_call_mercury, promise_semipure, may_not_duplicate],
 "
     java.util.GregorianCalendar gc = new java.util.GregorianCalendar(
         Yr + 1900, Mnt, MD, Hrs, Min, Sec);
diff --git a/tests/hard_coded/dst_test.m b/tests/hard_coded/dst_test.m
index a318848..66f4488 100644
--- a/tests/hard_coded/dst_test.m
+++ b/tests/hard_coded/dst_test.m
@@ -21,67 +21,68 @@
 :- import_module maybe.
 :- import_module time.
 
-main -->
-    difftest,
-    io__nl,
-    local_vs_gm_test,
-    io__nl.
+main(!IO) :-
+    difftest(!IO),
+    io.nl(!IO),
+    local_vs_gm_test(!IO),
+    io.nl(!IO).
 
-:- pred difftest(io__state::di, io__state::uo) is det.
+:- pred difftest(io::di, io::uo) is det.
 
-difftest -->
+difftest(!IO) :-
     % Sunday 2003-10-26 01:30:00
-    { BeforeStart = mktime(tm(103, 9, 26, 1, 30, 0, 298, 0,
-                yes(standard_time))) },
+    mktime(tm(103, 9, 26, 1, 30, 0, 298, 0, yes(standard_time)),
+        BeforeStart, !IO),
     % Sunday 2003-10-26 03:30:00
-    { AfterStart = mktime(tm(103, 9, 26, 3, 30, 0, 298, 0,
-                yes(daylight_time))) },
+    mktime(tm(103, 9, 26, 3, 30, 0, 298, 0, yes(daylight_time)),
+        AfterStart, !IO),
     % difference should be 1 hour
-    ( { difftime(AfterStart, BeforeStart) = 3600.0 } ->
-        io__write_string("start DST succeeded\n")
-    ;
-        io__write_string("start DST failed\n")
+    ( if difftime(AfterStart, BeforeStart) = 3600.0 then
+        io.write_string("start DST succeeded\n", !IO)
+    else
+        io.write_string("start DST failed\n", !IO)
     ),
 
     % Sunday 2004-02-28 02:30:00 (occurs twice)
-    { BeforeEnd = mktime(tm(104, 2, 28, 2, 30, 0, 87, 0,
-                yes(daylight_time))) },
-    { AfterEnd = mktime(tm(104, 2, 28, 2, 30, 0, 87, 0,
-                yes(standard_time))) },
+    mktime(tm(104, 2, 28, 2, 30, 0, 87, 0, yes(daylight_time)),
+        BeforeEnd, !IO),
+    mktime(tm(104, 2, 28, 2, 30, 0, 87, 0, yes(standard_time)),
+        AfterEnd, !IO),
     % difference should be 1 hour
-    ( { difftime(AfterEnd, BeforeEnd) = 3600.0 } ->
-        io__write_string("end DST succeeded\n")
-    ;
-        io__write_string("end DST failed\n")
+    ( if difftime(AfterEnd, BeforeEnd) = 3600.0 then
+        io.write_string("end DST succeeded\n", !IO)
+    else
+        io.write_string("end DST failed\n", !IO)
     ).
 
-:- pred local_vs_gm_test(io__state::di, io__state::uo) is det.
-
-local_vs_gm_test -->
-    local_vs_gm_test(tm(103, 9, 26, 1, 59, 0, 298, 0, yes(standard_time))),
-    local_vs_gm_test(tm(103, 9, 26, 3, 0, 0, 298, 0, yes(daylight_time))),
-    local_vs_gm_test(tm(103, 9, 26, 3, 1, 0, 298, 0, yes(daylight_time))),
-    io__nl,
-
-    local_vs_gm_test(tm(104, 2, 28, 1, 59, 0, 87, 0, yes(daylight_time))),
-    local_vs_gm_test(tm(104, 2, 28, 2, 0, 0, 87, 0, yes(daylight_time))),
-    local_vs_gm_test(tm(104, 2, 28, 2, 1, 0, 87, 0, yes(daylight_time))),
-    io__nl,
-
-    local_vs_gm_test(tm(104, 2, 28, 2, 59, 0, 87, 0, yes(daylight_time))),
-    local_vs_gm_test(tm(104, 2, 28, 2, 0, 0, 87, 0, yes(standard_time))),
-    local_vs_gm_test(tm(104, 2, 28, 2, 1, 0, 87, 0, yes(standard_time))),
-    io__nl,
-
-    local_vs_gm_test(tm(104, 2, 28, 2, 59, 0, 87, 0, yes(standard_time))),
-    local_vs_gm_test(tm(104, 2, 28, 3, 0, 0, 87, 0, yes(standard_time))),
-    local_vs_gm_test(tm(104, 2, 28, 3, 1, 0, 87, 0, yes(standard_time))).
-
-:- pred local_vs_gm_test(tm::in, io__state::di, io__state::uo) is det.
-
-local_vs_gm_test(TM) -->
-    { Time = mktime(TM) },
-    io__write_string("Local:\t"),
-    io__write_string(asctime(localtime(Time))),
-    io__write_string("GMT:\t"),
-    io__write_string(asctime(gmtime(Time))).
+:- pred local_vs_gm_test(io::di, io::uo) is det.
+
+local_vs_gm_test(!IO) :-
+    local_vs_gm(tm(103, 9, 26, 1, 59, 0, 298, 0, yes(standard_time)), !IO),
+    local_vs_gm(tm(103, 9, 26, 3, 0, 0, 298, 0, yes(daylight_time)), !IO),
+    local_vs_gm(tm(103, 9, 26, 3, 1, 0, 298, 0, yes(daylight_time)), !IO),
+    io.nl(!IO),
+
+    local_vs_gm(tm(104, 2, 28, 1, 59, 0, 87, 0, yes(daylight_time)), !IO),
+    local_vs_gm(tm(104, 2, 28, 2, 0, 0, 87, 0, yes(daylight_time)), !IO),
+    local_vs_gm(tm(104, 2, 28, 2, 1, 0, 87, 0, yes(daylight_time)), !IO),
+    io.nl(!IO),
+
+    local_vs_gm(tm(104, 2, 28, 2, 59, 0, 87, 0, yes(daylight_time)), !IO),
+    local_vs_gm(tm(104, 2, 28, 2, 0, 0, 87, 0, yes(standard_time)), !IO),
+    local_vs_gm(tm(104, 2, 28, 2, 1, 0, 87, 0, yes(standard_time)), !IO),
+    io.nl(!IO),
+
+    local_vs_gm(tm(104, 2, 28, 2, 59, 0, 87, 0, yes(standard_time)), !IO),
+    local_vs_gm(tm(104, 2, 28, 3, 0, 0, 87, 0, yes(standard_time)), !IO),
+    local_vs_gm(tm(104, 2, 28, 3, 1, 0, 87, 0, yes(standard_time)), !IO).
+
+:- pred local_vs_gm(tm::in, io::di, io::uo) is det.
+
+local_vs_gm(TM, !IO) :-
+    mktime(TM, Time, !IO),
+    io.write_string("Local:\t", !IO),
+    localtime(Time, LocalTM, !IO),
+    io.write_string(asctime(LocalTM), !IO),
+    io.write_string("GMT:\t", !IO),
+    io.write_string(asctime(gmtime(Time)), !IO).
diff --git a/tests/hard_coded/time_test.m b/tests/hard_coded/time_test.m
index 22cb306..2e00bab 100644
--- a/tests/hard_coded/time_test.m
+++ b/tests/hard_coded/time_test.m
@@ -8,7 +8,7 @@
 
 :- import_module io.
 
-:- pred main(io__state::di, io__state::uo) is det.
+:- pred main(io::di, io::uo) is det.
 
 :- implementation.
 
@@ -16,16 +16,18 @@
 :- import_module time.
 :- import_module float.
 
-main -->
-    time(Time),
-    { Diff = difftime(Time, mktime(localtime(Time))) },
-    ( { (Diff >= 0.0, Diff < 1.0) } ->
-        io__write_string("mktime succeeded\n")
-    ;
-        io__write_string("mktime failed\n")
+main(!IO) :-
+    time(Time, !IO),
+    localtime(Time, LocalTM, !IO),
+    mktime(LocalTM, MkTime, !IO),
+    Diff = difftime(Time, MkTime),
+    ( if Diff >= 0.0, Diff < 1.0 then
+        io.write_string("mktime succeeded\n", !IO)
+    else
+        io.write_string("mktime failed\n", !IO)
     ),
 
     % Sunday 2001-01-07 03:02:01
-    { TM = tm(101, 0, 7, 3, 2, 1, 6, 0, no) },
-    io__write_string(asctime(TM)),
-    io__nl.
+    TM = tm(101, 0, 7, 3, 2, 1, 6, 0, no),
+    io.write_string(asctime(TM), !IO),
+    io.nl(!IO).
-- 
2.8.2



More information about the reviews mailing list