test_MAPL_DateTime_Parsing.pf Source File


This file depends on

sourcefile~~test_mapl_datetime_parsing.pf~~EfferentGraph sourcefile~test_mapl_datetime_parsing.pf test_MAPL_DateTime_Parsing.pf sourcefile~mapl_exceptionhandling.f90 MAPL_ExceptionHandling.F90 sourcefile~test_mapl_datetime_parsing.pf->sourcefile~mapl_exceptionhandling.f90 sourcefile~errorhandling.f90 ErrorHandling.F90 sourcefile~mapl_exceptionhandling.f90->sourcefile~errorhandling.f90 sourcefile~mapl_throw.f90 MAPL_Throw.F90 sourcefile~mapl_exceptionhandling.f90->sourcefile~mapl_throw.f90 sourcefile~errorhandling.f90->sourcefile~mapl_throw.f90

Source Code

#include "MAPL_Exceptions.h"

! Test suite
! Not complete
! Some portions are commented out because corresponding procedures to be
! tested are not working completely.
module test_MAPL_DateTime_Parsing
      use MAPL_ExceptionHandling
      use MAPL_DateTime_Parsing 
      use pfunit
      use, intrinsic :: iso_fortran_env, only: R64 => real64

      implicit none

      character(len=*), parameter :: DATE_DELIMITER = '-'
      character(len=*), parameter :: TIME_DELIMITER = ':'
      integer, dimension(12), parameter :: ENDS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
      integer, dimension(size(ENDS)), parameter :: ENDS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
      integer, parameter :: MAX_LEN = 1024

      integer, parameter :: SUCCESS = _SUCCESS
      integer, parameter :: FAILURE = _FAILURE

contains

   @test
   subroutine test_multipleof()
      @assertTrue(multipleof(21, 7))
      @assertFalse(multipleof(22, 7))
      @assertTrue(21 .multipleof. 7)
      @assertFalse(22 .multipleof. 7)
   end subroutine test_multipleof

   @test
   subroutine test_between_op()
      integer, parameter :: lower = 36
      integer, parameter :: upper = 48
      integer, parameter :: delta = 6
      @assertTrue((lower + delta) .between. [lower, upper])
      @assertFalse(lower .between. [lower, upper])
      @assertFalse((lower - 1) .between. [lower, upper])
      @assertFalse(upper .between. [lower, upper])
      @assertFalse((upper + 1) .between. [lower, upper])
   end subroutine test_between_op

   @test
   subroutine test_in_op()
      integer, parameter :: lower = 36
      integer, parameter :: upper = 48
      integer, parameter :: delta = 6
      @assertTrue((lower + delta) .between. [lower, upper])
      @assertFalse(lower .between. [lower, upper])
      @assertFalse((lower - 1) .between. [lower, upper])
      @assertFalse(upper .between. [lower, upper])
      @assertFalse((upper + 1) .between. [lower, upper])
   end subroutine test_in_op

   @test
   subroutine test_is_digit()
      integer :: i
      integer, parameter :: imin = 0
      integer, parameter :: imax = 127
      integer, parameter :: i1 = iachar('0') - 1
      integer, parameter :: i2 = iachar('9') + 1
      integer, parameter :: acodes(*) = [(i, i = imin, i1), (i, i = i2, imax)]
      integer :: i0 = iachar('0')

      do i = 0, 9
         @assertTrue(is_digit(achar(i0+i)))
      end do

      do i = 1, size(acodes)
         @assertFalse(is_digit(achar(acodes(i))))
      end do

   end subroutine test_is_digit

   @test
   subroutine test_is_whole_number()
      integer :: n

      do n= 0, 9
         @assertTrue(is_whole_number(n))
      end do

      do n= 1, 9
         @assertFalse(is_whole_number(-n))
      end do

   end subroutine test_is_whole_number

   @test
   subroutine test_get_integer_digit()
      integer :: n
      integer, parameter :: NONDIGIT = -1
      integer, parameter :: ASCII_MIN = 0
      integer, parameter :: ASCII_MAX = 127
      integer, parameter :: ASCII_LT0 = iachar('0') - 1
      integer, parameter :: ASCII_GT9 = iachar('9') + 1
      character(len=10), parameter :: digit = '0123456789'
      character :: c

      do n = 1, len(digit)
         @assertEqual(n-1, get_integer_digit(digit(n:n)))
      end do

      do n = ASCII_MIN, ASCII_LT0
         c = achar(n)
         @assertEqual(NONDIGIT, get_integer_digit(c))
      end do

      do n = ASCII_GT9, ASCII_MAX
         c = achar(n)
         @assertEqual(NONDIGIT, get_integer_digit(c))
      end do
   end subroutine test_get_integer_digit

   @test
   subroutine test_get_integer_digit_from_string
      integer :: i
      integer, parameter :: NONDIGIT = -1
      character(len=5), parameter :: digit = '19150'
      integer, dimension(5) :: values = [1, 9, 1, 5, 0]

      @assertEqual(1, get_integer_digit_from_string('19150', 1))

      do i= 1, len(digit)
         @assertEqual(values(i), get_integer_digit_from_string(digit, i))
      end do

      @assertEqual(NONDIGIT, get_integer_digit_from_string(digit, 0))
      @assertEqual(NONDIGIT, get_integer_digit_from_string(digit, -1))
      @assertEqual(NONDIGIT, get_integer_digit_from_string(digit, len(digit)+1))
   end subroutine test_get_integer_digit_from_string

   @test
   subroutine test_read_whole_number()
      character(len=*), parameter :: NUM_STRING = '01234'
      integer, parameter :: WHOLE_NUMBER = 1234
      integer, parameter :: NOT_WHOLE_NUMBER = -1
      character(len=*), parameter :: LETTERS = 'ABCDEFG'
      character(len=*), parameter :: ALPHA_NUMERICS = '9B4D3F7'
      character(len=*), parameter :: NEGATIVE_INTEGER = '-' // NUM_STRING
      character(len=*), parameter :: SYMBOLS = '100%'
      character(len=*), parameter :: FLOATING_POINT = '10.07'
      character(len=*), parameter :: POSITIVE_INTEGER = '+' // NUM_STRING

      @assertEqual(WHOLE_NUMBER, read_whole_number(NUM_STRING))

      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number(LETTERS))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number(ALPHA_NUMERICS))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number(NEGATIVE_INTEGER))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number(SYMBOLS))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number(FLOATING_POINT))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number(POSITIVE_INTEGER))
   end subroutine test_read_whole_number

   @test
   subroutine test_read_whole_number_indexed()
      character(len=11), parameter :: STRING = '12345678901'
      character(len=15), parameter :: BAD_STRING = '18The3.1415...'
      integer, parameter :: NOT_WHOLE_NUMBER = -1

      @assertEqual(12, read_whole_number_indexed(STRING, 1, 2))
      @assertEqual(90, read_whole_number_indexed(STRING, 9, 10))
      @assertEqual(7, read_whole_number_indexed(STRING, 7, 7))
      @assertEqual(1, read_whole_number_indexed(STRING, 10, 11))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(STRING, 2, 1))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(STRING, 1, 12))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(STRING, -1, 9))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(STRING, 1, -1))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(STRING, 0, 2))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(STRING, 2, 0))
      @assertEqual(18, read_whole_number_indexed(BAD_STRING, 1, 2))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(BAD_STRING, 2, 3))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(BAD_STRING, 3, 5))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(BAD_STRING, 5, 5))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(BAD_STRING, 5, 6))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(BAD_STRING, 6, 11))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(BAD_STRING, 7, 7))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed(BAD_STRING, 7, 8))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed('+800', 1, 3))
      @assertEqual(NOT_WHOLE_NUMBER, read_whole_number_indexed('-800', 1, 3))
   end subroutine test_read_whole_number_indexed

   @test
   subroutine test_undelimit()
      character(len=32), parameter :: UNDELIMITED_DATE = '20220720'
      character(len=32), parameter :: DATE ='2022-07-20' 
      character(len=32), parameter :: UNDELIMITED_TIME1 = '153437'
      character(len=32), parameter :: TIME1 = '15:34:37'
      character(len=32), parameter :: UNDELIMITED_TIME2 = '153437421'
      character(len=32), parameter :: TIME2 = '15:34:37.421'
      integer, parameter :: DATEWIDTH = len_trim(UNDELIMITED_DATE)
      integer, parameter :: TIMEWIDTH1 = len_trim(UNDELIMITED_TIME1)
      integer, parameter :: TIMEWIDTH2 = len_trim(UNDELIMITED_TIME2)

      @assertEqual(trim(UNDELIMITED_DATE), trim(undelimit(DATE,'-')))
      @assertEqual(DATEWIDTH, len_trim(UNDELIMITED_DATE)) 
      @assertEqual(trim(UNDELIMITED_TIME1), trim(undelimit(TIME1,':')))
      @assertEqual(TIMEWIDTH1, len_trim(UNDELIMITED_TIME1))
      @assertEqual(trim(UNDELIMITED_TIME2), trim(undelimit(undelimit(TIME2,':'),'.')))
      @assertEqual(TIMEWIDTH2, len_trim(UNDELIMITED_TIME2))
      @assertTrue(trim(UNDELIMITED_TIME1) /= trim(undelimit(TIME1,'.')))
      @assertEqual(trim(UNDELIMITED_TIME1), trim(undelimit(UNDELIMITED_TIME1,':')))
      @assertEqual('', trim(undelimit('',':')))
   end subroutine test_undelimit

   @test
   subroutine test_undelimit_all()
      character(len=32), parameter :: UNDELIMITED_DATE = '20220720'
      character(len=32), parameter :: DATE ='2022-07-20' 
      character(len=32), parameter :: UNDELIMITED_TIME1 = '153437'
      character(len=32), parameter :: TIME1 = '15:34:37'
      character(len=32), parameter :: UNDELIMITED_TIME2 = '153437421'
      character(len=32), parameter :: TIME2 = '15:34:37.421'
      integer, parameter :: DATEWIDTH = len_trim(UNDELIMITED_DATE)
      integer, parameter :: TIMEWIDTH1 = len_trim(UNDELIMITED_TIME1)
      integer, parameter :: TIMEWIDTH2 = len_trim(UNDELIMITED_TIME2)

      character(len=*), parameter :: cf_datetime_string_A = '2023-04-23 21:05:37'
      character(len=*), parameter :: expected_A = '20230423210537'
      character(len=*), parameter :: cf_datetime_string_B= '2023-04-23 21:05:37.337'
      character(len=*), parameter :: expected_B = '20230423210537337'
      character(len=*), parameter :: undelimited_datetime_string_C = '20230423210537'
      character(len=*), parameter :: fail_D = '2023-4-23 21:05:37'
      character(len=*), parameter :: fail_E = '2023-04-23 21:5:37'

      @assertEqual(trim(UNDELIMITED_DATE), trim(undelimit_all(DATE)))
      @assertEqual(DATEWIDTH, len_trim(UNDELIMITED_DATE)) 
      @assertEqual(trim(UNDELIMITED_TIME1), trim(undelimit_all(TIME1)))
      @assertEqual(TIMEWIDTH1, len_trim(UNDELIMITED_TIME1))
      @assertEqual(trim(UNDELIMITED_TIME2), trim(undelimit_all(TIME2)))
      @assertEqual(TIMEWIDTH2, len_trim(UNDELIMITED_TIME2))
      @assertEqual(trim(UNDELIMITED_TIME1), trim(undelimit_all(UNDELIMITED_TIME1)))
      @assertEqual('', trim(undelimit_all('')))

      @assertEqual(trim(expected_A), trim(undelimit_all(cf_datetime_string_A)), 'Mismatch A')
      @assertEqual(trim(expected_B), trim(undelimit_all(cf_datetime_string_B)), 'Mismatch B')
      @assertEqual(trim(undelimited_datetime_string_C), trim(undelimit_all(undelimited_datetime_string_C)), 'Mismatch C')

   end subroutine test_undelimit_all

   @test
   subroutine test_is_leap_year()
      @assertTrue(is_leap_year(2024))
      @assertFalse(is_leap_year(2023))
      @assertFalse(is_leap_year(2022))
      @assertFalse(is_leap_year(2021))
      @assertFalse(is_leap_year(2100))
      @assertTrue(is_leap_year(2000))
   end subroutine test_is_leap_year

   @test
   subroutine test_get_month_ends()
      integer, dimension(size(ENDS)) :: actual
      integer :: i

      actual = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

      actual = get_month_ends(2022)
      do i = 1, size(actual)
         @assertEqual(ENDS(i), actual(i))
      end do

      actual = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

      actual = get_month_ends(2020)
      do i = 1, size(actual)
         @assertEqual(ENDS_LEAP(i), actual(i))
      end do
   end subroutine test_get_month_ends

   @test
   subroutine test_get_month_end()
      integer, parameter :: year = 2022
      integer, parameter :: leap_year = 2020
      integer :: i

      do i = 1, 12
         @assertEqual(ENDS(i), get_month_end(year, i))
         @assertEqual(ENDS_LEAP(i), get_month_end(leap_year, i))
      end do

   end subroutine test_get_month_end

   @test
   subroutine test_is_valid_year()
      integer :: i

      @assertFalse(is_valid_year(-1))
      @assertFalse(is_valid_year(10000))
      do i = 0, 9999
         @assertTrue(is_valid_year(i))
      end do
   end subroutine test_is_valid_year

   @test
   subroutine test_is_valid_month()
      integer :: i

      @assertFalse(is_valid_month(0))
      @assertFalse(is_valid_month(-1))
      @assertFalse(is_valid_month(13))
      do i = 1, 12
         @assertTrue(is_valid_month(i))
      end do
   end subroutine test_is_valid_month

   @test
   subroutine test_is_valid_day()
      character(len=*), parameter :: FMT = '("day (",i4,", ",i2,", ",i2,")",a)'
      character(len=*), parameter :: SHOULD = ' should be valid.'
      character(len=*), parameter :: SHOULD_NOT = ' should not be valid.'
      character(len=255) :: error_message

      integer :: d, m, y
      d = 30; m = 4; y = 2023
      write(error_message, fmt=FMT) y, m, d, SHOULD
      @assertTrue(is_valid_day(y, m, d), trim(error_message))
      d = 31
      write(error_message, fmt=FMT) y, m, d, SHOULD_NOT
      @assertFalse(is_valid_day(y, m, d), trim(error_message))
      d = 29
      write(error_message, fmt=FMT) y, m, d, SHOULD
      @assertTrue(is_valid_day(y, m, d), trim(error_message))
      m = 2
      write(error_message, fmt=FMT) y, m, d, SHOULD_NOT
      @assertFalse(is_valid_day(y, m, d), trim(error_message))
      y = 2024
      write(error_message, fmt=FMT) y, m, d, SHOULD
      @assertTrue(is_valid_day(y, m, d), trim(error_message))
   end subroutine test_is_valid_day

   @test
   subroutine test_is_valid_hour()
      integer, parameter :: IMIN = 0
      integer, parameter :: IMAX = 23
      integer :: i
      do i= IMIN, IMAX
         @assertTrue(is_valid_hour(i))
      end do
      @assertFalse(is_valid_hour(IMAX+1))
      @assertFalse(is_valid_hour(IMIN-1))
   end subroutine test_is_valid_hour

   subroutine test_is_valid_minute()
      integer, parameter :: IMIN = 0
      integer, parameter :: IMAX = 59
      integer :: i
      do i= IMIN, IMAX
         @assertTrue(is_valid_minute(i))
      end do
      @assertFalse(is_valid_minute(IMAX+1))
      @assertFalse(is_valid_minute(IMIN-1))
   end subroutine test_is_valid_minute

   subroutine test_is_valid_second()
      integer, parameter :: IMIN = 0
      integer, parameter :: IMAX = 60
      integer :: i
      do i= IMIN, IMAX
         @assertTrue(is_valid_second(i))
      end do
      @assertFalse(is_valid_second(IMAX+1))
      @assertFalse(is_valid_second(IMIN-1))
   end subroutine test_is_valid_second

   @test
   subroutine test_is_valid_millisecond()
      integer, parameter :: IMIN = 0
      integer, parameter :: IMAX = 999
      integer :: i
      do i= IMIN, IMAX
         @assertTrue(is_valid_millisecond(i))
      end do
      @assertFalse(is_valid_millisecond(IMAX+1))
      @assertFalse(is_valid_millisecond(IMIN-1))
   end subroutine test_is_valid_millisecond

   @test
   subroutine test_is_valid_timezone_offset()
      integer, parameter :: GOOD_TZ = 0
      integer, parameter :: BAD_TZ = 60
      @assertTrue(is_valid_timezone_offset(0))
      @assertTrue(is_valid_timezone_offset(60))
      @assertTrue(is_valid_timezone_offset(-60))
      @assertFalse(is_valid_timezone_offset(4320))
      @assertFalse(is_valid_timezone_offset(-1441))
   end subroutine test_is_valid_timezone_offset

   @test
   subroutine test_is_valid_date()
      type(date_fields) :: valid_date
      type(date_fields) :: invalid_date
      valid_date = date_fields(2022, 7, 7)
      invalid_date = date_fields(2022, 6, 31)
      @assertTrue(is_valid_date(valid_date))
      @assertFalse(is_valid_date(invalid_date))
   end subroutine test_is_valid_date

   @test
   subroutine test_is_valid_time()
      type(time_fields) :: valid_time
      type(time_fields) :: invalid_time
      valid_time = time_fields(9, 41, 33, 456, 0)
      invalid_time = time_fields(24, 41, 33, 456, 0)
      @assertTrue(is_valid_time(valid_time))
      @assertFalse(is_valid_time(invalid_time))
   end subroutine test_is_valid_time

   @test
   subroutine test_parse_timezone_offset()
      integer, parameter :: FIELD_WIDTH = 2
      integer, parameter :: INVALID_OFFSET = -1

      character(len=*), parameter :: HOUR_ONLY = '15'
      character(len=*), parameter :: HOUR_MINUTE00 = '1500'
      character(len=*), parameter :: HOUR_MINUTE = '1530'
      integer, parameter :: OFFSET1500 = 15*60
      integer, parameter :: OFFSET1530 = 15*60 + 30

      character(len=*), parameter :: NARROW1 = '1'
      character(len=*), parameter :: NARROW3 = '153'
      character(len=*), parameter :: WIDE5 = '15307'
      character(len=*), parameter :: GARBAGE = '1T3R'

      character(len=*), parameter :: NEG_HOUR = '-15'
      character(len=*), parameter :: NEG_HOURMIN00 = '-1500'
      character(len=*), parameter :: NEG_HOURMIN = '-1530'
      integer, parameter :: OFFSETM1500 = -1*(15*60)
      integer, parameter :: OFFSETM1530 = -1*(15*60 + 30)

      @assertEqual(OFFSET1500, parse_timezone_offset(HOUR_ONLY, FIELD_WIDTH))
      @assertEqual(OFFSET1500, parse_timezone_offset(HOUR_MINUTE00, FIELD_WIDTH))
      @assertEqual(OFFSET1530, parse_timezone_offset(HOUR_MINUTE, FIELD_WIDTH))

      @assertTrue(parse_timezone_offset(NARROW1, FIELD_WIDTH) < 0)
      @assertTrue(parse_timezone_offset(NARROW3, FIELD_WIDTH) < 0)
      @assertTrue(parse_timezone_offset(WIDE5, FIELD_WIDTH) < 0)
      @assertTrue(parse_timezone_offset(GARBAGE, FIELD_WIDTH) < 0)

   end subroutine test_parse_timezone_offset

   @test
   subroutine test_parse_date()
      type(date_fields) :: date

      date = parse_date('2022-07-07', DATE_DELIMITER)
      @assertTrue(date%is_valid_)
      @assertEqual(2022, date%year_)
      @assertEqual(7, date%month_)
      @assertEqual(7, date%day_)

      date = parse_date('20220707', DATE_DELIMITER)
      @assertTrue(date%is_valid_)
      @assertEqual(2022, date%year_)
      @assertEqual(7, date%month_)
      @assertEqual(7, date%day_)
   end subroutine test_parse_date

   @test
   subroutine test_parse_time()
      type(time_fields) :: time

      time = parse_time('17:41:07.513Z', TIME_DELIMITER)
      @assertTrue(time%is_valid_)
      @assertEqual(17, time%hour_)
      @assertEqual(41, time%minute_)
      @assertEqual(7, time%second_)
      @assertEqual(513, time%millisecond_)
      @assertEqual(0, time%timezone_offset_)

      time = parse_time('174107.513Z', TIME_DELIMITER)
      @assertTrue(time%is_valid_)
      @assertEqual(17, time%hour_)
      @assertEqual(41, time%minute_)
      @assertEqual(7, time%second_)
      @assertEqual(513, time%millisecond_)
      @assertEqual(0, time%timezone_offset_)

   end subroutine test_parse_time

   @test
   subroutine test_construct_date_fields_null()
      type(date_fields) :: df
      df = date_fields()
      @assertFalse(df % is_valid(), 'null df should not be valid.')
   end subroutine test_construct_date_fields_null

   @test
   subroutine test_construct_time_fields_null()
      type(time_fields) :: tf
      tf = time_fields()
      @assertFalse(tf % is_valid(), 'null tf should not be valid.')
   end subroutine test_construct_time_fields_null

   @test
   subroutine test_get_year_field
      type(date_fields) :: df
      integer :: d = 23, m = 4, y = 2023
      df = date_fields(y, m, d)
      @assertTrue(df % is_valid(), 'Failed to initialize df')
      @assertEqual(y, df % year(), 'Wrong year')
   end subroutine test_get_year_field

   @test
   subroutine test_get_month_field
      type(date_fields) :: df
      integer :: d = 23, m = 4, y = 2023
      df = date_fields(y, m, d)
      @assertTrue(df % is_valid(), 'Failed to initialize df')
      @assertEqual(m, df % month(), 'Wrong month')
   end subroutine test_get_month_field

   @test
   subroutine test_get_day_field
      type(date_fields) :: df
      integer :: d = 23, m = 4, y = 2023
      df = date_fields(y, m, d)
      @assertTrue(df % is_valid(), 'Failed to initialize df')
      @assertEqual(d, df % day(), 'Wrong month')
   end subroutine test_get_day_field

   @test
   subroutine test_are_valid_date_fields
      type(date_fields) :: df
      df = date_fields(2023, 4, 25) 
      @assertTrue(df % is_valid(), 'df should be valid.')
      df = date_fields(2023, 4, 31)
      @assertFalse(df % is_valid(), 'df should not be valid.')
   end subroutine test_are_valid_date_fields

   @test
   subroutine test_get_hour_field
      type(time_fields) :: tf
      integer :: h = 13, m = 43, s = 37, ms = 100, tzo = 0
      tf = time_fields(h, m, s, ms, tzo)
      @assertTrue(tf % is_valid(), 'Failed to initialize tf')
      @assertEqual(h, tf % hour())
   end subroutine test_get_hour_field

   @test
   subroutine test_get_minute_field
      type(time_fields) :: tf
      integer :: h = 13, m = 43, s = 37, ms = 100, tzo = 0
      tf = time_fields(h, m, s, ms, tzo)
      @assertTrue(tf % is_valid(), 'Failed to initialize tf')
      @assertEqual(m, tf % minute())
   end subroutine test_get_minute_field

   @test
   subroutine test_get_second_field
      type(time_fields) :: tf
      integer :: h = 13, m = 43, s = 37, ms = 100, tzo = 0
      tf = time_fields(h, m, s, ms, tzo)
      @assertTrue(tf % is_valid(), 'Failed to initialize tf')
      @assertEqual(s, tf % second())
   end subroutine test_get_second_field

   @test
   subroutine test_get_millisecond_field
      type(time_fields) :: tf
      integer :: h = 13, m = 43, s = 37, ms = 100, tzo = 0
      tf = time_fields(h, m, s, ms, tzo)
      @assertTrue(tf % is_valid(), 'Failed to initialize tf')
      @assertEqual(ms, tf % millisecond())
   end subroutine test_get_millisecond_field

   @test
   subroutine test_get_timezone_offset_field
      type(time_fields) :: tf
      integer :: h = 13, m = 43, s = 37, ms = 100, tzo = 0
      tf = time_fields(h, m, s, ms, tzo)
      @assertTrue(tf % is_valid(), 'Failed to initialize tf')
      @assertEqual(tzo, tf % timezone_offset())
   end subroutine test_get_timezone_offset_field

   @test
   subroutine test_are_valid_time_fields
      type(time_fields) :: tf
      tf = time_fields(13, 43, 37, 100, 0)
      @assertTrue(tf % is_valid(), 'tf should be valid.')
      tf = time_fields(13, 63, 37, 100, 0)
      @assertFalse(tf % is_valid(), 'tf should not be valid.')
   end subroutine test_are_valid_time_fields

!   @test
   subroutine test_convert_to_ISO8601DateTime()
      character(len=*), parameter :: cf_datetime_string_A = '2023-04-23 21:05:37'
      character(len=*), parameter :: expected_A = '2023-04-23T21:05:37'
      character(len=*), parameter :: cf_datetime_string_B= '2023-04-23 21:05:37.337'
      character(len=*), parameter :: expected_B = '2023-04-23T21:05:37.337'
      character(len=*), parameter :: undelimited_datetime_string_C = '20230423210537'
      character(len=*), parameter :: fail_D = '2023-4-23 21:05:37'
      character(len=*), parameter :: fail_E = '2023-04-23 21:5:37'
      character(len=:), allocatable :: output
      character(len=MAX_LEN) :: actual
      integer :: status

      call convert_to_ISO8601DateTime(cf_datetime_string_A, output, rc = status)
      @assertEqual(status, 0, 'Conversion A failed: ' // trim(cf_datetime_string_A))
      actual = trim(output)
      @assertEqual(expected_A, trim(actual), 'Datetime strings do not match.')

      call convert_to_ISO8601DateTime(cf_datetime_string_B, output, rc = status)
      @assertEqual(status, 0, 'Conversion B failed: ' // trim(cf_datetime_string_B))
      actual = trim(output)
      @assertEqual(expected_B, trim(actual), 'Datetime strings do not match.')

      call convert_to_ISO8601DateTime(undelimited_datetime_string_C, output, rc = status)
      @assertEqual(status, 0, 'Conversion C failed: ' // trim(undelimited_datetime_string_C))
      actual = trim(output)
      @assertEqual(expected_A, trim(actual), 'Datetime strings do not match.')

      call convert_to_ISO8601DateTime(fail_D, output, rc = status)
      @assertFalse(status == 0, 'Failed to catch illegal value - D')

      call convert_to_ISO8601DateTime(fail_E, output, rc = status)
      @assertFalse(status == 0, 'Failed to catch illegal value - E.')

   end subroutine test_convert_to_ISO8601DateTime

   @test
   subroutine test_construct_datetime_duration()
      integer, parameter :: IEX = UNSET_FIELD
      real(kind=R64), parameter :: REX = real(IEX, kind=R64)
      logical, parameter :: LEX = .FALSE.
      type(datetime_duration) :: d
      d = datetime_duration()
      @assertEqual(IEX, d % year, 'year should be unset')
      @assertEqual(IEX, d % month, 'month should be unset')
      @assertEqual(IEX, d % day, 'day should be unset')
      @assertEqual(IEX, d % hour, 'hour should be unset')
      @assertEqual(IEX, d % minute, 'minute should be unset')
      @assertEqual(IEX, d % second, 'second should be unset')
      @assertEqual(REX, d % hour_real, 'hour_real should be unset')
      @assertEqual(REX, d % minute_real, 'minute_real should be unset')
      @assertEqual(REX, d % second_real, 'second_real should be unset')
      @assertFalse(d % year_is_set(), 'year_is_set should be .FALSE.')
      @assertFalse(d % month_is_set(), 'month_is_set should be .FALSE.')
      @assertFalse(d % day_is_set(), 'hour_is_set should be .FALSE.')
      @assertFalse(d % hour_is_set(), 'hour_is_set should be .FALSE.')
      @assertFalse(d % minute_is_set(), 'minute_is_set should be .FALSE.')
      @assertFalse(d % second_is_set(), 'hour_is_set should be .FALSE.')
      @assertFalse(d % hour_is_real(), 'hour_is_real should be .FALSE.')
      @assertFalse(d % minute_is_real(), 'minute_is_real should be .FALSE.')
      @assertFalse(d % second_is_real(), 'hour_is_real should be .FALSE.')
   end subroutine test_construct_datetime_duration 

   subroutine test_set_year_datetime_duration()
      type(datetime_duration) :: d
      integer :: expected

      expected = 2001
      d = datetime_duration()
      call d % set_year(expected)
      @assertEqual(expected, d % year, 'Value not set correctly')

   end subroutine test_set_year_datetime_duration

   subroutine test_set_month_datetime_duration()
      type(datetime_duration) :: d
      integer :: expected

      expected = 3
      d = datetime_duration()
      call d % set_month(expected)
      @assertEqual(expected, d % month, 'Value not set correctly')

   end subroutine test_set_month_datetime_duration

   subroutine test_set_day_datetime_duration()
      type(datetime_duration) :: d
      integer :: expected

      expected = 20
      d = datetime_duration()
      call d % set_day(expected)
      @assertEqual(expected, d % day, 'Value not set correctly')

   end subroutine test_set_day_datetime_duration

   subroutine test_set_hour_datetime_duration()
      type(datetime_duration) :: d
      integer :: expected

      expected = 3
      d = datetime_duration()
      call d % set_hour(expected)
      @assertEqual(expected, d % hour, 'Value not set correctly')

   end subroutine test_set_hour_datetime_duration

   subroutine test_set_minute_datetime_duration()
      type(datetime_duration) :: d
      integer :: expected

      expected = 10
      d = datetime_duration()
      call d % set_minute(expected)
      @assertEqual(expected, d % minute, 'Value not set correctly')

   end subroutine test_set_minute_datetime_duration

   subroutine test_set_second_datetime_duration()
      type(datetime_duration) :: d
      integer :: expected

      expected = 1800
      d = datetime_duration()
      call d % set_second(expected)
      @assertEqual(expected, d % second, 'Value not set correctly')

   end subroutine test_set_second_datetime_duration

   subroutine test_set_hour_real_datetime_duration()
      type(datetime_duration) :: d
      real(R64) :: expected

      expected = 3.0
      d = datetime_duration()
      call d % set_hour_real(expected)
      @assertEqual(expected, d % hour_real, 'Value not set correctly')

   end subroutine test_set_hour_real_datetime_duration

   subroutine test_set_minute_real_datetime_duration()
      type(datetime_duration) :: d
      real(R64) :: expected

      expected = 10.0
      d = datetime_duration()
      call d % set_minute_real(expected)
      @assertEqual(expected, d % minute_real, 'Value not set correctly')

   end subroutine test_set_minute_real_datetime_duration

   subroutine test_set_second_real_datetime_duration()
      type(datetime_duration) :: d
      real(R64) :: expected

      expected = 1800.0
      d = datetime_duration()
      call d % set_second_real(expected)
      @assertEqual(expected, d % second_real, 'Value not set correctly')

   end subroutine test_set_second_real_datetime_duration

   subroutine test_set_real_value_datetime_duration()
      type(datetime_duration) :: d
      real(R64) :: expected

      expected = 1800.0
      d = datetime_duration()
      call d % set_value(SECOND_TIME_UNIT, expected)
      @assertEqual(expected, d % second_real, 'Value not set correctly')

   end subroutine test_set_real_value_datetime_duration

   subroutine test_set_integer_value_datetime_duration()
      type(datetime_duration) :: d
      integer :: expected

      expected = 1800
      d = datetime_duration()
      call d % set_value(SECOND_TIME_UNIT, expected)
      @assertEqual(expected, d % second, 'Value not set correctly')

   end subroutine test_set_integer_value_datetime_duration

   subroutine test_is_valid_datestring()
   end subroutine test_is_valid_datestring

   subroutine test_is_in_char_set()
      character(len=*), parameter :: DIGITS = '1234567890'
      character :: ch

      ch = '0'
      @assertTrue(is_in_char_set(ch, DIGITS), 'Character not found')
      ch = 'A'
      @assertFalse(is_in_char_set(ch, DIGITS), 'Character is not in set')

   end subroutine test_is_in_char_set

   subroutine test_find_delta()
   end subroutine test_find_delta

   subroutine test_find_delta_datestring()
   end subroutine test_find_delta_datestring

   subroutine test_split_digit_string_delimited()
   end subroutine test_split_digit_string_delimited

   subroutine test_valid_index()
   end subroutine test_valid_index

   subroutine test_split_digit_string_indexed()
   end subroutine test_split_digit_string_indexed

   subroutine test_convert_lengths_to_indices()
   end subroutine test_convert_lengths_to_indices

   @test
   subroutine test_get_time_unit()
      integer(kind(TIME_UNIT)) :: expected, actual
      character(len=8) :: unit_name

         unit_name = 'year'
         expected = YEAR_TIME_UNIT
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)
         unit_name = unit_name // 's'
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)

         unit_name = 'month'
         expected = MONTH_TIME_UNIT
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)
         unit_name = unit_name // 's'
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)

         unit_name = 'day'
         expected = DAY_TIME_UNIT
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)
         unit_name = unit_name // 's'
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)

         unit_name = 'hour'
         expected = HOUR_TIME_UNIT
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)
         unit_name = unit_name // 's'
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)

         unit_name = 'minute'
         expected = MINUTE_TIME_UNIT
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)
         unit_name = unit_name // 's'
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)

         unit_name = 'second'
         expected = SECOND_TIME_UNIT
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)
         unit_name = unit_name // 's'
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)
      
         unit_name = 'furlong'
         expected = UNKNOWN_TIME_UNIT
         actual = get_time_unit(unit_name)
         @assertEqual(expected, actual, 'Mismatch for ' // unit_name)

   end subroutine test_get_time_unit

end module test_MAPL_DateTime_Parsing