[m-rev.] diff: improve tests for converting date_times to and from strings

Julien Fischer jfischer at opturion.com
Tue Apr 7 10:58:36 AEST 2026


Improve tests for converting date_times to and from strings.

Currently, testing of conversion of date_times to and from strings is only done
by the hard_coded/calendar_test test case.  This testing is only done indirectly
as part of testing other functionality and, in particular, does *not* cover the
failure paths of the string to date_time conversion. Add a new test case that
tests the date_time conversions more thoroughly.

tests/hard_coded/Mmakefile:
tests/hard_coded/calendar_date_time_conv.{m,exp}:
    Add the new test case.

Julien.

diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index dc4e1b1ad..d60586b75 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -792,6 +792,7 @@ ifeq "$(findstring profdeep,$(GRADE))" ""
  bitwise_uint64 \
  bitwise_uint8 \
  calendar_basics \
+ calendar_date_time_conv \
  calendar_init_date \
  char_to_string \
  clamp_int \
diff --git a/tests/hard_coded/calendar_date_time_conv.exp
b/tests/hard_coded/calendar_date_time_conv.exp
new file mode 100644
index 000000000..3757903ba
--- /dev/null
+++ b/tests/hard_coded/calendar_date_time_conv.exp
@@ -0,0 +1,224 @@
+=== Testing date_time_from_string/2 with valid inputs ===
+
+date_time_from_string("2024-01-15 12:30:00") ===> TEST PASSED
(accepted: date_time(2024, 1, 15, 12, 30, 0, 0))
+date_time_from_string("1977-10-09 12:00:00") ===> TEST PASSED
(accepted: date_time(1977, 10, 9, 12, 0, 0, 0))
+date_time_from_string("0000-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(0, 1, 1, 0, 0, 0, 0))
+date_time_from_string("0000-12-31 23:59:59") ===> TEST PASSED
(accepted: date_time(0, 12, 31, 23, 59, 59, 0))
+date_time_from_string("0001-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(1, 1, 1, 0, 0, 0, 0))
+date_time_from_string("9999-12-31 23:59:59") ===> TEST PASSED
(accepted: date_time(9999, 12, 31, 23, 59, 59, 0))
+date_time_from_string("-0001-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(-1, 1, 1, 0, 0, 0, 0))
+date_time_from_string("-0001-12-31 00:00:00") ===> TEST PASSED
(accepted: date_time(-1, 12, 31, 0, 0, 0, 0))
+date_time_from_string("-4713-11-24 00:00:00") ===> TEST PASSED
(accepted: date_time(-4713, 11, 24, 0, 0, 0, 0))
+date_time_from_string("10000-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(10000, 1, 1, 0, 0, 0, 0))
+date_time_from_string("100000-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(100000, 1, 1, 0, 0, 0, 0))
+date_time_from_string("2000-02-29 00:00:00") ===> TEST PASSED
(accepted: date_time(2000, 2, 29, 0, 0, 0, 0))
+date_time_from_string("2024-02-29 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 2, 29, 0, 0, 0, 0))
+date_time_from_string("0000-02-29 00:00:00") ===> TEST PASSED
(accepted: date_time(0, 2, 29, 0, 0, 0, 0))
+date_time_from_string("-0004-02-29 00:00:00") ===> TEST PASSED
(accepted: date_time(-4, 2, 29, 0, 0, 0, 0))
+date_time_from_string("2023-02-28 00:00:00") ===> TEST PASSED
(accepted: date_time(2023, 2, 28, 0, 0, 0, 0))
+date_time_from_string("1900-02-28 00:00:00") ===> TEST PASSED
(accepted: date_time(1900, 2, 28, 0, 0, 0, 0))
+date_time_from_string("2024-01-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 1, 31, 0, 0, 0, 0))
+date_time_from_string("2024-03-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 3, 31, 0, 0, 0, 0))
+date_time_from_string("2024-04-30 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 4, 30, 0, 0, 0, 0))
+date_time_from_string("2024-05-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 5, 31, 0, 0, 0, 0))
+date_time_from_string("2024-06-30 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 6, 30, 0, 0, 0, 0))
+date_time_from_string("2024-07-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 7, 31, 0, 0, 0, 0))
+date_time_from_string("2024-08-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 8, 31, 0, 0, 0, 0))
+date_time_from_string("2024-09-30 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 9, 30, 0, 0, 0, 0))
+date_time_from_string("2024-10-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 10, 31, 0, 0, 0, 0))
+date_time_from_string("2024-11-30 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 11, 30, 0, 0, 0, 0))
+date_time_from_string("2024-12-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 12, 31, 0, 0, 0, 0))
+date_time_from_string("2024-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 0))
+date_time_from_string("2024-01-01 23:59:59") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 23, 59, 59, 0))
+date_time_from_string("2024-01-01 00:00:60") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 60, 0))
+date_time_from_string("2024-01-01 00:00:61") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 61, 0))
+date_time_from_string("2024-01-01 00:00:00.1") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 100000))
+date_time_from_string("2024-01-01 00:00:00.12") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 120000))
+date_time_from_string("2024-01-01 00:00:00.123") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 123000))
+date_time_from_string("2024-01-01 00:00:00.1234") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 123400))
+date_time_from_string("2024-01-01 00:00:00.12345") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 123450))
+date_time_from_string("2024-01-01 00:00:00.123456") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 123456))
+date_time_from_string("2024-01-01 00:00:00.000001") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 1))
+date_time_from_string("2024-01-01 00:00:00.999999") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 999999))
+date_time_from_string("2024-12-31 23:59:61.999999") ===> TEST PASSED
(accepted: date_time(2024, 12, 31, 23, 59, 61, 999999))
+date_time_from_string("1970-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(1970, 1, 1, 0, 0, 0, 0))
+date_time_from_string("1582-10-15 00:00:00") ===> TEST PASSED
(accepted: date_time(1582, 10, 15, 0, 0, 0, 0))
+date_time_from_string("2024-01-01 00:00:00.10") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string:
"2024-01-01 00:00:00.1"))
+date_time_from_string("2024-01-01 00:00:00.100") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string:
"2024-01-01 00:00:00.1"))
+date_time_from_string("2024-01-01 00:00:00.1000") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string:
"2024-01-01 00:00:00.1"))
+date_time_from_string("2024-01-01 00:00:00.10000") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string:
"2024-01-01 00:00:00.1"))
+date_time_from_string("2024-01-01 00:00:00.100000") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 100000): to-string:
"2024-01-01 00:00:00.1"))
+date_time_from_string("2024-01-01 00:00:00.120000") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 120000): to-string:
"2024-01-01 00:00:00.12"))
+date_time_from_string("2024-01-01 00:00:00.123000") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 123000): to-string:
"2024-01-01 00:00:00.123"))
+date_time_from_string("2024-01-01 00:00:00.123400") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 123400): to-string:
"2024-01-01 00:00:00.1234"))
+date_time_from_string("2024-01-01 00:00:00.123450") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 123450): to-string:
"2024-01-01 00:00:00.12345"))
+date_time_from_string("2024-01-01 00:00:00.000000") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 0): to-string: "2024-01-01
00:00:00"))
+date_time_from_string("00000-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(0, 1, 1, 0, 0, 0, 0): to-string: "0000-01-01
00:00:00"))
+date_time_from_string("00001-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(1, 1, 1, 0, 0, 0, 0): to-string: "0001-01-01
00:00:00"))
+
+=== Testing date_time_from_string/2 with invalid inputs ===
+
+date_time_from_string("") ===> TEST PASSED (rejected: empty string)
+date_time_from_string("    ") ===> TEST PASSED (rejected: blank string)
+date_time_from_string("not a date") ===> TEST PASSED (rejected: not a date)
+date_time_from_string("2024-01-01") ===> TEST PASSED (rejected:
missing time component)
+date_time_from_string("12:00:00") ===> TEST PASSED (rejected: missing
date component)
+date_time_from_string("2024/01/01 00:00:00") ===> TEST PASSED
(rejected: incorrect date separator)
+date_time_from_string("2024-01-01T00:00:00") ===> TEST PASSED
(rejected: ISO 8601 T separator)
+date_time_from_string("2024-01-01 00:00") ===> TEST PASSED (rejected:
missing seconds)
+date_time_from_string("2024-01-01 00:00:00 ") ===> TEST PASSED
(rejected: trailing space)
+date_time_from_string(" 2024-01-01 00:00:00") ===> TEST PASSED
(rejected: leading space)
+date_time_from_string("2024-01-01 00:00:00Z") ===> TEST PASSED
(rejected: trailing timezone indicator)
+date_time_from_string("2024-01-01 00:00:00+10:00") ===> TEST PASSED
(rejected: trailing UTC offset)
+date_time_from_string("99-01-01 00:00:00") ===> TEST PASSED
(rejected: two-digit year)
+date_time_from_string("999-01-01 00:00:00") ===> TEST PASSED
(rejected: three-digit year)
+date_time_from_string("2024-1-01 00:00:00") ===> TEST PASSED
(rejected: one-digit month)
+date_time_from_string("2024-01-1 00:00:00") ===> TEST PASSED
(rejected: one-digit day)
+date_time_from_string("2024-01-01 0:00:00") ===> TEST PASSED
(rejected: one-digit hour)
+date_time_from_string("2024-01-01 00:0:00") ===> TEST PASSED
(rejected: one-digit minute)
+date_time_from_string("2024-01-01 00:00:0") ===> TEST PASSED
(rejected: one-digit second)
+date_time_from_string("2024-00-01 00:00:00") ===> TEST PASSED
(rejected: month zero)
+date_time_from_string("2024-13-01 00:00:00") ===> TEST PASSED
(rejected: month 13)
+date_time_from_string("2024-01-00 00:00:00") ===> TEST PASSED
(rejected: day zero)
+date_time_from_string("2024-01-32 00:00:00") ===> TEST PASSED
(rejected: day 32 in a 31-day month)
+date_time_from_string("2023-02-29 00:00:00") ===> TEST PASSED
(rejected: Feb 29 in a non-leap year)
+date_time_from_string("1900-02-29 00:00:00") ===> TEST PASSED
(rejected: Feb 29 in a century non-leap year)
+date_time_from_string("2024-04-31 00:00:00") ===> TEST PASSED
(rejected: day 31 in a 30-day month)
+date_time_from_string("2024-01-01 24:00:00") ===> TEST PASSED
(rejected: hour 24)
+date_time_from_string("2024-01-01 00:60:00") ===> TEST PASSED
(rejected: minute 60)
+date_time_from_string("2024-01-01 00:00:62") ===> TEST PASSED
(rejected: second 62)
+date_time_from_string("2024-01-01 00:00:00.") ===> TEST PASSED
(rejected: trailing dot with no digits)
+date_time_from_string("2024-01-01 00:00:00.1234567") ===> TEST PASSED
(rejected: seven fractional digits)
+date_time_from_string("-01-01 00:00:00") ===> TEST PASSED (rejected:
negative sign but only two-digit year)
+date_time_from_string("-0001-13-01 00:00:00") ===> TEST PASSED
(rejected: negative year with out-of-range month)
+date_time_from_string("2024-ab-01 00:00:00") ===> TEST PASSED
(rejected: letter in month field)
+date_time_from_string("2024-01-01 xx:00:00") ===> TEST PASSED
(rejected: letters in hour field)
+date_time_from_string("--0001-01-01 00:00:00") ===> TEST PASSED
(rejected: double negative prefix)
+date_time_from_string("2024--01-01 00:00:00") ===> TEST PASSED
(rejected: negative sign on month)
+date_time_from_string("2024-01-01 -01:00:00") ===> TEST PASSED
(rejected: negative sign on hour)
+date_time_from_string("2024-01-01 00:00:-01") ===> TEST PASSED
(rejected: negative sign on second)
+date_time_from_string("2024-01-01  00:00:00") ===> TEST PASSED
(rejected: double space between date and time)
+date_time_from_string("2024-01-01 00:00:00.123.456") ===> TEST PASSED
(rejected: two decimal points)
+date_time_from_string("2024-01-01 00:00:00.123456.7") ===> TEST
PASSED (rejected: decimal point in microseconds follow by more)
+date_time_from_string("2024-001-01 00:00:00") ===> TEST PASSED
(rejected: three-digit month)
+date_time_from_string("2024-01-001 00:00:00") ===> TEST PASSED
(rejected: three-digit day)
+date_time_from_string("2024-01-01 000:00:00") ===> TEST PASSED
(rejected: three-digit hour)
+date_time_from_string("2024-01-01 00:001:00") ===> TEST PASSED
(rejected: three-digit minute)
+date_time_from_string("2024-01-01 00:00:001") ===> TEST PASSED
(rejected: three-digit second)
+date_time_from_string("2024-01-01 00:00:00.0000000") ===> TEST PASSED
(rejected: seven zeros in microseconds)
+date_time_from_string("2024-01-01 00 :00:00") ===> TEST PASSED
(rejected: space within time component)
+date_time_from_string("2024 -01-01 00:00:00") ===> TEST PASSED
(rejected: space within date component)
+date_time_from_string("2024:01:01 00:00:00") ===> TEST PASSED
(rejected: colons in date part)
+date_time_from_string("2024-01-01 00-00-00") ===> TEST PASSED
(rejected: dashes in time part)
+date_time_from_string("2024--01 00:00:00") ===> TEST PASSED
(rejected: missing month)
+date_time_from_string("2024-01- 00:00:00") ===> TEST PASSED
(rejected: missing day)
+date_time_from_string("2024-01-01 :00:00") ===> TEST PASSED
(rejected: missing hour)
+date_time_from_string("2024-01-01 00::00") ===> TEST PASSED
(rejected: missing minute)
+date_time_from_string("2024-01-01 00:00:") ===> TEST PASSED
(rejected: trailing colon with no second digits)
+
+=== Testing det_date_from_string/1 with valid inputs ===
+
+det_date_time_from_string("2024-01-15 12:30:00") ===> TEST PASSED
(accepted: date_time(2024, 1, 15, 12, 30, 0, 0))
+det_date_time_from_string("1977-10-09 12:00:00") ===> TEST PASSED
(accepted: date_time(1977, 10, 9, 12, 0, 0, 0))
+det_date_time_from_string("0000-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(0, 1, 1, 0, 0, 0, 0))
+det_date_time_from_string("0000-12-31 23:59:59") ===> TEST PASSED
(accepted: date_time(0, 12, 31, 23, 59, 59, 0))
+det_date_time_from_string("0001-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(1, 1, 1, 0, 0, 0, 0))
+det_date_time_from_string("9999-12-31 23:59:59") ===> TEST PASSED
(accepted: date_time(9999, 12, 31, 23, 59, 59, 0))
+det_date_time_from_string("-0001-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(-1, 1, 1, 0, 0, 0, 0))
+det_date_time_from_string("-0001-12-31 00:00:00") ===> TEST PASSED
(accepted: date_time(-1, 12, 31, 0, 0, 0, 0))
+det_date_time_from_string("-4713-11-24 00:00:00") ===> TEST PASSED
(accepted: date_time(-4713, 11, 24, 0, 0, 0, 0))
+det_date_time_from_string("10000-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(10000, 1, 1, 0, 0, 0, 0))
+det_date_time_from_string("100000-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(100000, 1, 1, 0, 0, 0, 0))
+det_date_time_from_string("2000-02-29 00:00:00") ===> TEST PASSED
(accepted: date_time(2000, 2, 29, 0, 0, 0, 0))
+det_date_time_from_string("2024-02-29 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 2, 29, 0, 0, 0, 0))
+det_date_time_from_string("0000-02-29 00:00:00") ===> TEST PASSED
(accepted: date_time(0, 2, 29, 0, 0, 0, 0))
+det_date_time_from_string("-0004-02-29 00:00:00") ===> TEST PASSED
(accepted: date_time(-4, 2, 29, 0, 0, 0, 0))
+det_date_time_from_string("2023-02-28 00:00:00") ===> TEST PASSED
(accepted: date_time(2023, 2, 28, 0, 0, 0, 0))
+det_date_time_from_string("1900-02-28 00:00:00") ===> TEST PASSED
(accepted: date_time(1900, 2, 28, 0, 0, 0, 0))
+det_date_time_from_string("2024-01-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 1, 31, 0, 0, 0, 0))
+det_date_time_from_string("2024-03-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 3, 31, 0, 0, 0, 0))
+det_date_time_from_string("2024-04-30 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 4, 30, 0, 0, 0, 0))
+det_date_time_from_string("2024-05-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 5, 31, 0, 0, 0, 0))
+det_date_time_from_string("2024-06-30 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 6, 30, 0, 0, 0, 0))
+det_date_time_from_string("2024-07-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 7, 31, 0, 0, 0, 0))
+det_date_time_from_string("2024-08-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 8, 31, 0, 0, 0, 0))
+det_date_time_from_string("2024-09-30 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 9, 30, 0, 0, 0, 0))
+det_date_time_from_string("2024-10-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 10, 31, 0, 0, 0, 0))
+det_date_time_from_string("2024-11-30 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 11, 30, 0, 0, 0, 0))
+det_date_time_from_string("2024-12-31 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 12, 31, 0, 0, 0, 0))
+det_date_time_from_string("2024-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 0))
+det_date_time_from_string("2024-01-01 23:59:59") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 23, 59, 59, 0))
+det_date_time_from_string("2024-01-01 00:00:60") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 60, 0))
+det_date_time_from_string("2024-01-01 00:00:61") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 61, 0))
+det_date_time_from_string("2024-01-01 00:00:00.1") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 100000))
+det_date_time_from_string("2024-01-01 00:00:00.12") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 120000))
+det_date_time_from_string("2024-01-01 00:00:00.123") ===> TEST PASSED
(accepted: date_time(2024, 1, 1, 0, 0, 0, 123000))
+det_date_time_from_string("2024-01-01 00:00:00.1234") ===> TEST
PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123400))
+det_date_time_from_string("2024-01-01 00:00:00.12345") ===> TEST
PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123450))
+det_date_time_from_string("2024-01-01 00:00:00.123456") ===> TEST
PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 123456))
+det_date_time_from_string("2024-01-01 00:00:00.000001") ===> TEST
PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 1))
+det_date_time_from_string("2024-01-01 00:00:00.999999") ===> TEST
PASSED (accepted: date_time(2024, 1, 1, 0, 0, 0, 999999))
+det_date_time_from_string("2024-12-31 23:59:61.999999") ===> TEST
PASSED (accepted: date_time(2024, 12, 31, 23, 59, 61, 999999))
+det_date_time_from_string("1970-01-01 00:00:00") ===> TEST PASSED
(accepted: date_time(1970, 1, 1, 0, 0, 0, 0))
+det_date_time_from_string("1582-10-15 00:00:00") ===> TEST PASSED
(accepted: date_time(1582, 10, 15, 0, 0, 0, 0))
+
+=== Testing det_date_from_string/1 with invalid inputs ===
+
+det_date_time_from_string("") ===> TEST PASSED (exception: empty string)
+det_date_time_from_string("    ") ===> TEST PASSED (exception: blank string)
+det_date_time_from_string("not a date") ===> TEST PASSED (exception:
not a date)
+det_date_time_from_string("2024-01-01") ===> TEST PASSED (exception:
missing time component)
+det_date_time_from_string("12:00:00") ===> TEST PASSED (exception:
missing date component)
+det_date_time_from_string("2024/01/01 00:00:00") ===> TEST PASSED
(exception: incorrect date separator)
+det_date_time_from_string("2024-01-01T00:00:00") ===> TEST PASSED
(exception: ISO 8601 T separator)
+det_date_time_from_string("2024-01-01 00:00") ===> TEST PASSED
(exception: missing seconds)
+det_date_time_from_string("2024-01-01 00:00:00 ") ===> TEST PASSED
(exception: trailing space)
+det_date_time_from_string(" 2024-01-01 00:00:00") ===> TEST PASSED
(exception: leading space)
+det_date_time_from_string("2024-01-01 00:00:00Z") ===> TEST PASSED
(exception: trailing timezone indicator)
+det_date_time_from_string("2024-01-01 00:00:00+10:00") ===> TEST
PASSED (exception: trailing UTC offset)
+det_date_time_from_string("99-01-01 00:00:00") ===> TEST PASSED
(exception: two-digit year)
+det_date_time_from_string("999-01-01 00:00:00") ===> TEST PASSED
(exception: three-digit year)
+det_date_time_from_string("2024-1-01 00:00:00") ===> TEST PASSED
(exception: one-digit month)
+det_date_time_from_string("2024-01-1 00:00:00") ===> TEST PASSED
(exception: one-digit day)
+det_date_time_from_string("2024-01-01 0:00:00") ===> TEST PASSED
(exception: one-digit hour)
+det_date_time_from_string("2024-01-01 00:0:00") ===> TEST PASSED
(exception: one-digit minute)
+det_date_time_from_string("2024-01-01 00:00:0") ===> TEST PASSED
(exception: one-digit second)
+det_date_time_from_string("2024-00-01 00:00:00") ===> TEST PASSED
(exception: month zero)
+det_date_time_from_string("2024-13-01 00:00:00") ===> TEST PASSED
(exception: month 13)
+det_date_time_from_string("2024-01-00 00:00:00") ===> TEST PASSED
(exception: day zero)
+det_date_time_from_string("2024-01-32 00:00:00") ===> TEST PASSED
(exception: day 32 in a 31-day month)
+det_date_time_from_string("2023-02-29 00:00:00") ===> TEST PASSED
(exception: Feb 29 in a non-leap year)
+det_date_time_from_string("1900-02-29 00:00:00") ===> TEST PASSED
(exception: Feb 29 in a century non-leap year)
+det_date_time_from_string("2024-04-31 00:00:00") ===> TEST PASSED
(exception: day 31 in a 30-day month)
+det_date_time_from_string("2024-01-01 24:00:00") ===> TEST PASSED
(exception: hour 24)
+det_date_time_from_string("2024-01-01 00:60:00") ===> TEST PASSED
(exception: minute 60)
+det_date_time_from_string("2024-01-01 00:00:62") ===> TEST PASSED
(exception: second 62)
+det_date_time_from_string("2024-01-01 00:00:00.") ===> TEST PASSED
(exception: trailing dot with no digits)
+det_date_time_from_string("2024-01-01 00:00:00.1234567") ===> TEST
PASSED (exception: seven fractional digits)
+det_date_time_from_string("-01-01 00:00:00") ===> TEST PASSED
(exception: negative sign but only two-digit year)
+det_date_time_from_string("-0001-13-01 00:00:00") ===> TEST PASSED
(exception: negative year with out-of-range month)
+det_date_time_from_string("2024-ab-01 00:00:00") ===> TEST PASSED
(exception: letter in month field)
+det_date_time_from_string("2024-01-01 xx:00:00") ===> TEST PASSED
(exception: letters in hour field)
+det_date_time_from_string("--0001-01-01 00:00:00") ===> TEST PASSED
(exception: double negative prefix)
+det_date_time_from_string("2024--01-01 00:00:00") ===> TEST PASSED
(exception: negative sign on month)
+det_date_time_from_string("2024-01-01 -01:00:00") ===> TEST PASSED
(exception: negative sign on hour)
+det_date_time_from_string("2024-01-01 00:00:-01") ===> TEST PASSED
(exception: negative sign on second)
+det_date_time_from_string("2024-01-01  00:00:00") ===> TEST PASSED
(exception: double space between date and time)
+det_date_time_from_string("2024-01-01 00:00:00.123.456") ===> TEST
PASSED (exception: two decimal points)
+det_date_time_from_string("2024-01-01 00:00:00.123456.7") ===> TEST
PASSED (exception: decimal point in microseconds follow by more)
+det_date_time_from_string("2024-001-01 00:00:00") ===> TEST PASSED
(exception: three-digit month)
+det_date_time_from_string("2024-01-001 00:00:00") ===> TEST PASSED
(exception: three-digit day)
+det_date_time_from_string("2024-01-01 000:00:00") ===> TEST PASSED
(exception: three-digit hour)
+det_date_time_from_string("2024-01-01 00:001:00") ===> TEST PASSED
(exception: three-digit minute)
+det_date_time_from_string("2024-01-01 00:00:001") ===> TEST PASSED
(exception: three-digit second)
+det_date_time_from_string("2024-01-01 00:00:00.0000000") ===> TEST
PASSED (exception: seven zeros in microseconds)
+det_date_time_from_string("2024-01-01 00 :00:00") ===> TEST PASSED
(exception: space within time component)
+det_date_time_from_string("2024 -01-01 00:00:00") ===> TEST PASSED
(exception: space within date component)
+det_date_time_from_string("2024:01:01 00:00:00") ===> TEST PASSED
(exception: colons in date part)
+det_date_time_from_string("2024-01-01 00-00-00") ===> TEST PASSED
(exception: dashes in time part)
+det_date_time_from_string("2024--01 00:00:00") ===> TEST PASSED
(exception: missing month)
+det_date_time_from_string("2024-01- 00:00:00") ===> TEST PASSED
(exception: missing day)
+det_date_time_from_string("2024-01-01 :00:00") ===> TEST PASSED
(exception: missing hour)
+det_date_time_from_string("2024-01-01 00::00") ===> TEST PASSED
(exception: missing minute)
+det_date_time_from_string("2024-01-01 00:00:") ===> TEST PASSED
(exception: trailing colon with no second digits)
+
diff --git a/tests/hard_coded/calendar_date_time_conv.m
b/tests/hard_coded/calendar_date_time_conv.m
new file mode 100644
index 000000000..b680be6a8
--- /dev/null
+++ b/tests/hard_coded/calendar_date_time_conv.m
@@ -0,0 +1,338 @@
+%---------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%---------------------------------------------------------------------------%
+%
+% Test predicates and functions from the calendar module that convert
+% date_times to and from strings.
+%
+%---------------------------------------------------------------------------%
+
+:- module calendar_date_time_conv.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%---------------------------------------------------------------------------%
+%---------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module bool.
+:- import_module calendar.
+:- import_module list.
+:- import_module string.
+
+%---------------------------------------------------------------------------%
+
+main(!IO) :-
+    test_valid_date_times(!IO),
+    test_invalid_date_times(!IO),
+    test_exception_valid_date_times(!IO),
+    test_exception_invalid_date_times(!IO).
+
+%---------------------------------------------------------------------------%
+
+:- pred test_valid_date_times(io::di, io::uo) is det.
+
+test_valid_date_times(!IO) :-
+    io.write_string(
+        "=== Testing date_time_from_string/2 with valid inputs ===\n\n",
+        !IO),
+    list.foldl(do_test_valid_date_time(yes), valid_date_times, !IO),
+    list.foldl(do_test_valid_date_time(no), valid_no_roundtrip_date_times,
+        !IO),
+    io.nl(!IO).
+
+:- pred do_test_valid_date_time(bool::in, dt_conv_test::in,
+    io::di, io::uo) is det.
+
+do_test_valid_date_time(CheckRoundTrip, Test, !IO) :-
+    Test = dt_conv_test(Desc, TestString),
+    io.format("date_time_from_string(\"%s\") ===> ", [s(TestString)], !IO),
+    ( if date_time_from_string(TestString, DateTime) then
+        RoundTripString = date_time_to_string(DateTime),
+        (
+            CheckRoundTrip = yes,
+            ( if TestString = RoundTripString then
+                io.format("TEST PASSED (accepted: %s)\n",
+                    [s(string(DateTime))], !IO)
+            else
+                io.write_string("TEST FAILED (roundtrip failed)\n", !IO)
+            )
+        ;
+            CheckRoundTrip = no,
+            io.format("TEST PASSED (accepted: %s: to-string: \"%s\"))\n",
+                [s(string(DateTime)), s(RoundTripString)], !IO)
+        )
+    else
+        io.format("TEST FAILED (rejected: %s)\n", [s(Desc)], !IO)
+    ).
+
+%---------------------------------------------------------------------------%
+
+:- pred test_invalid_date_times(io::di, io::uo) is det.
+
+test_invalid_date_times(!IO) :-
+    io.write_string(
+        "=== Testing date_time_from_string/2 with invalid inputs ===\n\n",
+        !IO),
+    list.foldl(do_test_invalid_date_time, invalid_date_times, !IO),
+    io.nl(!IO).
+
+:- pred do_test_invalid_date_time(dt_conv_test::in, io::di, io::uo) is det.
+
+do_test_invalid_date_time(Test, !IO) :-
+    Test = dt_conv_test(Desc, TestString),
+    io.format("date_time_from_string(\"%s\") ===> ", [s(TestString)], !IO),
+    ( if date_time_from_string(TestString, DateTime) then
+        DateTimeString = date_time_to_string(DateTime),
+        io.format("TEST FAILED (accepted: %s)\n",
+            [s(DateTimeString)], !IO)
+    else
+        io.format("TEST PASSED (rejected: %s)\n", [s(Desc)], !IO)
+    ).
+
+%---------------------------------------------------------------------------%
+
+:- pred test_exception_valid_date_times(io::di, io::uo) is cc_multi.
+
+test_exception_valid_date_times(!IO) :-
+    io.write_string(
+        "=== Testing det_date_from_string/1 with valid inputs ===\n\n", !IO),
+    list.foldl(do_test_exception_valid_date_time, valid_date_times, !IO),
+    io.nl(!IO).
+
+:- pred do_test_exception_valid_date_time(dt_conv_test::in, io::di, io::uo)
+    is cc_multi.
+
+do_test_exception_valid_date_time(Test, !IO) :-
+    Test = dt_conv_test(Desc, TestString),
+    io.format("det_date_time_from_string(\"%s\") ===> ", [s(TestString)], !IO),
+    ( try []
+        DateTime = det_date_time_from_string(TestString)
+    then
+        io.format("TEST PASSED (accepted: %s)\n",
+            [s(string(DateTime))], !IO)
+    catch_any _ ->
+        io.format("TEST FAILED (exception: %s)\n", [s(Desc)], !IO)
+    ).
+
+%---------------------------------------------------------------------------%
+
+:- pred test_exception_invalid_date_times(io::di, io::uo) is cc_multi.
+
+test_exception_invalid_date_times(!IO) :-
+    io.write_string(
+        "=== Testing det_date_from_string/1 with invalid inputs ===\n\n", !IO),
+    list.foldl(do_test_exception_invalid_date_time, invalid_date_times, !IO),
+    io.nl(!IO).
+
+:- pred do_test_exception_invalid_date_time(dt_conv_test::in, io::di, io::uo)
+    is cc_multi.
+
+do_test_exception_invalid_date_time(Test, !IO) :-
+    Test = dt_conv_test(Desc, TestString),
+    io.format("det_date_time_from_string(\"%s\") ===> ", [s(TestString)], !IO),
+    ( try []
+        DateTime = det_date_time_from_string(TestString)
+    then
+        io.format("TEST FAILED (accepted: %s)\n",
+            [s(string(DateTime))], !IO)
+    catch_any _ ->
+        io.format("TEST PASSED (exception: %s)\n", [s(Desc)], !IO)
+    ).
+
+%---------------------------------------------------------------------------%
+
+:- type dt_conv_test
+    --->    dt_conv_test(
+                description :: string,
+                test_string :: string
+            ).
+
+%---------------------------------------------------------------------------%
+
+% Valid date_times crated from strings are mostly round-trippable through
+% date_time_to_string/1. The only exceptions are where the year components
+% have leading zeros or the fractional components have trailing zeros.
+
+    % These can be round-tripped through date_time_to_string/1.
+    %
+:- func valid_date_times = list(dt_conv_test).
+
+valid_date_times = [
+    dt_conv_test("unremarkable mid-month date", "2024-01-15 12:30:00"),
+    dt_conv_test("arbitrary date in the past", "1977-10-09 12:00:00"),
+
+    dt_conv_test("first day of year zero", "0000-01-01 00:00:00"),
+    dt_conv_test("last day of year zero", "0000-12-31 23:59:59"),
+    dt_conv_test("first day of year one", "0001-01-01 00:00:00"),
+    dt_conv_test("last four-digit year", "9999-12-31 23:59:59"),
+
+    dt_conv_test("year -1", "-0001-01-01 00:00:00"),
+    dt_conv_test("end of year -1", "-0001-12-31 00:00:00"),
+    dt_conv_test("Julian period origin in proleptic Gregorian calendar",
+        "-4713-11-24 00:00:00"),
+
+    dt_conv_test("five-digit year", "10000-01-01 00:00:00"),
+    dt_conv_test("six-digit year", "100000-01-01 00:00:00"),
+
+    dt_conv_test("400-year leap year", "2000-02-29 00:00:00"),
+    dt_conv_test("ordinary leap year", "2024-02-29 00:00:00"),
+    dt_conv_test("year zero is a leap year", "0000-02-29 00:00:00"),
+    dt_conv_test("negative leap year", "-0004-02-29 00:00:00"),
+
+    dt_conv_test("last day of Feb in a non-leap year",
+        "2023-02-28 00:00:00"),
+    dt_conv_test("last day of Feb in a century non-leap year",
+        "1900-02-28 00:00:00"),
+
+    dt_conv_test("end of Jan",   "2024-01-31 00:00:00"),
+    dt_conv_test("end of March", "2024-03-31 00:00:00"),
+    dt_conv_test("end of April", "2024-04-30 00:00:00"),
+    dt_conv_test("end of May",   "2024-05-31 00:00:00"),
+    dt_conv_test("end of June",  "2024-06-30 00:00:00"),
+    dt_conv_test("end of July",  "2024-07-31 00:00:00"),
+    dt_conv_test("end of Aug",   "2024-08-31 00:00:00"),
+    dt_conv_test("end of Sep",   "2024-09-30 00:00:00"),
+    dt_conv_test("end of Oct",   "2024-10-31 00:00:00"),
+    dt_conv_test("end of Nov",   "2024-11-30 00:00:00"),
+    dt_conv_test("end of Dec",   "2024-12-31 00:00:00"),
+
+    dt_conv_test("midnight", "2024-01-01 00:00:00"),
+    dt_conv_test("last second of the day", "2024-01-01 23:59:59"),
+    dt_conv_test("leap second", "2024-01-01 00:00:60"),
+    dt_conv_test("double leap second", "2024-01-01 00:00:61"),
+
+    dt_conv_test("one fractional digit", "2024-01-01 00:00:00.1"),
+    dt_conv_test("two fractional digits", "2024-01-01 00:00:00.12"),
+    dt_conv_test("three fractional digits", "2024-01-01 00:00:00.123"),
+    dt_conv_test("four fractional digits", "2024-01-01 00:00:00.1234"),
+    dt_conv_test("five fractional digits", "2024-01-01 00:00:00.12345"),
+    dt_conv_test("six fractional digits", "2024-01-01 00:00:00.123456"),
+    dt_conv_test("smallest nonzero microsecond", "2024-01-01 00:00:00.000001"),
+    dt_conv_test("largest microsecond value", "2024-01-01 00:00:00.999999"),
+
+    dt_conv_test("all maximum", "2024-12-31 23:59:61.999999"),
+    dt_conv_test("Unix epoch", "1970-01-01 00:00:00"),
+    dt_conv_test("first day of the Gregorian calendar",
+        "1582-10-15 00:00:00")
+].
+
+    % These cannot be round-tripped through date_time_to_string/1.
+    %
+:- func valid_no_roundtrip_date_times = list(dt_conv_test).
+
+valid_no_roundtrip_date_times =[
+    dt_conv_test("one fractional trailing zero", "2024-01-01 00:00:00.10"),
+    dt_conv_test("two fractional trailing zeros", "2024-01-01 00:00:00.100"),
+    dt_conv_test("three fractional trailing zeros",
+        "2024-01-01 00:00:00.1000"),
+    dt_conv_test("four fractional trailing zeros",
+        "2024-01-01 00:00:00.10000"),
+    dt_conv_test("five fractional trailing zeros",
+        "2024-01-01 00:00:00.100000"),
+
+    dt_conv_test("trailing zeros after two fractional digits",
+        "2024-01-01 00:00:00.120000"),
+    dt_conv_test("trailing zeros after three fractional digits",
+        "2024-01-01 00:00:00.123000"),
+    dt_conv_test("trailing zeros after four fractional digits",
+        "2024-01-01 00:00:00.123400"),
+    dt_conv_test("trailing zeros after five fractional digits",
+        "2024-01-01 00:00:00.123450"),
+
+    dt_conv_test("all zero fractional digits",
+        "2024-01-01 00:00:00.000000"),
+
+    dt_conv_test("five digit year zero", "00000-01-01 00:00:00"),
+    dt_conv_test("five digit year one", "00001-01-01 00:00:00")
+].
+
+%---------------------------------------------------------------------------%
+
+:- func invalid_date_times = list(dt_conv_test).
+
+invalid_date_times = [
+    dt_conv_test("empty string", ""),
+    dt_conv_test("blank string", "    "),
+    dt_conv_test("not a date", "not a date"),
+    dt_conv_test("missing time component", "2024-01-01"),
+    dt_conv_test("missing date component", "12:00:00"),
+    dt_conv_test("incorrect date separator", "2024/01/01 00:00:00"),
+    dt_conv_test("ISO 8601 T separator", "2024-01-01T00:00:00"),
+    dt_conv_test("missing seconds", "2024-01-01 00:00"),
+    dt_conv_test("trailing space", "2024-01-01 00:00:00 "),
+    dt_conv_test("leading space", " 2024-01-01 00:00:00"),
+    dt_conv_test("trailing timezone indicator", "2024-01-01 00:00:00Z"),
+    dt_conv_test("trailing UTC offset", "2024-01-01 00:00:00+10:00"),
+
+    dt_conv_test("two-digit year", "99-01-01 00:00:00"),
+    dt_conv_test("three-digit year", "999-01-01 00:00:00"),
+
+    dt_conv_test("one-digit month", "2024-1-01 00:00:00"),
+    dt_conv_test("one-digit day", "2024-01-1 00:00:00"),
+    dt_conv_test("one-digit hour", "2024-01-01 0:00:00"),
+    dt_conv_test("one-digit minute", "2024-01-01 00:0:00"),
+    dt_conv_test("one-digit second", "2024-01-01 00:00:0"),
+
+    dt_conv_test("month zero", "2024-00-01 00:00:00"),
+    dt_conv_test("month 13", "2024-13-01 00:00:00"),
+    dt_conv_test("day zero", "2024-01-00 00:00:00"),
+    dt_conv_test("day 32 in a 31-day month", "2024-01-32 00:00:00"),
+    dt_conv_test("Feb 29 in a non-leap year",  "2023-02-29 00:00:00"),
+    dt_conv_test("Feb 29 in a century non-leap year", "1900-02-29 00:00:00"),
+    dt_conv_test("day 31 in a 30-day month", "2024-04-31 00:00:00"),
+    dt_conv_test("hour 24", "2024-01-01 24:00:00"),
+    dt_conv_test("minute 60", "2024-01-01 00:60:00"),
+    dt_conv_test("second 62", "2024-01-01 00:00:62"),
+
+    dt_conv_test("trailing dot with no digits", "2024-01-01 00:00:00."),
+    dt_conv_test("seven fractional digits", "2024-01-01 00:00:00.1234567"),
+
+    dt_conv_test("negative sign but only two-digit year",
+        "-01-01 00:00:00"),
+    dt_conv_test("negative year with out-of-range month",
+        "-0001-13-01 00:00:00"),
+
+    dt_conv_test("letter in month field", "2024-ab-01 00:00:00"),
+    dt_conv_test("letters in hour field", "2024-01-01 xx:00:00"),
+
+    dt_conv_test("double negative prefix", "--0001-01-01 00:00:00"),
+    dt_conv_test("negative sign on month", "2024--01-01 00:00:00"),
+    dt_conv_test("negative sign on hour", "2024-01-01 -01:00:00"),
+    dt_conv_test("negative sign on second", "2024-01-01 00:00:-01"),
+
+    dt_conv_test("double space between date and time",
+        "2024-01-01  00:00:00"),
+    dt_conv_test("two decimal points", "2024-01-01 00:00:00.123.456"),
+    dt_conv_test("decimal point in microseconds follow by more",
+        "2024-01-01 00:00:00.123456.7"),
+    dt_conv_test("three-digit month", "2024-001-01 00:00:00"),
+    dt_conv_test("three-digit day", "2024-01-001 00:00:00"),
+    dt_conv_test("three-digit hour", "2024-01-01 000:00:00"),
+    dt_conv_test("three-digit minute", "2024-01-01 00:001:00"),
+    dt_conv_test("three-digit second", "2024-01-01 00:00:001"),
+
+    dt_conv_test("seven zeros in microseconds",
+        "2024-01-01 00:00:00.0000000"),
+
+    dt_conv_test("space within time component", "2024-01-01 00 :00:00"),
+    dt_conv_test("space within date component", "2024 -01-01 00:00:00"),
+
+    dt_conv_test("colons in date part", "2024:01:01 00:00:00"),
+    dt_conv_test("dashes in time part", "2024-01-01 00-00-00"),
+
+    dt_conv_test("missing month", "2024--01 00:00:00"),
+    dt_conv_test("missing day", "2024-01- 00:00:00"),
+    dt_conv_test("missing hour", "2024-01-01 :00:00"),
+    dt_conv_test("missing minute", "2024-01-01 00::00"),
+    dt_conv_test("trailing colon with no second digits",
+        "2024-01-01 00:00:")
+].
+
+%---------------------------------------------------------------------------%
+:- end_module calendar_date_time_conv.
+%---------------------------------------------------------------------------%


More information about the reviews mailing list