From 9548efd70d7e52d0bc58dee81d81a01758668dcc Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Fri, 12 Jan 2024 17:55:25 -0700 Subject: [PATCH 01/10] Add initial F90 API skeleton for ESMF_TimeInterval from a string. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index 1db5625ac9..5149729a27 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -928,7 +928,7 @@ module ESMF_TimeIntervalMod module procedure ESMF_TimeIntervalSetDurStart module procedure ESMF_TimeIntervalSetDurCal module procedure ESMF_TimeIntervalSetDurCalTyp - + module procedure ESMF_TimeIntervalSetString ! !DESCRIPTION: ! This interface provides a single entry point for {\tt ESMF\_TimeInterval} ! Set methods. @@ -2763,6 +2763,58 @@ subroutine ESMF_TimeIntervalSetDurCalTyp(timeinterval, calkindflag, & if (present(rc)) rc = ESMF_SUCCESS end subroutine ESMF_TimeIntervalSetDurCalTyp +!------------------------------------------------------------------------------ +#undef ESMF_METHOD +#define ESMF_METHOD "ESMF_TimeIntervalSetString()" +!BOP +! !IROUTINE: ESMF_TimeIntervalSet - Initialize or set a TimeInterval from ISO format string + +! !INTERFACE: + ! Private name; call using ESMF_TimeIntervalSet() + subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) + +! !ARGUMENTS: + type(ESMF_TimeInterval), intent(inout) :: timeinterval + character(*), intent(in) :: timeIntervalString + integer, intent(out), optional :: rc + +! +! +! !DESCRIPTION: +! Sets the value of the {\tt ESMF\_TimeInterval} using a user specified +! string in ISO duration format (P[n]Y[n]M[n]DT[n]H[n]M[n]S). +! +! The arguments are: +! \begin{description} +! \item[timeinterval] +! The object instance to initialize. +! \item[timeIntervalString] +! ISO format duration string. +! \item[{[rc]}] +! Return code; equals {\tt ESMF\_SUCCESS} if there are no errors. +! \end{description} +! +!EOP +! !REQUIREMENTS: +! TMGn.n.n + integer :: localrc ! local return code + + ! Assume failure until success + if (present(rc)) rc = ESMF_RC_NOT_IMPL + localrc = ESMF_RC_NOT_IMPL + + ! DEBUG: + write(*,*) "Duration string is:",timeIntervalString + + ! Parse string into values for each time unit + + + ! Set time interval using time unit values parsed above + + ! Return success + if (present(rc)) rc = ESMF_SUCCESS + end subroutine ESMF_TimeIntervalSetString + !------------------------------------------------------------------------------ #undef ESMF_METHOD #define ESMF_METHOD "ESMF_TimeIntervalValidate()" From 708d757a8b068bac49e76edce4d6ccef5d8fef4e Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Fri, 12 Jan 2024 18:36:38 -0700 Subject: [PATCH 02/10] Adding initial unit test for setting an ESMF_TimeInterval from an ISO duration string. --- .../TimeMgr/tests/ESMF_TimeIntervalUTest.F90 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 index f5ef22283c..c27759b5ab 100644 --- a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 +++ b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 @@ -3894,6 +3894,16 @@ program ESMF_TimeIntervalUTest name, failMsg, result, ESMF_SRCLINE) !print *, "S, sN, sD_i8 = ", S, sN, sD_i8 + ! ---------------------------------------------------------------------------- + !EX_UTest + ! Testing ESMF_TimeIntervalSet from an ISO duration string + write(name, *) "Set an ESMF_TimeInterval using an ISO duration string." + write(failMsg, *) "Output did not match duration set in string." + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y2M3DT4H5M6S", rc=rc) + !call ESMF_TimeIntervalGet(timeInterval1, s=S, sN=sN, sD=sD, rc=rc) + call ESMF_Test((rc .eq. ESMF_SUCCESS), & + name, failMsg, result, ESMF_SRCLINE) + ! ---------------------------------------------------------------------------- ! return number of failures to environment; 0 = success (all pass) ! return result ! TODO: no way to do this in F90 ? From b04c4e527895a36abf11c3aed42d3d9d9a96bab2 Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Thu, 18 Jan 2024 11:04:55 -0700 Subject: [PATCH 03/10] Add skeleton of duration string parsing subroutine. Connect that and time interval creation together with possible time quantity variables. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index 5149729a27..a5afe3df8c 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -2763,6 +2763,30 @@ subroutine ESMF_TimeIntervalSetDurCalTyp(timeinterval, calkindflag, & if (present(rc)) rc = ESMF_SUCCESS end subroutine ESMF_TimeIntervalSetDurCalTyp + +! Internal subroutine to parse an ISO duration string and return +! the corresponding numeric time values +subroutine ParseDurationString(timeintervalString, & + yy_i8, mm_i8, d_i8, s_i8, & + h_r8, m_r8, s_r8, rc) + + character(*), intent(in) :: timeIntervalString + integer(ESMF_KIND_I8), intent(in), optional :: yy_i8 + integer(ESMF_KIND_I8), intent(in), optional :: mm_i8 + integer(ESMF_KIND_I8), intent(in), optional :: d_i8 + integer(ESMF_KIND_I8), intent(in), optional :: s_i8 + real(ESMF_KIND_R8), intent(in), optional :: h_r8 + real(ESMF_KIND_R8), intent(in), optional :: m_r8 + real(ESMF_KIND_R8), intent(in), optional :: s_r8 + integer, intent(out), optional :: rc + + + ! Return success + if (present(rc)) rc = ESMF_SUCCESS + +end subroutine ParseDurationString + + !------------------------------------------------------------------------------ #undef ESMF_METHOD #define ESMF_METHOD "ESMF_TimeIntervalSetString()" @@ -2798,7 +2822,15 @@ subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) ! !REQUIREMENTS: ! TMGn.n.n integer :: localrc ! local return code - + integer(ESMF_KIND_I8) :: yy_i8 + integer(ESMF_KIND_I8) :: mm_i8 + integer(ESMF_KIND_I8) :: d_i8 + integer(ESMF_KIND_I8) :: s_i8 + real(ESMF_KIND_R8) :: d_r8 + real(ESMF_KIND_R8) :: h_r8 + real(ESMF_KIND_R8) :: m_r8 + real(ESMF_KIND_R8) :: s_r8 + ! Assume failure until success if (present(rc)) rc = ESMF_RC_NOT_IMPL localrc = ESMF_RC_NOT_IMPL @@ -2807,10 +2839,32 @@ subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) write(*,*) "Duration string is:",timeIntervalString ! Parse string into values for each time unit - - + call ParseDurationString(timeintervalString, & + yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, s_i8=s_i8, & + h_r8=h_r8, m_r8=m_r8, s_r8=s_r8, rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + ! Set time interval using time unit values parsed above + ! NOTE: Just use I8 for integer values, since it looks like integer values + ! are stored that way anyway. Also, for times (h,m,s), it looks like both R8 + ! and I8 are added together, so you can just + ! use both and whichever isn't needed set to 0. + ! It's unclear if that's true for days, so don't do it for that. + call ESMF_TimeIntervalSetDur(timeinterval, & + yy_i8=yy_i8, & + mm_i8=mm_i8, & + d_i8=d_i8, & + s_i8=s_i8, & + ! d_r8=d_r8, & ! Not supported right now + h_r8=h_r8, & + m_r8=m_r8, & + s_r8=s_r8, & + rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + ! Return success if (present(rc)) rc = ESMF_SUCCESS end subroutine ESMF_TimeIntervalSetString From 4b444af7a681f14bb4cbb3d971c8058da6014486 Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Tue, 23 Jan 2024 12:10:26 -0700 Subject: [PATCH 04/10] Added date parsing. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 122 ++++++++++++++++-- 1 file changed, 110 insertions(+), 12 deletions(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index a5afe3df8c..4f36ac04df 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -2764,27 +2764,125 @@ subroutine ESMF_TimeIntervalSetDurCalTyp(timeinterval, calkindflag, & end subroutine ESMF_TimeIntervalSetDurCalTyp +!------------------------------------------------------------------------------ +#undef ESMF_METHOD +#define ESMF_METHOD "ESMF_ParseDurationString()" + ! Internal subroutine to parse an ISO duration string and return ! the corresponding numeric time values -subroutine ParseDurationString(timeintervalString, & +subroutine ESMF_ParseDurationString(timeintervalString, & yy_i8, mm_i8, d_i8, s_i8, & h_r8, m_r8, s_r8, rc) - character(*), intent(in) :: timeIntervalString - integer(ESMF_KIND_I8), intent(in), optional :: yy_i8 - integer(ESMF_KIND_I8), intent(in), optional :: mm_i8 - integer(ESMF_KIND_I8), intent(in), optional :: d_i8 - integer(ESMF_KIND_I8), intent(in), optional :: s_i8 - real(ESMF_KIND_R8), intent(in), optional :: h_r8 - real(ESMF_KIND_R8), intent(in), optional :: m_r8 - real(ESMF_KIND_R8), intent(in), optional :: s_r8 + character(*), intent(in) :: timeIntervalString + integer(ESMF_KIND_I8), intent(out) :: yy_i8 + integer(ESMF_KIND_I8), intent(out) :: mm_i8 + integer(ESMF_KIND_I8), intent(out) :: d_i8 + integer(ESMF_KIND_I8), intent(out) :: s_i8 + real(ESMF_KIND_R8), intent(out) :: h_r8 + real(ESMF_KIND_R8), intent(out) :: m_r8 + real(ESMF_KIND_R8), intent(out) :: s_r8 integer, intent(out), optional :: rc + integer :: localrc + integer :: beg_loc, end_loc + integer :: t_loc ! Return success if (present(rc)) rc = ESMF_SUCCESS - -end subroutine ParseDurationString + + ! See if T is there and if so where + t_loc=0 + t_loc=INDEX(timeIntervalString,"T") + + !! TODO: Break this into two subroutine one handling dates the other handling times. That'll be an easier way to deal with the M thing + + ! Make sure P is there and find beginning of string + beg_loc=INDEX(timeIntervalString,"P") + + ! Complain if it doesn't start with P + if (beg_loc < 1) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + msg=" ISO 8601 duration strings need to begin with: P", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + + ! Advance to slot after P + beg_loc=beg_loc+1 + + ! Look for Y (year), and if it exists process it + end_loc=INDEX(timeIntervalString,"Y") + if (end_loc > 0) then + ! Shift position before Y for end loc + end_loc=end_loc-1 + + ! Make sure that it isn't empty + if (end_loc < beg_loc) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + msg=" Y value missing in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + + ! Read year value + read(timeIntervalString(beg_loc:end_loc), *) yy_i8 + + ! New beg_loc is after indicator + beg_loc=end_loc+2 + endif + + ! Look for M (month), and if it exists process it + end_loc=INDEX(timeIntervalString,"M") + if (end_loc > 0) then + ! Shift position before M for end loc + end_loc=end_loc-1 + + ! Make sure that it isn't empty + if (end_loc < beg_loc) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + msg=" M value missing in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + + ! Read year value + read(timeIntervalString(beg_loc:end_loc), *) mm_i8 + + ! New beg_loc is after indicator + beg_loc=end_loc+2 + endif + + ! Look for D (days), and if it exists process it + end_loc=INDEX(timeIntervalString,"D") + if (end_loc > 0) then + ! Shift position before M for end loc + end_loc=end_loc-1 + + ! Make sure that it isn't empty + if (end_loc < beg_loc) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + msg=" D value missing in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + + ! Read year value + read(timeIntervalString(beg_loc:end_loc), *) d_i8 + + ! New beg_loc is after indicator + beg_loc=end_loc+2 + endif + + + write(*,*) "Year value=",yy_i8 + write(*,*) "Month value=",mm_i8 + write(*,*) "Days value=",d_i8 + + + + +end subroutine ESMF_ParseDurationString !------------------------------------------------------------------------------ @@ -2839,7 +2937,7 @@ subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) write(*,*) "Duration string is:",timeIntervalString ! Parse string into values for each time unit - call ParseDurationString(timeintervalString, & + call ESMF_ParseDurationString(timeintervalString, & yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, s_i8=s_i8, & h_r8=h_r8, m_r8=m_r8, s_r8=s_r8, rc=localrc) if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & From 65b39eed5f99b7f9c691697160fa8c51ad42e47b Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Wed, 24 Jan 2024 15:47:58 -0700 Subject: [PATCH 05/10] Break parsing into two parts to deal with the repeated us of M. Add time string parsing. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 224 +++++++++++++++--- 1 file changed, 196 insertions(+), 28 deletions(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index 4f36ac04df..a229297a31 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -2764,52 +2764,139 @@ subroutine ESMF_TimeIntervalSetDurCalTyp(timeinterval, calkindflag, & end subroutine ESMF_TimeIntervalSetDurCalTyp -!------------------------------------------------------------------------------ + !------------------------------------------------------------------------------ + #undef ESMF_METHOD -#define ESMF_METHOD "ESMF_ParseDurationString()" +#define ESMF_METHOD "ESMF_ParseDurTimeString()" -! Internal subroutine to parse an ISO duration string and return +! Internal subroutine to parse the time part of an +! ISO duration string and return ! the corresponding numeric time values -subroutine ESMF_ParseDurationString(timeintervalString, & - yy_i8, mm_i8, d_i8, s_i8, & - h_r8, m_r8, s_r8, rc) +subroutine ESMF_ParseDurTimeString(timeintervalString, & + h_r8, m_r8, s_r8, s_i8, rc) character(*), intent(in) :: timeIntervalString - integer(ESMF_KIND_I8), intent(out) :: yy_i8 - integer(ESMF_KIND_I8), intent(out) :: mm_i8 - integer(ESMF_KIND_I8), intent(out) :: d_i8 - integer(ESMF_KIND_I8), intent(out) :: s_i8 real(ESMF_KIND_R8), intent(out) :: h_r8 real(ESMF_KIND_R8), intent(out) :: m_r8 real(ESMF_KIND_R8), intent(out) :: s_r8 + integer(ESMF_KIND_I8), intent(out) :: s_i8 integer, intent(out), optional :: rc integer :: localrc integer :: beg_loc, end_loc integer :: t_loc + ! Init output to 0 + h_r8=0 + m_r8=0 + s_r8=0 + s_i8=0 + + ! Start at the beginning of the string + beg_loc=1 + + ! Look for H (hours), and if it exists process it + end_loc=INDEX(timeIntervalString,"H") + if (end_loc > 0) then + ! Shift position before Y for end loc + end_loc=end_loc-1 + + ! Make sure that it isn't empty + if (end_loc < beg_loc) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + msg=" H value missing in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + + ! Read year value + read(timeIntervalString(beg_loc:end_loc), *) h_r8 + + ! New beg_loc is after indicator + beg_loc=end_loc+2 + endif + + ! Look for M (minutes), and if it exists process it + end_loc=INDEX(timeIntervalString,"M") + if (end_loc > 0) then + ! Shift position before M for end loc + end_loc=end_loc-1 + + ! Make sure that it isn't empty + if (end_loc < beg_loc) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + msg=" M value missing in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + + ! Read year value + read(timeIntervalString(beg_loc:end_loc), *) m_r8 + + ! New beg_loc is after indicator + beg_loc=end_loc+2 + endif + + ! Look for S (seconds), and if it exists process it + end_loc=INDEX(timeIntervalString,"S") + if (end_loc > 0) then + ! Shift position before M for end loc + end_loc=end_loc-1 + + ! Make sure that it isn't empty + if (end_loc < beg_loc) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + msg=" S value missing in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + + ! Read year value + read(timeIntervalString(beg_loc:end_loc), *) s_i8 + + ! New beg_loc is after indicator + beg_loc=end_loc+2 + endif + + write(*,*) "Hour value=",h_r8 + write(*,*) "Minute value=",m_r8 + write(*,*) "Seconds value=",s_r8 + write(*,*) "Seconds value=",s_i8 + ! Return success - if (present(rc)) rc = ESMF_SUCCESS + if (present(rc)) rc = ESMF_SUCCESS + +end subroutine ESMF_ParseDurTimeString - ! See if T is there and if so where - t_loc=0 - t_loc=INDEX(timeIntervalString,"T") - !! TODO: Break this into two subroutine one handling dates the other handling times. That'll be an easier way to deal with the M thing + +!------------------------------------------------------------------------------ +#undef ESMF_METHOD +#define ESMF_METHOD "ESMF_ParseDurDateString()" - ! Make sure P is there and find beginning of string - beg_loc=INDEX(timeIntervalString,"P") +! Internal subroutine to parse the date part of an +! ISO duration string and return +! the corresponding numeric time values +subroutine ESMF_ParseDurDateString(timeintervalString, & + yy_i8, mm_i8, d_i8, rc) - ! Complain if it doesn't start with P - if (beg_loc < 1) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & - msg=" ISO 8601 duration strings need to begin with: P", & - ESMF_CONTEXT, rcToReturn=rc) - return - endif + character(*), intent(in) :: timeIntervalString + integer(ESMF_KIND_I8), intent(out) :: yy_i8 + integer(ESMF_KIND_I8), intent(out) :: mm_i8 + integer(ESMF_KIND_I8), intent(out) :: d_i8 + integer, intent(out), optional :: rc - ! Advance to slot after P - beg_loc=beg_loc+1 + integer :: localrc + integer :: beg_loc, end_loc + integer :: t_loc + + ! Init output to 0 + yy_i8=0 + mm_i8=0 + d_i8=0 + + ! Start at the beginning of the string + beg_loc=1 ! Look for Y (year), and if it exists process it end_loc=INDEX(timeIntervalString,"Y") @@ -2873,14 +2960,95 @@ subroutine ESMF_ParseDurationString(timeintervalString, & ! New beg_loc is after indicator beg_loc=end_loc+2 endif - write(*,*) "Year value=",yy_i8 write(*,*) "Month value=",mm_i8 write(*,*) "Days value=",d_i8 + ! Return success + if (present(rc)) rc = ESMF_SUCCESS + +end subroutine ESMF_ParseDurDateString - + + +!------------------------------------------------------------------------------ +#undef ESMF_METHOD +#define ESMF_METHOD "ESMF_ParseDurationString()" + +! Internal subroutine to parse an ISO duration string and return +! the corresponding numeric time values +subroutine ESMF_ParseDurationString(timeintervalString, & + yy_i8, mm_i8, d_i8, s_i8, & + h_r8, m_r8, s_r8, rc) + + character(*), intent(in) :: timeIntervalString + integer(ESMF_KIND_I8), intent(out) :: yy_i8 + integer(ESMF_KIND_I8), intent(out) :: mm_i8 + integer(ESMF_KIND_I8), intent(out) :: d_i8 + integer(ESMF_KIND_I8), intent(out) :: s_i8 + real(ESMF_KIND_R8), intent(out) :: h_r8 + real(ESMF_KIND_R8), intent(out) :: m_r8 + real(ESMF_KIND_R8), intent(out) :: s_r8 + integer, intent(out), optional :: rc + + integer :: localrc + integer :: beg_loc, end_loc + integer :: t_loc + + ! Make sure P is there and find beginning of string + beg_loc=INDEX(timeIntervalString,"P") + + ! Complain if it doesn't start with P + if (beg_loc < 1) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + msg=" ISO 8601 duration strings need to begin with: P", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + + ! Advance to slot after P + beg_loc=beg_loc+1 + + ! See if T is there and if so where + t_loc=0 + t_loc=INDEX(timeIntervalString,"T") + + ! Figure out end_loc + if (t_loc == 0) then + ! No times, so end is the end of the string + end_loc=LEN(timeIntervalString) + else + ! There are times so end is right before t + end_loc=t_loc-1 + endif + + ! Parse just the date part of the string + call ESMF_ParseDurDateString(timeintervalString(beg_loc:end_loc), & + yy_i8, mm_i8, d_i8, rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + + ! If there are times, then parse those + if (t_loc > 0) then + + ! Begin is after t + beg_loc=t_loc+1 + + ! End is end of string + end_loc=LEN(timeIntervalString) + + ! Parse just the date part of the string + call ESMF_ParseDurTimeString(timeintervalString(beg_loc:end_loc), & + h_r8, m_r8, s_r8, s_i8, rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + + endif + + + ! Return success + if (present(rc)) rc = ESMF_SUCCESS end subroutine ESMF_ParseDurationString From c65e62f930bd6de04432f0301e93a2d4eb9c60f2 Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Thu, 25 Jan 2024 14:50:03 -0700 Subject: [PATCH 06/10] Add error checking of duration string value readng. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 105 +++++++++++++----- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index a229297a31..993b7a18a0 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -2785,6 +2785,7 @@ subroutine ESMF_ParseDurTimeString(timeintervalString, & integer :: localrc integer :: beg_loc, end_loc integer :: t_loc + integer :: ioStatus ! Init output to 0 h_r8=0 @@ -2803,15 +2804,21 @@ subroutine ESMF_ParseDurTimeString(timeintervalString, & ! Make sure that it isn't empty if (end_loc < beg_loc) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" H value missing in ISO duration string.", & ESMF_CONTEXT, rcToReturn=rc) return endif ! Read year value - read(timeIntervalString(beg_loc:end_loc), *) h_r8 - + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) h_r8 + if (ioStatus /=0) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & + msg=" An error occurred while reading H value in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + ! New beg_loc is after indicator beg_loc=end_loc+2 endif @@ -2824,15 +2831,22 @@ subroutine ESMF_ParseDurTimeString(timeintervalString, & ! Make sure that it isn't empty if (end_loc < beg_loc) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" M value missing in ISO duration string.", & ESMF_CONTEXT, rcToReturn=rc) return endif ! Read year value - read(timeIntervalString(beg_loc:end_loc), *) m_r8 + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) m_r8 + if (ioStatus /=0) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & + msg=" An error occurred while reading M value in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + ! New beg_loc is after indicator beg_loc=end_loc+2 endif @@ -2845,14 +2859,21 @@ subroutine ESMF_ParseDurTimeString(timeintervalString, & ! Make sure that it isn't empty if (end_loc < beg_loc) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" S value missing in ISO duration string.", & ESMF_CONTEXT, rcToReturn=rc) return endif ! Read year value - read(timeIntervalString(beg_loc:end_loc), *) s_i8 + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) s_i8 + if (ioStatus /=0) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & + msg=" An error occurred while reading S value in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + ! New beg_loc is after indicator beg_loc=end_loc+2 @@ -2906,15 +2927,22 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & ! Make sure that it isn't empty if (end_loc < beg_loc) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" Y value missing in ISO duration string.", & ESMF_CONTEXT, rcToReturn=rc) return endif ! Read year value - read(timeIntervalString(beg_loc:end_loc), *) yy_i8 + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) yy_i8 + if (ioStatus /=0) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & + msg=" An error occurred while reading Y value in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + ! New beg_loc is after indicator beg_loc=end_loc+2 endif @@ -2927,15 +2955,22 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & ! Make sure that it isn't empty if (end_loc < beg_loc) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" M value missing in ISO duration string.", & ESMF_CONTEXT, rcToReturn=rc) return endif ! Read year value - read(timeIntervalString(beg_loc:end_loc), *) mm_i8 + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) mm_i8 + if (ioStatus /=0) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & + msg=" An error occurred while reading M value in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + ! New beg_loc is after indicator beg_loc=end_loc+2 endif @@ -2948,15 +2983,22 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & ! Make sure that it isn't empty if (end_loc < beg_loc) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" D value missing in ISO duration string.", & ESMF_CONTEXT, rcToReturn=rc) return endif ! Read year value - read(timeIntervalString(beg_loc:end_loc), *) d_i8 + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) d_i8 + if (ioStatus /=0) then + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & + msg=" An error occurred while reading D value in ISO duration string.", & + ESMF_CONTEXT, rcToReturn=rc) + return + endif + ! New beg_loc is after indicator beg_loc=end_loc+2 endif @@ -2974,11 +3016,11 @@ end subroutine ESMF_ParseDurDateString !------------------------------------------------------------------------------ #undef ESMF_METHOD -#define ESMF_METHOD "ESMF_ParseDurationString()" +#define ESMF_METHOD "ESMF_ParseDurString()" ! Internal subroutine to parse an ISO duration string and return ! the corresponding numeric time values -subroutine ESMF_ParseDurationString(timeintervalString, & +subroutine ESMF_ParseDurString(timeintervalString, & yy_i8, mm_i8, d_i8, s_i8, & h_r8, m_r8, s_r8, rc) @@ -3001,7 +3043,7 @@ subroutine ESMF_ParseDurationString(timeintervalString, & ! Complain if it doesn't start with P if (beg_loc < 1) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_WRONG, & + call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" ISO 8601 duration strings need to begin with: P", & ESMF_CONTEXT, rcToReturn=rc) return @@ -3022,13 +3064,17 @@ subroutine ESMF_ParseDurationString(timeintervalString, & ! There are times so end is right before t end_loc=t_loc-1 endif + - ! Parse just the date part of the string - call ESMF_ParseDurDateString(timeintervalString(beg_loc:end_loc), & - yy_i8, mm_i8, d_i8, rc=localrc) - if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & - ESMF_CONTEXT, rcToReturn=rc)) return + ! If not empty, parse just the date part of the string + if (beg_loc <= end_loc) then + call ESMF_ParseDurDateString(timeintervalString(beg_loc:end_loc), & + yy_i8, mm_i8, d_i8, rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + endif + ! If there are times, then parse those if (t_loc > 0) then @@ -3038,19 +3084,20 @@ subroutine ESMF_ParseDurationString(timeintervalString, & ! End is end of string end_loc=LEN(timeIntervalString) - ! Parse just the date part of the string - call ESMF_ParseDurTimeString(timeintervalString(beg_loc:end_loc), & - h_r8, m_r8, s_r8, s_i8, rc=localrc) - if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & - ESMF_CONTEXT, rcToReturn=rc)) return - + ! If not empty, parse just the time part of the string + if (beg_loc <= end_loc) then + call ESMF_ParseDurTimeString(timeintervalString(beg_loc:end_loc), & + h_r8, m_r8, s_r8, s_i8, rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + endif endif ! Return success if (present(rc)) rc = ESMF_SUCCESS -end subroutine ESMF_ParseDurationString +end subroutine ESMF_ParseDurString !------------------------------------------------------------------------------ @@ -3105,7 +3152,7 @@ subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) write(*,*) "Duration string is:",timeIntervalString ! Parse string into values for each time unit - call ESMF_ParseDurationString(timeintervalString, & + call ESMF_ParseDurString(timeintervalString, & yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, s_i8=s_i8, & h_r8=h_r8, m_r8=m_r8, s_r8=s_r8, rc=localrc) if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & From 3f43fa52aa21c562e4736032ef9aae4c08384bf7 Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Thu, 25 Jan 2024 17:28:57 -0700 Subject: [PATCH 07/10] Allow seconds to be either real or integer in string. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 11 ++++++++--- .../TimeMgr/tests/ESMF_TimeIntervalUTest.F90 | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index 993b7a18a0..1935700e55 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -2865,8 +2865,12 @@ subroutine ESMF_ParseDurTimeString(timeintervalString, & return endif - ! Read year value - read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) s_i8 + ! Read second value depending on if it looks like an integer or a real + if (VERIFY(timeIntervalString(beg_loc:end_loc),"+-0123456789") == 0) then + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) s_i8 + else + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) s_r8 + endif if (ioStatus /=0) then call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" An error occurred while reading S value in ISO duration string.", & @@ -2910,7 +2914,8 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & integer :: localrc integer :: beg_loc, end_loc integer :: t_loc - + integer :: ioStatus + ! Init output to 0 yy_i8=0 mm_i8=0 diff --git a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 index c27759b5ab..ee46709a81 100644 --- a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 +++ b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 @@ -3899,7 +3899,7 @@ program ESMF_TimeIntervalUTest ! Testing ESMF_TimeIntervalSet from an ISO duration string write(name, *) "Set an ESMF_TimeInterval using an ISO duration string." write(failMsg, *) "Output did not match duration set in string." - call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y2M3DT4H5M6S", rc=rc) + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y2M3DT4H5M6.1S", rc=rc) !call ESMF_TimeIntervalGet(timeInterval1, s=S, sN=sN, sD=sD, rc=rc) call ESMF_Test((rc .eq. ESMF_SUCCESS), & name, failMsg, result, ESMF_SRCLINE) From 45d16f40808483e8935d81b069a9938c3f7e0748 Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Fri, 26 Jan 2024 14:42:23 -0700 Subject: [PATCH 08/10] Allow days to either be I8 or real in duration string. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 47 ++++++++++++------- .../TimeMgr/tests/ESMF_TimeIntervalUTest.F90 | 2 +- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index 1935700e55..f403609c81 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -2773,13 +2773,13 @@ end subroutine ESMF_TimeIntervalSetDurCalTyp ! ISO duration string and return ! the corresponding numeric time values subroutine ESMF_ParseDurTimeString(timeintervalString, & - h_r8, m_r8, s_r8, s_i8, rc) + h_r8, m_r8, s_i8, s_r8, rc) character(*), intent(in) :: timeIntervalString real(ESMF_KIND_R8), intent(out) :: h_r8 real(ESMF_KIND_R8), intent(out) :: m_r8 + integer(ESMF_KIND_I8), intent(out) :: s_i8 real(ESMF_KIND_R8), intent(out) :: s_r8 - integer(ESMF_KIND_I8), intent(out) :: s_i8 integer, intent(out), optional :: rc integer :: localrc @@ -2788,15 +2788,16 @@ subroutine ESMF_ParseDurTimeString(timeintervalString, & integer :: ioStatus ! Init output to 0 - h_r8=0 - m_r8=0 - s_r8=0 + h_r8=0.0 + m_r8=0.0 + s_r8=0.0 s_i8=0 ! Start at the beginning of the string beg_loc=1 ! Look for H (hours), and if it exists process it + ! Use R8 for both real and integer, since R8 can exactly represent I4 end_loc=INDEX(timeIntervalString,"H") if (end_loc > 0) then ! Shift position before Y for end loc @@ -2824,6 +2825,7 @@ subroutine ESMF_ParseDurTimeString(timeintervalString, & endif ! Look for M (minutes), and if it exists process it + ! Use R8 for both real and integer, since R8 can exactly represent I4 end_loc=INDEX(timeIntervalString,"M") if (end_loc > 0) then ! Shift position before M for end loc @@ -2903,12 +2905,13 @@ end subroutine ESMF_ParseDurTimeString ! ISO duration string and return ! the corresponding numeric time values subroutine ESMF_ParseDurDateString(timeintervalString, & - yy_i8, mm_i8, d_i8, rc) + yy_i8, mm_i8, d_i8, d_r8, rc) character(*), intent(in) :: timeIntervalString integer(ESMF_KIND_I8), intent(out) :: yy_i8 integer(ESMF_KIND_I8), intent(out) :: mm_i8 integer(ESMF_KIND_I8), intent(out) :: d_i8 + real(ESMF_KIND_R8), intent(out) :: d_r8 integer, intent(out), optional :: rc integer :: localrc @@ -2920,6 +2923,7 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & yy_i8=0 mm_i8=0 d_i8=0 + d_r8=0.0 ! Start at the beginning of the string beg_loc=1 @@ -2932,7 +2936,7 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & ! Make sure that it isn't empty if (end_loc < beg_loc) then - call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & + Call Esmf_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" Y value missing in ISO duration string.", & ESMF_CONTEXT, rcToReturn=rc) return @@ -2994,8 +2998,12 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & return endif - ! Read year value - read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) d_i8 + ! Read day value depending on if it looks like an integer or a real + if (VERIFY(timeIntervalString(beg_loc:end_loc),"+-0123456789") == 0) then + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) d_i8 + else + read(timeIntervalString(beg_loc:end_loc), *, ioStat=ioStatus) d_r8 + endif if (ioStatus /=0) then call ESMF_LogSetError(rcToCheck=ESMF_RC_ARG_VALUE, & msg=" An error occurred while reading D value in ISO duration string.", & @@ -3011,6 +3019,7 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & write(*,*) "Year value=",yy_i8 write(*,*) "Month value=",mm_i8 write(*,*) "Days value=",d_i8 + write(*,*) "Days value=",d_r8 ! Return success if (present(rc)) rc = ESMF_SUCCESS @@ -3026,16 +3035,17 @@ end subroutine ESMF_ParseDurDateString ! Internal subroutine to parse an ISO duration string and return ! the corresponding numeric time values subroutine ESMF_ParseDurString(timeintervalString, & - yy_i8, mm_i8, d_i8, s_i8, & - h_r8, m_r8, s_r8, rc) + yy_i8, mm_i8, d_i8, d_r8, & + h_r8, m_r8, s_i8, s_r8, rc) character(*), intent(in) :: timeIntervalString integer(ESMF_KIND_I8), intent(out) :: yy_i8 integer(ESMF_KIND_I8), intent(out) :: mm_i8 integer(ESMF_KIND_I8), intent(out) :: d_i8 - integer(ESMF_KIND_I8), intent(out) :: s_i8 + real(ESMF_KIND_R8), intent(out) :: d_r8 real(ESMF_KIND_R8), intent(out) :: h_r8 real(ESMF_KIND_R8), intent(out) :: m_r8 + integer(ESMF_KIND_I8), intent(out) :: s_i8 real(ESMF_KIND_R8), intent(out) :: s_r8 integer, intent(out), optional :: rc @@ -3074,7 +3084,7 @@ subroutine ESMF_ParseDurString(timeintervalString, & ! If not empty, parse just the date part of the string if (beg_loc <= end_loc) then call ESMF_ParseDurDateString(timeintervalString(beg_loc:end_loc), & - yy_i8, mm_i8, d_i8, rc=localrc) + yy_i8, mm_i8, d_i8, d_r8, rc=localrc) if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & ESMF_CONTEXT, rcToReturn=rc)) return endif @@ -3092,7 +3102,7 @@ subroutine ESMF_ParseDurString(timeintervalString, & ! If not empty, parse just the time part of the string if (beg_loc <= end_loc) then call ESMF_ParseDurTimeString(timeintervalString(beg_loc:end_loc), & - h_r8, m_r8, s_r8, s_i8, rc=localrc) + h_r8, m_r8, s_i8, s_r8, rc=localrc) if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & ESMF_CONTEXT, rcToReturn=rc)) return endif @@ -3158,8 +3168,8 @@ subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) ! Parse string into values for each time unit call ESMF_ParseDurString(timeintervalString, & - yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, s_i8=s_i8, & - h_r8=h_r8, m_r8=m_r8, s_r8=s_r8, rc=localrc) + yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, d_r8=d_r8, & + h_r8=h_r8, m_r8=m_r8, s_i8=s_i8, s_r8=s_r8, rc=localrc) if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & ESMF_CONTEXT, rcToReturn=rc)) return @@ -3169,13 +3179,14 @@ subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) ! are stored that way anyway. Also, for times (h,m,s), it looks like both R8 ! and I8 are added together, so you can just ! use both and whichever isn't needed set to 0. - ! It's unclear if that's true for days, so don't do it for that. + ! An R8 can exactly represent an I4, so just use R8 for hours and minutes + ! where an I4 is all that's available. call ESMF_TimeIntervalSetDur(timeinterval, & yy_i8=yy_i8, & mm_i8=mm_i8, & d_i8=d_i8, & s_i8=s_i8, & - ! d_r8=d_r8, & ! Not supported right now + d_r8=d_r8, & h_r8=h_r8, & m_r8=m_r8, & s_r8=s_r8, & diff --git a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 index ee46709a81..f57f72a2ce 100644 --- a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 +++ b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 @@ -3899,7 +3899,7 @@ program ESMF_TimeIntervalUTest ! Testing ESMF_TimeIntervalSet from an ISO duration string write(name, *) "Set an ESMF_TimeInterval using an ISO duration string." write(failMsg, *) "Output did not match duration set in string." - call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y2M3DT4H5M6.1S", rc=rc) + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y2M3.3DT4H5M6S", rc=rc) !call ESMF_TimeIntervalGet(timeInterval1, s=S, sN=sN, sD=sD, rc=rc) call ESMF_Test((rc .eq. ESMF_SUCCESS), & name, failMsg, result, ESMF_SRCLINE) From f4a258833e5ad59ae7466beb8a2c347e218f15c1 Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Fri, 26 Jan 2024 16:38:31 -0700 Subject: [PATCH 09/10] Add more unit tests for ESMF_TimeInterval from ISO duration string. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 36 +++-- .../TimeMgr/tests/ESMF_TimeIntervalUTest.F90 | 139 +++++++++++++++++- 2 files changed, 158 insertions(+), 17 deletions(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index f403609c81..9c75f82171 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -2884,11 +2884,12 @@ subroutine ESMF_ParseDurTimeString(timeintervalString, & ! New beg_loc is after indicator beg_loc=end_loc+2 endif - - write(*,*) "Hour value=",h_r8 - write(*,*) "Minute value=",m_r8 - write(*,*) "Seconds value=",s_r8 - write(*,*) "Seconds value=",s_i8 + + ! DEBUG OUTPUT + ! write(*,*) "Hour value=",h_r8 + ! write(*,*) "Minute value=",m_r8 + ! write(*,*) "Seconds value=",s_r8 + ! write(*,*) "Seconds value=",s_i8 ! Return success if (present(rc)) rc = ESMF_SUCCESS @@ -3015,11 +3016,12 @@ subroutine ESMF_ParseDurDateString(timeintervalString, & ! New beg_loc is after indicator beg_loc=end_loc+2 endif - - write(*,*) "Year value=",yy_i8 - write(*,*) "Month value=",mm_i8 - write(*,*) "Days value=",d_i8 - write(*,*) "Days value=",d_r8 + + ! DEBUG OUTPUT + ! write(*,*) "Year value=",yy_i8 + ! write(*,*) "Month value=",mm_i8 + ! write(*,*) "Days value (I8)=",d_i8 + ! write(*,*) "Days value (R8)=",d_r8 ! Return success if (present(rc)) rc = ESMF_SUCCESS @@ -3053,6 +3055,16 @@ subroutine ESMF_ParseDurString(timeintervalString, & integer :: beg_loc, end_loc integer :: t_loc + ! Init output to 0 + yy_i8=0 + mm_i8=0 + d_i8=0 + d_r8=0.0 + h_r8=0.0 + m_r8=0.0 + s_r8=0.0 + s_i8=0 + ! Make sure P is there and find beginning of string beg_loc=INDEX(timeIntervalString,"P") @@ -3163,8 +3175,8 @@ subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) if (present(rc)) rc = ESMF_RC_NOT_IMPL localrc = ESMF_RC_NOT_IMPL - ! DEBUG: - write(*,*) "Duration string is:",timeIntervalString + ! DEBUG OUTPUT: + !write(*,*) "Duration string is:",timeIntervalString ! Parse string into values for each time unit call ESMF_ParseDurString(timeintervalString, & diff --git a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 index f57f72a2ce..b413ebb75b 100644 --- a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 +++ b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 @@ -79,9 +79,11 @@ program ESMF_TimeIntervalUTest ms_out_r8, ms_in_r8, min_out_r8, hr_in_r8, & sec_out_r8 integer(ESMF_KIND_I8) :: days2 + integer(ESMF_KIND_I8) :: yy_i8, mm_i8, d_i8, s_i8 type(ESMF_TimeInterval) :: timeInterval1, timeInterval2, timeInterval3, & timeInterval4, timeInterval5, timeInterval6 type(ESMF_TimeInterval) :: diffTime, absoluteTime + logical :: correct #endif @@ -3894,16 +3896,143 @@ program ESMF_TimeIntervalUTest name, failMsg, result, ESMF_SRCLINE) !print *, "S, sN, sD_i8 = ", S, sN, sD_i8 - ! ---------------------------------------------------------------------------- +! ---------------------------------------------------------------------------- + !EX_UTest + ! Testing ESMF_TimeIntervalSet from an ISO duration string + write(name, *) "Set an ESMF_TimeInterval using an ISO duration string with just I4." + write(failMsg, *) "Output did not match duration set in string." + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y2M3DT4H5M6S", rc=rc) + call ESMF_TimeIntervalGet(timeInterval1, yy=YY, mm=months, d=days, & + h=h,m=m, s=s, rc=rc) + + ! Check answers + correct=.true. + if (yy /= 1) correct=.false. + if (months /= 2) correct=.false. + if (days /= 3) correct=.false. + if (h /= 4) correct=.false. + if (m /= 5) correct=.false. + if (s /= 6) correct=.false. + +! Debug output +! write(*,*) "yy=",yy +! write(*,*) "mm=",months +! write(*,*) "dd=",days +! write(*,*) "h=",h +! write(*,*) "m=",m +! write(*,*) "s=",s + + + call ESMF_Test((rc .eq. ESMF_SUCCESS) .and. correct, & + name, failMsg, result, ESMF_SRCLINE) + + ! ---------------------------------------------------------------------------- + !EX_UTest + ! Testing ESMF_TimeIntervalSet from an ISO duration string + write(name, *) "Set an ESMF_TimeInterval using an ISO duration string without date." + write(failMsg, *) "Output did not match duration set in string." + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="PT4H5M6S", rc=rc) + call ESMF_TimeIntervalGet(timeInterval1, & + ! yy=YY, mm=months, d_r8=days_r8, d=days, & + h=h,m=m, s=s, rc=rc) + + ! Check answers + correct=.true. + if (h /= 4) correct=.false. + if (m /= 5) correct=.false. + if (s /= 6) correct=.false. + + + call ESMF_Test((rc .eq. ESMF_SUCCESS) .and. correct, & + name, failMsg, result, ESMF_SRCLINE) + +! ---------------------------------------------------------------------------- + !EX_UTest + ! Testing ESMF_TimeIntervalSet from an ISO duration string + write(name, *) "Set an ESMF_TimeInterval using an ISO duration string without time." + write(failMsg, *) "Output did not match duration set in string." + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y2M3D", rc=rc) + call ESMF_TimeIntervalGet(timeInterval1, yy=YY, mm=months, d=days, & + rc=rc) + + ! Check answers + correct=.true. + if (yy /= 1) correct=.false. + if (months /= 2) correct=.false. + if (days /= 3) correct=.false. + +! Debug output +! write(*,*) "yy=",yy +! write(*,*) "mm=",months +! write(*,*) "dd=",days +! write(*,*) "h=",h +! write(*,*) "m=",m +! write(*,*) "s=",s + + + call ESMF_Test((rc .eq. ESMF_SUCCESS) .and. correct, & + name, failMsg, result, ESMF_SRCLINE) + + + + ! ---------------------------------------------------------------------------- + !EX_UTest + ! Testing ESMF_TimeIntervalSet from an ISO duration string + write(name, *) "Set an ESMF_TimeInterval using an ISO duration string with R8 seconds." + write(failMsg, *) "Output did not match duration set in string." + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="PT6.6S", rc=rc) + call ESMF_TimeIntervalGet(timeInterval1, & + s_r8=sec_in_r8, rc=rc) + + ! Check answers + correct=.true. + if (abs(sec_in_r8 - 6.6) < 1.0E-14) correct=.false. + + call ESMF_Test((rc .eq. ESMF_SUCCESS) .and. correct, & + name, failMsg, result, ESMF_SRCLINE) + + ! ---------------------------------------------------------------------------- + !EX_UTest + ! Testing ESMF_TimeIntervalSet from an ISO duration string + write(name, *) "Set an ESMF_TimeInterval using an ISO duration string with R8 time values." + write(failMsg, *) "Output did not match duration set in string." + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="PT4.1H5.2M6.3S", rc=rc) + call ESMF_TimeIntervalGet(timeInterval1, & + h_r8=hr_out_r8,m_r8=min_in_r8, s_r8=sec_out_r8, rc=rc) + + ! Check answers + correct=.true. + if (abs(hr_out_r8-4.1) < 1.0E-14) correct=.false. + if (abs(min_in_r8-5.2) < 1.0E-14) correct=.false. + if (abs(sec_out_r8-6.3) < 1.0E-14) correct=.false. + + call ESMF_Test((rc .eq. ESMF_SUCCESS) .and. correct, & + name, failMsg, result, ESMF_SRCLINE) + + +! ---------------------------------------------------------------------------- !EX_UTest ! Testing ESMF_TimeIntervalSet from an ISO duration string - write(name, *) "Set an ESMF_TimeInterval using an ISO duration string." + write(name, *) "Set an ESMF_TimeInterval using an ISO duration string with I8 (where available)." write(failMsg, *) "Output did not match duration set in string." - call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y2M3.3DT4H5M6S", rc=rc) - !call ESMF_TimeIntervalGet(timeInterval1, s=S, sN=sN, sD=sD, rc=rc) - call ESMF_Test((rc .eq. ESMF_SUCCESS), & + call ESMF_TimeIntervalSet(timeInterval1, & + timeIntervalString="P10000000000Y20000000000M30000000000DT60000000000S", & + rc=rc) + call ESMF_TimeIntervalGet(timeInterval1, yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, & + s_i8=s_i8, rc=rc) + + ! Check answers + correct=.true. + if (yy_i8 /= 10000000000_ESMF_KIND_I8) correct=.false. + if (mm_i8 /= 20000000000_ESMF_KIND_I8) correct=.false. + if (d_i8 /= 30000000000_ESMF_KIND_I8) correct=.false. + if (s_i8 /= 60000000000_ESMF_KIND_I8) correct=.false. + + call ESMF_Test((rc .eq. ESMF_SUCCESS) .and. correct, & name, failMsg, result, ESMF_SRCLINE) + + ! ---------------------------------------------------------------------------- ! return number of failures to environment; 0 = success (all pass) ! return result ! TODO: no way to do this in F90 ? From 00f020e49bbccddd5a773382950bf4ebff5938c5 Mon Sep 17 00:00:00 2001 From: Robert Oehmke Date: Sun, 28 Jan 2024 23:45:13 -0700 Subject: [PATCH 10/10] Add other ISO duration string time interval set interfaces and unit tests. --- .../TimeMgr/interface/ESMF_TimeInterval.F90 | 291 +++++++++++++++++- .../TimeMgr/tests/ESMF_TimeIntervalUTest.F90 | 57 ++++ 2 files changed, 344 insertions(+), 4 deletions(-) diff --git a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 index 9c75f82171..267b24f7b0 100644 --- a/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 +++ b/src/Infrastructure/TimeMgr/interface/ESMF_TimeInterval.F90 @@ -928,7 +928,10 @@ module ESMF_TimeIntervalMod module procedure ESMF_TimeIntervalSetDurStart module procedure ESMF_TimeIntervalSetDurCal module procedure ESMF_TimeIntervalSetDurCalTyp - module procedure ESMF_TimeIntervalSetString + module procedure ESMF_TimeIntervalSetStr + module procedure ESMF_TimeIntervalSetStrStart + module procedure ESMF_TimeIntervalSetStrCal + module procedure ESMF_TimeIntervalSetStrCalTyp ! !DESCRIPTION: ! This interface provides a single entry point for {\tt ESMF\_TimeInterval} ! Set methods. @@ -3056,6 +3059,7 @@ subroutine ESMF_ParseDurString(timeintervalString, & integer :: t_loc ! Init output to 0 + ! NOTE: Need to do all of these here in case date or time parsing isn't done below yy_i8=0 mm_i8=0 d_i8=0 @@ -3129,13 +3133,13 @@ end subroutine ESMF_ParseDurString !------------------------------------------------------------------------------ #undef ESMF_METHOD -#define ESMF_METHOD "ESMF_TimeIntervalSetString()" +#define ESMF_METHOD "ESMF_TimeIntervalSetStr()" !BOP ! !IROUTINE: ESMF_TimeIntervalSet - Initialize or set a TimeInterval from ISO format string ! !INTERFACE: ! Private name; call using ESMF_TimeIntervalSet() - subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) + subroutine ESMF_TimeIntervalSetStr(timeinterval, timeIntervalString, rc) ! !ARGUMENTS: type(ESMF_TimeInterval), intent(inout) :: timeinterval @@ -3208,7 +3212,286 @@ subroutine ESMF_TimeIntervalSetString(timeinterval, timeIntervalString, rc) ! Return success if (present(rc)) rc = ESMF_SUCCESS - end subroutine ESMF_TimeIntervalSetString + end subroutine ESMF_TimeIntervalSetStr + + +!------------------------------------------------------------------------------ +#undef ESMF_METHOD +#define ESMF_METHOD "ESMF_TimeIntervalSetStrCal()" +!BOP +! !IROUTINE: ESMF_TimeIntervalSet - Initialize or set a TimeInterval from ISO format string + +! !INTERFACE: + ! Private name; call using ESMF_TimeIntervalSet() + subroutine ESMF_TimeIntervalSetStrCal(timeinterval, calendar, & + timeIntervalString, rc) + +! !ARGUMENTS: + type(ESMF_TimeInterval), intent(inout) :: timeinterval + type(ESMF_Calendar), intent(in) :: calendar + character(*), intent(in) :: timeIntervalString + integer, intent(out), optional :: rc + +! +! +! !DESCRIPTION: +! Sets the value of the {\tt ESMF\_TimeInterval} using a user specified +! string in ISO duration format (P[n]Y[n]M[n]DT[n]H[n]M[n]S). +! +! The arguments are: +! \begin{description} +! \item[timeinterval] +! The object instance to initialize. +! \item[calendar] +! {\tt Calendar} used to give better definition to +! calendar interval (yy, mm, and/or d) for arithmetic, comparison, +! and conversion operations. Allows calendar interval to "float" +! across all times on a specific calendar. Default = NULL; +! if startTime also not specified, calendar interval "floats" across +! all calendars and times. Mutually exclusive with startTime since +! it contains a calendar. Alternate to, and mutually exclusive with, +! calkindflag below. Primarily for specifying a custom calendar kind. +! \item[timeIntervalString] +! ISO format duration string. +! \item[{[rc]}] +! Return code; equals {\tt ESMF\_SUCCESS} if there are no errors. +! \end{description} +! +!EOP +! !REQUIREMENTS: +! TMGn.n.n + integer :: localrc ! local return code + integer(ESMF_KIND_I8) :: yy_i8 + integer(ESMF_KIND_I8) :: mm_i8 + integer(ESMF_KIND_I8) :: d_i8 + integer(ESMF_KIND_I8) :: s_i8 + real(ESMF_KIND_R8) :: d_r8 + real(ESMF_KIND_R8) :: h_r8 + real(ESMF_KIND_R8) :: m_r8 + real(ESMF_KIND_R8) :: s_r8 + + ! Assume failure until success + if (present(rc)) rc = ESMF_RC_NOT_IMPL + localrc = ESMF_RC_NOT_IMPL + + ! DEBUG OUTPUT: + !write(*,*) "Duration string is:",timeIntervalString + + ! Parse string into values for each time unit + call ESMF_ParseDurString(timeintervalString, & + yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, d_r8=d_r8, & + h_r8=h_r8, m_r8=m_r8, s_i8=s_i8, s_r8=s_r8, rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + + ! Set time interval using time unit values parsed above + + ! NOTE: Just use I8 for integer values, since it looks like integer values + ! are stored that way anyway. Also, for times (h,m,s), it looks like both R8 + ! and I8 are added together, so you can just + ! use both and whichever isn't needed set to 0. + ! An R8 can exactly represent an I4, so just use R8 for hours and minutes + ! where an I4 is all that's available. + call ESMF_TimeIntervalSetDurCal(timeinterval, calendar, & + yy_i8=yy_i8, & + mm_i8=mm_i8, & + d_i8=d_i8, & + s_i8=s_i8, & + d_r8=d_r8, & + h_r8=h_r8, & + m_r8=m_r8, & + s_r8=s_r8, & + rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + + ! Return success + if (present(rc)) rc = ESMF_SUCCESS + end subroutine ESMF_TimeIntervalSetStrCal + + + +!------------------------------------------------------------------------------ +#undef ESMF_METHOD +#define ESMF_METHOD "ESMF_TimeIntervalSetStrCalTyp()" +!BOP +! !IROUTINE: ESMF_TimeIntervalSet - Initialize or set a TimeInterval from ISO format string + +! !INTERFACE: + ! Private name; call using ESMF_TimeIntervalSet() + subroutine ESMF_TimeIntervalSetStrCalTyp(timeinterval, calkindflag, & + timeIntervalString, rc) + +! !ARGUMENTS: + type(ESMF_TimeInterval), intent(inout) :: timeinterval + type(ESMF_CalKind_Flag), intent(in) :: calkindflag + character(*), intent(in) :: timeIntervalString + integer, intent(out), optional :: rc + +! +! +! !DESCRIPTION: +! Sets the value of the {\tt ESMF\_TimeInterval} using a user specified +! string in ISO duration format (P[n]Y[n]M[n]DT[n]H[n]M[n]S). +! +! The arguments are: +! \begin{description} +! \item[timeinterval] +! The object instance to initialize. +! \item[calkindflag] +! Alternate to, and mutually exclusive with, +! calendar above. More convenient way of specifying a built-in +! calendar kind. +! \item[timeIntervalString] +! ISO format duration string. +! \item[{[rc]}] +! Return code; equals {\tt ESMF\_SUCCESS} if there are no errors. +! \end{description} +! +!EOP +! !REQUIREMENTS: +! TMGn.n.n + integer :: localrc ! local return code + integer(ESMF_KIND_I8) :: yy_i8 + integer(ESMF_KIND_I8) :: mm_i8 + integer(ESMF_KIND_I8) :: d_i8 + integer(ESMF_KIND_I8) :: s_i8 + real(ESMF_KIND_R8) :: d_r8 + real(ESMF_KIND_R8) :: h_r8 + real(ESMF_KIND_R8) :: m_r8 + real(ESMF_KIND_R8) :: s_r8 + + ! Assume failure until success + if (present(rc)) rc = ESMF_RC_NOT_IMPL + localrc = ESMF_RC_NOT_IMPL + + ! DEBUG OUTPUT: + !write(*,*) "Duration string is:",timeIntervalString + + ! Parse string into values for each time unit + call ESMF_ParseDurString(timeintervalString, & + yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, d_r8=d_r8, & + h_r8=h_r8, m_r8=m_r8, s_i8=s_i8, s_r8=s_r8, rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + + ! Set time interval using time unit values parsed above + + ! NOTE: Just use I8 for integer values, since it looks like integer values + ! are stored that way anyway. Also, for times (h,m,s), it looks like both R8 + ! and I8 are added together, so you can just + ! use both and whichever isn't needed set to 0. + ! An R8 can exactly represent an I4, so just use R8 for hours and minutes + ! where an I4 is all that's available. + call ESMF_TimeIntervalSetDurCalTyp(timeinterval, calkindflag, & + yy_i8=yy_i8, & + mm_i8=mm_i8, & + d_i8=d_i8, & + s_i8=s_i8, & + d_r8=d_r8, & + h_r8=h_r8, & + m_r8=m_r8, & + s_r8=s_r8, & + rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + + ! Return success + if (present(rc)) rc = ESMF_SUCCESS + end subroutine ESMF_TimeIntervalSetStrCalTyp + +!------------------------------------------------------------------------------ +#undef ESMF_METHOD +#define ESMF_METHOD "ESMF_TimeIntervalSetStrStart()" +!BOP +! !IROUTINE: ESMF_TimeIntervalSet - Initialize or set a TimeInterval from ISO format string + +! !INTERFACE: + ! Private name; call using ESMF_TimeIntervalSet() + subroutine ESMF_TimeIntervalSetStrStart(timeinterval, startTime, & + timeIntervalString, rc) + +! !ARGUMENTS: + type(ESMF_TimeInterval), intent(inout) :: timeinterval + type(ESMF_Time), intent(in) :: startTime + character(*), intent(in) :: timeIntervalString + integer, intent(out), optional :: rc + +! +! +! !DESCRIPTION: +! Sets the value of the {\tt ESMF\_TimeInterval} using a user specified +! string in ISO duration format (P[n]Y[n]M[n]DT[n]H[n]M[n]S). +! +! The arguments are: +! \begin{description} +! \item[timeinterval] +! The object instance to initialize. +! \item[startTime] +! Starting time of an absolute calendar interval +! (yy, mm, and/or d); pins a calendar interval to a specific point +! in time. If not set, and calendar also not set, calendar interval +! "floats" across all calendars and times. +! \item[timeIntervalString] +! ISO format duration string. +! \item[{[rc]}] +! Return code; equals {\tt ESMF\_SUCCESS} if there are no errors. +! \end{description} +! +!EOP +! !REQUIREMENTS: +! TMGn.n.n + integer :: localrc ! local return code + integer(ESMF_KIND_I8) :: yy_i8 + integer(ESMF_KIND_I8) :: mm_i8 + integer(ESMF_KIND_I8) :: d_i8 + integer(ESMF_KIND_I8) :: s_i8 + real(ESMF_KIND_R8) :: d_r8 + real(ESMF_KIND_R8) :: h_r8 + real(ESMF_KIND_R8) :: m_r8 + real(ESMF_KIND_R8) :: s_r8 + + ! Assume failure until success + if (present(rc)) rc = ESMF_RC_NOT_IMPL + localrc = ESMF_RC_NOT_IMPL + + ! DEBUG OUTPUT: + !write(*,*) "Duration string is:",timeIntervalString + + ! Parse string into values for each time unit + call ESMF_ParseDurString(timeintervalString, & + yy_i8=yy_i8, mm_i8=mm_i8, d_i8=d_i8, d_r8=d_r8, & + h_r8=h_r8, m_r8=m_r8, s_i8=s_i8, s_r8=s_r8, rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + + ! Set time interval using time unit values parsed above + + ! NOTE: Just use I8 for integer values, since it looks like integer values + ! are stored that way anyway. Also, for times (h,m,s), it looks like both R8 + ! and I8 are added together, so you can just + ! use both and whichever isn't needed set to 0. + ! An R8 can exactly represent an I4, so just use R8 for hours and minutes + ! where an I4 is all that's available. + call ESMF_TimeIntervalSetDurStart(timeinterval, startTime, & + yy_i8=yy_i8, & + mm_i8=mm_i8, & + d_i8=d_i8, & + s_i8=s_i8, & + d_r8=d_r8, & + h_r8=h_r8, & + m_r8=m_r8, & + s_r8=s_r8, & + rc=localrc) + if (ESMF_LogFoundError(localrc, ESMF_ERR_PASSTHRU, & + ESMF_CONTEXT, rcToReturn=rc)) return + + ! Return success + if (present(rc)) rc = ESMF_SUCCESS + end subroutine ESMF_TimeIntervalSetStrStart + + + !------------------------------------------------------------------------------ #undef ESMF_METHOD diff --git a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 index b413ebb75b..e4d8fb104b 100644 --- a/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 +++ b/src/Infrastructure/TimeMgr/tests/ESMF_TimeIntervalUTest.F90 @@ -83,6 +83,7 @@ program ESMF_TimeIntervalUTest type(ESMF_TimeInterval) :: timeInterval1, timeInterval2, timeInterval3, & timeInterval4, timeInterval5, timeInterval6 type(ESMF_TimeInterval) :: diffTime, absoluteTime + type(ESMF_CalKind_Flag) :: calkindflag logical :: correct #endif @@ -2959,6 +2960,34 @@ program ESMF_TimeIntervalUTest !print *, "yy=", YY, "mm=", MM, "d=", D, "h=", H, "m=", M, "s=", S + ! ---------------------------------------------------------------------------- + !EX_UTest + write(name, *) "Test ISO String and Calendar set interface." + write(failMsg, *) " Did not return 24 months or ESMF_SUCCESS" + call ESMF_TimeIntervalSet(timeStep, timeIntervalString="P2Y", calendar=gregorianCalendar, & + rc=rc) + call ESMF_TimeIntervalGet(timeStep, mm=months, rc=rc) + + call ESMF_Test((months==24 .and. rc==ESMF_SUCCESS), & + name, failMsg, result, ESMF_SRCLINE) + + + ! ---------------------------------------------------------------------------- + !EX_UTest + write(name, *) "Test ISO String and startTime set interface." + write(failMsg, *) " Did not return 24 months or ESMF_SUCCESS" + call ESMF_TimeSet(startTime, d=1200, h=12, m=17, s=58, & + calendar=julianDayCalendar, rc=rc) + call ESMF_TimeIntervalSet(timeStep, startTime, timeIntervalString="P2Y", & + rc=rc) + + call ESMF_Test((rc==ESMF_SUCCESS), & + name, failMsg, result, ESMF_SRCLINE) + + ! ---------------------------------------------------------------------------- + + + call ESMF_CalendarDestroy(julianDayCalendar) call ESMF_CalendarDestroy(day360Calendar) call ESMF_CalendarDestroy(noLeapCalendar) @@ -4030,9 +4059,37 @@ program ESMF_TimeIntervalUTest call ESMF_Test((rc .eq. ESMF_SUCCESS) .and. correct, & name, failMsg, result, ESMF_SRCLINE) + + +! ---------------------------------------------------------------------------- + !EX_UTest + ! Testing ESMF_TimeIntervalSet from an ISO duration string + write(name, *) "Set an ESMF_TimeInterval using an ISO duration string and caltype" + write(failMsg, *) "Output did not match duration set in string." + call ESMF_TimeIntervalSet(timeInterval1, timeIntervalString="P1Y", & + calkindflag=ESMF_CALKIND_GREGORIAN, rc=rc) + call ESMF_TimeIntervalGet(timeInterval1, yy=YY, calkindflag=calkindflag, & + rc=rc) + + ! Check answers + correct=.true. + if (yy /= 1) correct=.false. + if (calkindflag /= ESMF_CALKIND_GREGORIAN) correct=.false. + ! Debug output +! write(*,*) "yy=",yy +! write(*,*) "mm=",months +! write(*,*) "dd=",days +! write(*,*) "h=",h +! write(*,*) "m=",m +! write(*,*) "s=",s + + call ESMF_Test((rc .eq. ESMF_SUCCESS) .and. correct, & + name, failMsg, result, ESMF_SRCLINE) + + ! ---------------------------------------------------------------------------- ! return number of failures to environment; 0 = success (all pass) ! return result ! TODO: no way to do this in F90 ?