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.
This commit is contained in:
Julien Fischer
2026-04-07 10:59:13 +10:00
parent d451d8aca5
commit bc89fe7fc9
3 changed files with 563 additions and 0 deletions

View File

@@ -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 \

View File

@@ -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)

View File

@@ -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.
%---------------------------------------------------------------------------%