[m-rev.] for review: improve error checking in the time module

Julien Fischer jfischer at opturion.com
Sun Aug 8 23:25:58 AEST 2021


For review by anyone.

I will address the XXXs, or at least better document the differences
between the backends in subsequent diffs.

--------------------------------

Improve error checking in the time module.

Always check for errors when calling time related functions or methods in the
target language. The absence of such checking means that it is possible to for
Mercury code to cause target language level exceptions in the C# and Java
grades.

Add some XXX comments about other such issues in the time module.

Delete tabled_for_io foreign proc attributes from C# and Java foreign
procs; they do not have any effect.

library/time.m:
     As above.

Julien.

diff --git a/library/time.m b/library/time.m
index 234e113..e61c2ce 100644
--- a/library/time.m
+++ b/library/time.m
@@ -186,6 +186,7 @@

  :- implementation.

+:- import_module bool.
  :- import_module exception.
  :- import_module int.
  :- import_module list.
@@ -205,7 +206,10 @@
          #include <unistd.h>
      #endif

-    #include ""mercury_timing.h"" // for MR_CLOCK_TICKS_PER_SECOND
+    #include ""mercury_timing.h""       // for MR_CLOCK_TICKS_PER_SECOND
+    #include ""mercury_runtime_util.h"" // For MR_sterror.
+    #include ""mercury_regs.h""         // For MR_{save,restore}_transient_hp
+    #include ""mercury_string.h""       // For MR_make_aligned_string_copy etc.
  ").

      % We use a no-tag wrapper type for time_t, rather than defining it as an
@@ -257,7 +261,7 @@ clock(Result, !IO) :-
  ").
  :- pragma foreign_proc("C#",
      c_clock(Ret::out, _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure],
  "
      // XXX Ticks is long in .NET!
      Ret = (int) System.Diagnostics.Process.GetCurrentProcess().
@@ -265,7 +269,7 @@ clock(Result, !IO) :-
  ").
  :- pragma foreign_proc("Java",
      c_clock(Ret::out, _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure],
  "
      java.lang.management.ThreadMXBean bean =
          java.lang.management.ManagementFactory.getThreadMXBean();
@@ -321,13 +325,13 @@ time(Result, !IO) :-
  ").
  :- pragma foreign_proc("C#",
      c_time(Ret::out, _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure],
  "
      Ret = System.DateTime.UtcNow;
  ").
  :- pragma foreign_proc("Java",
      c_time(Ret::out, _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure],
  "
      Ret = java.time.Instant.now();
  ").
@@ -424,7 +428,7 @@ times(Tms, Result, !IO) :-
  :- pragma foreign_proc("Java",
      c_times(Ret::out, Ut::out, St::out, CUt::out, CSt::out,
          _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, may_not_duplicate],
+    [will_not_call_mercury, promise_pure, may_not_duplicate],
  "
      // We can only keep the lower 31 bits of the timestamp.
      Ret = (int) (System.currentTimeMillis() & 0x7fffffff);
@@ -454,7 +458,7 @@ times(Tms, Result, !IO) :-
  :- pragma foreign_proc("C#",
      c_times(Ret::out, Ut::out, St::out, CUt::out, CSt::out,
          _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, may_not_duplicate],
+    [will_not_call_mercury, promise_pure, may_not_duplicate],
  "
      Ret = (int) System.DateTime.UtcNow.Ticks;

@@ -478,10 +482,10 @@ times(Tms, Result, !IO) :-
  :- pragma foreign_proc("C#",
      clk_tck = (Ret::out),
      [will_not_call_mercury, promise_pure, thread_safe],
-"{
+"
      // TicksPerSecond is guaranteed to be 10,000,000
      Ret = (int) System.TimeSpan.TicksPerSecond;
-}").
+").

  clk_tck = Ret :-
      Ret0 = c_clk_tck,
@@ -546,16 +550,24 @@ difftime(time_t(T1), time_t(T0)) = Diff :-

  localtime(Time, TM, !IO) :-
      Time = time_t(RawTime),
-    c_localtime(RawTime, Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, N, !IO),
-    TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, int_to_maybe_dst(N)).
+    c_localtime(RawTime, IsOk, Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, N,
+        ErrorMsg, !IO),
+    (
+        IsOk = yes,
+        TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, int_to_maybe_dst(N))
+    ;
+        IsOk = no,
+        throw(time_error("time.localtime: conversion failed: " ++ ErrorMsg))
+    ).

-:- pred c_localtime(time_t_rep::in, int::out, int::out, int::out,
-    int::out, int::out, int::out, int::out, int::out, int::out,
+:- pred c_localtime(time_t_rep::in, bool::out, int::out, int::out, int::out,
+    int::out, int::out, int::out, int::out, int::out, int::out, string::out,
      io::di, io::uo) is det.

  :- pragma foreign_proc("C",
-    c_localtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
-        Min::out, Sec::out, YD::out, WD::out, N::out, _IO0::di, _IO::uo),
+    c_localtime(Time::in, IsOk::out, Yr::out, Mnt::out, MD::out, Hrs::out,
+        Min::out, Sec::out, YD::out, WD::out, N::out, ErrorMsg::out,
+        _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, not_thread_safe, tabled_for_io],
  "
      struct tm   *p;
@@ -564,24 +576,36 @@ localtime(Time, TM, !IO) :-
      t = Time;

      p = localtime(&t);
-
-    // XXX do we need to handle the case where p == NULL here?
-
-    Sec = (MR_Integer) p->tm_sec;
-    Min = (MR_Integer) p->tm_min;
-    Hrs = (MR_Integer) p->tm_hour;
-    Mnt = (MR_Integer) p->tm_mon;
-    Yr = (MR_Integer) p->tm_year;
-    WD = (MR_Integer) p->tm_wday;
-    MD = (MR_Integer) p->tm_mday;
-    YD = (MR_Integer) p->tm_yday;
-    N = (MR_Integer) p->tm_isdst;
+    if (p == NULL) {
+        char errbuf[MR_STRERROR_BUF_SIZE];
+        const char *errno_msg;
+        IsOk = MR_NO;
+        errno_msg = MR_strerror(errno, errbuf, sizeof(errbuf));
+        MR_save_transient_hp();
+        MR_make_aligned_string_copy(ErrorMsg, errno_msg);
+        MR_restore_transient_hp();
+    } else {
+        IsOk = MR_YES;
+        Sec = (MR_Integer) p->tm_sec;
+        Min = (MR_Integer) p->tm_min;
+        Hrs = (MR_Integer) p->tm_hour;
+        Mnt = (MR_Integer) p->tm_mon;
+        Yr = (MR_Integer) p->tm_year;
+        WD = (MR_Integer) p->tm_wday;
+        MD = (MR_Integer) p->tm_mday;
+        YD = (MR_Integer) p->tm_yday;
+        N = (MR_Integer) p->tm_isdst;
+        ErrorMsg = MR_make_string_const(\"\");
+    }
  ").
  :- pragma foreign_proc("C#",
-    c_localtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
-        Min::out, Sec::out, YD::out, WD::out, N::out, _IO0::di, _IO::uo),
+    c_localtime(Time::in, IsOk::out, Yr::out, Mnt::out, MD::out, Hrs::out,
+        Min::out, Sec::out, YD::out, WD::out, N::out, ErrorMsg::out,
+         _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure],
  "
+    // XXX t will be clamped to MinValue / MaxValue if the converted
+    // time cannot be represented by a DateTime object.
      System.DateTime t = Time.ToLocalTime();

      // we don't handle leap seconds
@@ -603,42 +627,75 @@ localtime(Time, TM, !IO) :-
      } else {
          N = 0;
      }
+    IsOk = mr_bool.YES;
+    ErrorMsg = \"\";
  ").
  :- pragma foreign_proc("Java",
-    c_localtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
-        Min::out, Sec::out, YD::out, WD::out, N::out, _IO0::di, _IO::uo),
+    c_localtime(Time::in, IsOk::out, Yr::out, Mnt::out, MD::out, Hrs::out,
+        Min::out, Sec::out, YD::out, WD::out, N::out, ErrorMsg::out,
+        _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, may_not_duplicate],
  "
-    java.time.ZoneId tz = java.time.ZoneId.systemDefault();
-    java.time.ZonedDateTime zdt =
-        java.time.ZonedDateTime.ofInstant(Time, tz);
-
-    Yr = zdt.getYear() - 1900;
-    Mnt = zdt.getMonthValue() - 1;
-    MD = zdt.getDayOfMonth();
-    Hrs = zdt.getHour();
-    Min = zdt.getMinute();
-    Sec = zdt.getSecond();
-    YD = zdt.getDayOfYear() - 1;
-    WD = zdt.getDayOfWeek().getValue() % 7;
-
-    if (tz.getRules().isDaylightSavings(Time)) {
-        N = 1;
-    } else {
+    try {
+        java.time.ZoneId tz = java.time.ZoneId.systemDefault();
+        java.time.ZonedDateTime zdt =
+            java.time.ZonedDateTime.ofInstant(Time, tz);
+
+        Yr = zdt.getYear() - 1900;
+        Mnt = zdt.getMonthValue() - 1;
+        MD = zdt.getDayOfMonth();
+        Hrs = zdt.getHour();
+        Min = zdt.getMinute();
+        Sec = zdt.getSecond();
+        YD = zdt.getDayOfYear() - 1;
+        WD = zdt.getDayOfWeek().getValue() % 7;
+
+        if (tz.getRules().isDaylightSavings(Time)) {
+            N = 1;
+        } else {
+            N = 0;
+        }
+        IsOk = bool.YES;
+        ErrorMsg = \"\";
+    } catch (java.time.DateTimeException e) {
+        Yr = 0;
+        Mnt = 0;
+        MD = 0;
+        Hrs = 0;
+        Min = 0;
+        Sec = 0;
+        YD = 0;
+        WD = 0;
          N = 0;
+        IsOk = bool.NO;
+        if (e.getMessage() != null) {
+            ErrorMsg = e.getMessage();
+        } else {
+            ErrorMsg = \"\";
+        }
      }
  ").

-gmtime(time_t(Time)) = TM :-
-    c_gmtime(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 c_gmtime(time_t_rep::in, int::out, int::out, int::out, int::out,
-    int::out, int::out, int::out, int::out, int::out) is det.
+gmtime(Time) = TM :-
+    Time = time_t(RawTime),
+    c_gmtime(RawTime, IsOk, Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, N, ErrorMsg),
+    (
+        IsOk = yes,
+        TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, int_to_maybe_dst(N))
+    ;
+        IsOk = no,
+        throw(time_error("time.gmtime: conversion failed: " ++ ErrorMsg))
+    ).
+
+:- pred c_gmtime(time_t_rep::in, bool::out, int::out, int::out, int::out,
+    int::out, int::out, int::out, int::out, int::out, int::out,
+    string::out) is det.

  :- pragma foreign_proc("C",
-    c_gmtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
-        Min::out, Sec::out, YD::out, WD::out, N::out),
+    c_gmtime(Time::in, IsOk::out, Yr::out, Mnt::out, MD::out, Hrs::out,
+        Min::out, Sec::out, YD::out, WD::out, N::out, ErrorMsg::out),
      [will_not_call_mercury, promise_pure, not_thread_safe],
  "
      struct tm   *p;
@@ -647,27 +704,37 @@ gmtime(time_t(Time)) = TM :-
      t = Time;

      p = gmtime(&t);
-
-    // XXX do we need to handle the case where p == NULL here?
-
-    Sec = (MR_Integer) p->tm_sec;
-    Min = (MR_Integer) p->tm_min;
-    Hrs = (MR_Integer) p->tm_hour;
-    Mnt = (MR_Integer) p->tm_mon;
-    Yr = (MR_Integer) p->tm_year;
-    WD = (MR_Integer) p->tm_wday;
-    MD = (MR_Integer) p->tm_mday;
-    YD = (MR_Integer) p->tm_yday;
-    N = (MR_Integer) p->tm_isdst;
+    if (p == NULL) {
+        char errbuf[MR_STRERROR_BUF_SIZE];
+        const char *errno_msg;
+        IsOk = MR_NO;
+        errno_msg = MR_strerror(errno, errbuf, sizeof(errbuf));
+        MR_save_transient_hp();
+        MR_make_aligned_string_copy(ErrorMsg, errno_msg);
+        MR_restore_transient_hp();
+    } else {
+        IsOk = MR_YES;
+        Sec = (MR_Integer) p->tm_sec;
+        Min = (MR_Integer) p->tm_min;
+        Hrs = (MR_Integer) p->tm_hour;
+        Mnt = (MR_Integer) p->tm_mon;
+        Yr = (MR_Integer) p->tm_year;
+        WD = (MR_Integer) p->tm_wday;
+        MD = (MR_Integer) p->tm_mday;
+        YD = (MR_Integer) p->tm_yday;
+        N = (MR_Integer) p->tm_isdst;
+        ErrorMsg = MR_make_string_const(\"\");
+    }
  ").
  :- pragma foreign_proc("C#",
-    c_gmtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
-        Min::out, Sec::out, YD::out, WD::out, N::out),
+    c_gmtime(Time::in, IsOk::out, Yr::out, Mnt::out, MD::out, Hrs::out,
+        Min::out, Sec::out, YD::out, WD::out, N::out, ErrorMsg::out),
      [will_not_call_mercury, promise_pure],
  "
      System.DateTime t = Time;

-    // we don't handle leap seconds
+    // We don't handle leap seconds.
+    // (XXX actually C# does not handle leap seconds.)
      Sec = t.Second;
      Min = t.Minute;
      Hrs = t.Hour;
@@ -678,26 +745,48 @@ gmtime(time_t(Time)) = TM :-
      YD = t.DayOfYear - 1;
      // UTC time can never have daylight savings.
      N = 0;
+    IsOk = mr_bool.NO;
+    ErrorMsg = \"\";
  ").
  :- pragma foreign_proc("Java",
-    c_gmtime(Time::in, Yr::out, Mnt::out, MD::out, Hrs::out,
-        Min::out, Sec::out, YD::out, WD::out, N::out),
+    c_gmtime(Time::in, IsOk::out, Yr::out, Mnt::out, MD::out, Hrs::out,
+        Min::out, Sec::out, YD::out, WD::out, N::out, ErrorMsg::out),
      [will_not_call_mercury, promise_pure, may_not_duplicate],
  "
-    java.time.OffsetDateTime utcTime =
-        java.time.OffsetDateTime.ofInstant(Time,
-        java.time.ZoneOffset.UTC);
-
-    Yr = utcTime.getYear() - 1900;
-    Mnt = utcTime.getMonthValue() - 1;
-    MD = utcTime.getDayOfMonth();
-    Hrs = utcTime.getHour();
-    Min = utcTime.getMinute();
-    Sec = utcTime.getSecond();
-    YD = utcTime.getDayOfYear() - 1;
-    // Sunday = 7 = 0.
-    WD = utcTime.getDayOfWeek().getValue() % 7;
-    N = 0;
+    try {
+        java.time.OffsetDateTime utcTime =
+            java.time.OffsetDateTime.ofInstant(Time,
+            java.time.ZoneOffset.UTC);
+
+        Yr = utcTime.getYear() - 1900;
+        Mnt = utcTime.getMonthValue() - 1;
+        MD = utcTime.getDayOfMonth();
+        Hrs = utcTime.getHour();
+        Min = utcTime.getMinute();
+        Sec = utcTime.getSecond();
+        YD = utcTime.getDayOfYear() - 1;
+        // Sunday == 7 == 0.
+        WD = utcTime.getDayOfWeek().getValue() % 7;
+        N = 0;
+        IsOk = bool.YES;
+        ErrorMsg = \"\";
+    } catch (java.time.DateTimeException e) {
+        Yr = 0;
+        Mnt = 0;
+        MD = 0;
+        Hrs = 0;
+        Min = 0;
+        Sec = 0;
+        YD = 0;
+        WD = 0;
+        N = 0;
+        IsOk = bool.NO;
+        if (e.getMessage() != null) {
+            ErrorMsg = e.getMessage();
+        } else {
+            ErrorMsg = \"\";
+        }
+    }
  ").

  :- func int_to_maybe_dst(int) = maybe(dst).
@@ -715,22 +804,34 @@ int_to_maybe_dst(N) = DST :-

  mktime(TM, Time, !IO) :-
      TM = tm(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, DST),
+    % XXX we need to check the validity  of TM's fields here, since mktime()'s
+    % checks are rubbish.
      c_mktime(Yr, Mnt, MD, Hrs, Min, Sec, YD, WD, maybe_dst_to_int(DST),
-        RawTime, !IO),
-    Time = time_t(RawTime).
+        IsOk, RawTime, ErrMsg, !IO),
+    (
+        IsOk = yes,
+        Time = time_t(RawTime)
+    ;
+        IsOk = no,
+        unexpected($pred, "cannot convert to calendar time: " ++ ErrMsg)
+    ).

      % NOTE: mktime() modifies tzname, however we do not expose tzname
      % through a Mercury interface.
      %
  :- pred c_mktime(int::in, int::in, int::in, int::in, int::in, int::in,
-    int::in, int::in, int::in, time_t_rep::out, io::di, io::uo) is det.
+    int::in, int::in, int::in, bool::out, time_t_rep::out, string::out,
+     io::di, io::uo) is det.

  :- pragma foreign_proc("C",
      c_mktime(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in, Sec::in,
-        YD::in, WD::in, N::in, Time::out, _IO0::di, _IO::uo),
+        YD::in, WD::in, N::in, IsOk::out, Time::out, ErrorMsg::out,
+        _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, not_thread_safe, tabled_for_io],
  "
      struct tm t;
+    char errbuf[MR_STRERROR_BUF_SIZE];
+    const char *errno_msg;

      t.tm_sec = (int) Sec;
      t.tm_min = (int) Min;
@@ -743,10 +844,21 @@ mktime(TM, Time, !IO) :-
      t.tm_isdst = (int) N;

      Time = mktime(&t);
+    if (Time == (time_t) -1) {
+        IsOk = MR_NO;
+        errno_msg = MR_strerror(errno, errbuf, sizeof(errbuf));
+        MR_save_transient_hp();
+        MR_make_aligned_string_copy(ErrorMsg, errno_msg);
+        MR_restore_transient_hp();
+    } else {
+        IsOk = MR_YES;
+        ErrorMsg = MR_make_string_const(\"\");
+    }
  ").
  :- pragma foreign_proc("C#",
      c_mktime(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in, Sec::in,
-        _YD::in, _WD::in, _N::in, Time::out, _IO0::di, _IO::uo),
+        _YD::in, _WD::in, _N::in, IsOk::out, Time::out, ErrorMsg::out,
+         _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure],
  "
      // We don't use YD, WD and N.
@@ -756,47 +868,74 @@ mktime(TM, Time, !IO) :-
      // savings time (N = 1), and then again an hour later, during standard
      // time (N = 0). The .NET API does not seem to provide any way to
      // get the right answer in both cases.
-    System.DateTime local_time =
-        new System.DateTime(Yr + 1900, Mnt + 1, MD, Hrs, Min, Sec);
-    Time = local_time.ToUniversalTime();
+    try {
+        System.DateTime local_time =
+            new System.DateTime(Yr + 1900, Mnt + 1, MD, Hrs, Min, Sec);
+        Time = local_time.ToUniversalTime();
+        IsOk = mr_bool.YES;
+        ErrorMsg = \"\";
+    } catch (System.ArgumentOutOfRangeException e) {
+        Time = System.DateTime.MinValue; // Dummy value.
+        IsOk = mr_bool.NO;
+        ErrorMsg = e.Message;
+    }
  ").
  :- pragma foreign_proc("Java",
      c_mktime(Yr::in, Mnt::in, MD::in, Hrs::in, Min::in, Sec::in,
-        _YD::in, _WD::in, N::in, Time::out, _IO0::di, _IO::uo),
+        _YD::in, _WD::in, N::in, IsOk::out, Time::out, ErrorMsg::out,
+        _IO0::di, _IO::uo),
      [will_not_call_mercury, promise_pure, may_not_duplicate],
  "
-    java.time.ZoneId tz = java.time.ZoneId.systemDefault();
-    java.time.Instant Time0 = java.time.ZonedDateTime.of(
-        java.time.LocalDateTime.of(Yr + 1900, Mnt + 1, MD, Hrs, Min, Sec),
-        tz).toInstant();
-
-    // Correct for DST:  This is only an issue when it is possible for the same
-    // 'time' to occur twice due to daylight savings ending.
-    // (In Melbourne, 2:00am-2:59am occur twice when leaving DST)
-
-    java.time.zone.ZoneRules rules = tz.getRules();
-    boolean isDST = rules.isDaylightSavings(Time0);
-
-    if (N == 1 & !isDST) {
-        // If the time we constructed is not in daylight savings time, but it
-        // should be, we need to subtract the DSTSavings.
-        java.time.Duration savings = rules.getDaylightSavings(Time0);
-        Time = Time0.minus(savings);
-        if (!rules.isDaylightSavings(Time)) {
-            throw new RuntimeException(
-                ""time.mktime: failed to correct for DST"");
+    try {
+        java.time.ZoneId tz = java.time.ZoneId.systemDefault();
+        java.time.Instant Time0 = java.time.ZonedDateTime.of(
+            java.time.LocalDateTime.of(Yr + 1900, Mnt + 1, MD, Hrs, Min, Sec),
+            tz).toInstant();
+
+        // Correct for DST:  This is only an issue when it is possible for the
+        // same 'time' to occur twice due to daylight savings ending.
+        // (In Melbourne, 2:00am-2:59am occur twice when leaving DST)
+
+        java.time.zone.ZoneRules rules = tz.getRules();
+        boolean isDST = rules.isDaylightSavings(Time0);
+
+        if (N == 1 & !isDST) {
+            // If the time we constructed is not in daylight savings time, but
+            // it should be, we need to subtract the DSTSavings.
+            java.time.Duration savings = rules.getDaylightSavings(Time0);
+            Time = Time0.minus(savings);
+            if (!rules.isDaylightSavings(Time)) {
+                IsOk = bool.NO;
+                ErrorMsg = \"failed to correct for DST\";
+            } else {
+                IsOk = bool.YES;
+                ErrorMsg = \"\";
+            }
+        } else if (N == 0 && isDST) {
+            // If the time we constructed is in daylight savings time, but
+            // should not be, we need to add the DSTSavings.
+            java.time.Duration savings = rules.getDaylightSavings(Time0);
+            Time = Time0.plus(savings);
+            if (rules.isDaylightSavings(Time)) {
+                IsOk = bool.NO;
+                ErrorMsg = \"failed to correct for DST\";
+            } else {
+                IsOk = bool.YES;
+                ErrorMsg = \"\";
+            }
+        } else {
+            IsOk = bool.YES;
+            Time = Time0;
+            ErrorMsg = \"\";
          }
-    } else if (N == 0 && isDST) {
-        // If the time we constructed is in daylight savings time, but should
-        // not be, we need to add the DSTSavings.
-        java.time.Duration savings = rules.getDaylightSavings(Time0);
-        Time = Time0.plus(savings);
-        if (rules.isDaylightSavings(Time)) {
-            throw new RuntimeException(
-                ""time.mktime: failed to correct for DST"");
+    } catch (java.lang.Exception e) {
+        IsOk = bool.NO;
+        Time = java.time.Instant.MIN; // Dummy value.
+        if (e.getMessage() != null) {
+            ErrorMsg = e.getMessage();
+        } else {
+            ErrorMsg = \"\";
          }
-    } else {
-        Time = Time0;
      }
  ").



More information about the reviews mailing list