#include <misc.h>


module time_manager 59,5

   use shr_kind_mod, only: r8 => shr_kind_r8
   use pmgrid, only: masterproc
   use esmf_timemgmtmod, only: &
      esmf_errhandlersettype, esmf_err_return, esmf_errprint, esmf_success, &
      esmf_time, esmf_timeinit, esmf_timeget, esmf_timegetdays, &
        esmf_timeincrement, esmf_timedecrement, &
      esmf_date, esmf_dateinit, esmf_gregorian, esmf_no_leap, esmf_dateget, &
        esmf_dateincrementsec, esmf_dateincrementday, esmf_datedecrement, &
        esmf_datediff, esmf_dategetfltdayofyear, &
      esmf_timemgr, esmf_timemgrinit, esmf_timemgradvance, esmf_timemgrgetnstep, &
        esmf_timemgrgetstepsize, esmf_timemgrgetstartdate, esmf_timemgrgetbasedate, &
        esmf_timemgrlaststep, esmf_timemgrgetcurrdate, esmf_timemgrgetprevdate, esmf_dateislater, &
        esmf_timemgrrestartwrite, esmf_timemgrrestartread
   use string_utils, only: to_upper
   use dycore, only: dycore_is
   use abortutils, only: endrun
#ifdef SPMD
   use mpishorthand, only: mpicom, mpiint, mpilog
#endif

   implicit none
   private
   save

! Public methods

   public ::&
      timemgr_preset,           &! time manager initialization before namelist input
      timemgr_init,             &! time manager initialization
      advance_timestep,         &! increment timestep number
      get_step_size,            &! return step size in seconds
      get_nstep,                &! return timestep number
      get_curr_date,            &! return date components at end of current timestep
      get_prev_date,            &! return date components at beginning of current timestep
      get_start_date,           &! return components of the start date
      get_ref_date,             &! return components of the reference date
      get_perp_date,            &! return components of the perpetual date, and current time of day
      get_curr_time,            &! return components of elapsed time since reference date
      get_curr_calday,          &! return calendar day at end of current timestep
      is_first_step,            &! return true on first step of initial run
      is_first_restart_step,    &! return true on first step of restart or branch run
      is_end_curr_day,          &! return true on last timestep in current day
      is_end_curr_month,        &! return true on last timestep in current month
      is_last_step,             &! return true on last timestep
      is_perpetual,             &! return true if perpetual calendar is in use
      timemgr_write_restart,    &! write info to file needed to restart the time manager
      timemgr_read_restart,     &! read info from file needed to restart the time manager
      timemgr_restart,          &! restart the time manager
      timemgr_datediff           ! calculate difference between two time instants

! Public data for namelist input

   character(len=32), public ::&
      calendar   = 'NO_LEAP'     ! Calendar to use in date calculations.
                                 ! 'NO_LEAP' or 'GREGORIAN'
   integer, parameter :: uninit_int = -999999999
   integer, public ::&
      dtime         = uninit_int,  &! timestep in seconds
      nestep        = uninit_int,  &! final timestep (or day if negative) number
      nelapse       = uninit_int,  &! number of timesteps (or days if negative) to extend a run
      start_ymd     = uninit_int,  &! starting date for run in yearmmdd format
      start_tod     = 0,           &! starting time of day for run in seconds
      stop_ymd      = uninit_int,  &! stopping date for run in yearmmdd format
      stop_tod      = 0,           &! stopping time of day for run in seconds
      ref_ymd       = uninit_int,  &! reference date for time coordinate in yearmmdd format
      ref_tod       = 0,           &! reference time of day for time coordinate in seconds
      perpetual_ymd = uninit_int    ! date used by perpetual calendar
   logical, public ::&
      perpetual_run = .false.       ! .true. => use a perpetual calendar

! Public data for communicating with modules that don't have 'get' methods.

   integer, public ::&
      ic_ymd  = uninit_int,     &! date of initial conditions in yearmmdd format
      ic_tod  = 0                ! time of day of initial conditions in seconds
                                 ! ic_ymd, ic_tod set in control/readinitial.F90
   logical, public ::&
      tm_aqua_planet = .false.   ! flag for aqua-planet simulation

! Private module data

   type(esmf_timemgr) :: tm_id          ! time manager ID
   type(esmf_date)    :: tm_perp_date   ! reference date for perpetual calendar day calc

   integer ::&                      ! Data required to restart time manager:
      rst_type      = uninit_int,  &! calendar type
      rst_nstep     = uninit_int,  &! current step number
      rst_step_days = uninit_int,  &! days component of timestep size
      rst_step_sec  = uninit_int,  &! seconds component of timestep size
      rst_start_ymd = uninit_int,  &! start date
      rst_start_tod = uninit_int,  &! start time of day
      rst_stop_ymd  = uninit_int,  &! stop date
      rst_stop_tod  = uninit_int,  &! stop time of day
      rst_ref_ymd   = uninit_int,  &! reference date
      rst_ref_tod   = uninit_int,  &! reference time of day
      rst_curr_ymd  = uninit_int,  &! current date
      rst_curr_tod  = uninit_int,  &! current time of day
      rst_perp_ymd  = uninit_int    ! perpetual date
   logical ::&
      rst_perp_cal  = .false.       ! true when using perpetual calendar

   logical :: tm_first_restart_step = .false.  ! true for first step of a restart or branch run
   logical :: tm_perp_calendar = .false.       ! true when using perpetual calendar
   integer :: cal_type = uninit_int            ! calendar type

!=========================================================================================
contains
!=========================================================================================


subroutine timemgr_preset() 1,1

! Initialize variables before namelist input.

   implicit none

! Local variables
   character(len=*), parameter :: sub = 'timemgr_preset'
!-----------------------------------------------------------------------------------------

   if ( dtime == uninit_int ) then
      if (dycore_is ('EUL')) then
         dtime  = 1200
      else if (dycore_is ('SLD')) then
         dtime  = 3600
      else if (dycore_is ('LR')) then
         dtime  = 3600
      else
         call endrun (sub//': need valid dycore to set default DTIME')
      end if
   end if

end subroutine timemgr_preset
!=========================================================================================


subroutine timemgr_init() 1,20

! Initialize the ESMF time manager.
!
! NOTE - Assumptions:
!      1) The namelist variables have been set before this routine is called.  (set in control/parse_namelist.F90)
!      2) ic_ymd, ic_tod are set before this routine is called.  (set in control/readinitial.F90)

   implicit none

! Local variables
   character(len=*), parameter :: sub = 'timemgr_init'
   character(len=len(calendar)) :: cal
   integer :: rc                 ! return code
   type(esmf_time) :: step_size   ! timestep size
   type(esmf_date) :: start_date  ! start date for run
   type(esmf_date) :: stop_date   ! stop date for run
   type(esmf_date) :: temp_date   ! temporary date used in logic
   type(esmf_date) :: ref_date    ! reference date for time coordinate
! Some backwards compatibility stuff:
   type(esmf_time) :: diff
   integer :: ntspday, ndays, nsecs
   logical :: islater
   logical :: run_length_specified
!-----------------------------------------------------------------------------------------

! Initialize error handling.

   call esmf_errhandlersettype(esmf_err_return)

! Initialize calendar type.

   cal = to_upper(calendar)
   if ( trim(cal) == 'NO_LEAP' ) then
      cal_type = esmf_no_leap
   else if ( trim(cal) == 'GREGORIAN' ) then
      cal_type = esmf_gregorian
   else
      write(6,*)sub,': unrecognized calendar specified: ',calendar
      call endrun
   end if

! Initialize timestep size.

   if ( mod(86400,dtime) /=0 ) then
      call endrun (sub//': timestep must divide evenly into 1 day')
   end if

   step_size = esmf_timeinit(0, dtime, rc)
   call chkrc(rc, sub//': error return from esmf_timeinit: setting step_size')


! Initialize start date.

   if ( start_ymd == uninit_int ) then
      start_ymd = ic_ymd
      start_tod = ic_tod
   end if
   start_date = esmf_dateinit(cal_type, start_ymd, start_tod, rc)
   call chkrc(rc, sub//': error return from esmf_dateinit: setting start_date')

! Initialize reference date for time coordinate.

   if ( ref_ymd /= uninit_int ) then
      ref_date = esmf_dateinit(cal_type, ref_ymd, ref_tod, rc)
   else
      ref_date = esmf_dateinit(start_date, rc)
   end if
   call chkrc(rc, sub//': error return from esmf_dateinit: setting ref_date')

! Initialize stop date.

   stop_date = esmf_dateinit(cal_type, 99991231, stop_tod, rc)
   run_length_specified = .false.

   if ( stop_ymd /= uninit_int ) then
      temp_date = esmf_dateinit(cal_type, stop_ymd, stop_tod, rc)
      call chkrc(rc, sub//': error processing stop_ymd')
      call esmf_dateislater(temp_date, stop_date, islater, rc)
      call chkrc(rc, sub//': error from esmf_dateislater while processing stop_ymd')
      if ( islater ) then
         stop_date = temp_date
      end if
      run_length_specified = .true.
   end if

   if ( nestep /= uninit_int ) then
      if ( nestep >= 0 ) then
         temp_date = esmf_dateincrementsec(start_date, dtime*nestep, rc)
      else
         temp_date = esmf_dateincrementday(start_date, -nestep, rc)
      end if
      call chkrc(rc, sub//': error processing nestep')
      call esmf_dateislater(temp_date, stop_date, islater, rc)
      call chkrc(rc, sub//': error from esmf_dateislater while processing nestep')
      if ( islater ) then
         stop_date = temp_date
      end if
      run_length_specified = .true.
   end if

   if ( nelapse /= uninit_int ) then
      if ( nelapse >= 0 ) then
         temp_date = esmf_dateincrementsec(start_date, dtime*nelapse, rc)
      else
         temp_date = esmf_dateincrementday(start_date, -nelapse, rc)
      end if
      call chkrc(rc, sub//': error processing nelapse')
      call esmf_dateislater(temp_date, stop_date, islater, rc)
      call chkrc(rc, sub//': error from esmf_dateislater while processing nelapse')
      if ( islater ) then
         stop_date = temp_date
      end if
      run_length_specified = .true.
   end if

   if ( .not. run_length_specified ) then
      call endrun (sub//': Must specify at least one of stop_ymd, nestep, or nelapse')
   end if



! Initialize a time manager.

   tm_id = esmf_timemgrinit(step_size, start_date, stop_date, ref_date, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrinit')

! Initialize date used for perpetual calendar day calculation.

   if ( tm_aqua_planet ) then
      tm_perp_date = esmf_dateinit(cal_type, 321, 0, rc)
      call chkrc(rc, sub//': error return from esmf_dateinit: setting tm_perp_date')
      tm_perp_calendar = .true.
   else if ( perpetual_run ) then
      if ( perpetual_ymd /= uninit_int ) then
         tm_perp_date = esmf_dateinit(cal_type, perpetual_ymd, 0, rc)
         call chkrc(rc, sub//': error return from esmf_dateinit: setting tm_perp_date')
         tm_perp_calendar = .true.
      else
         tm_perp_date = esmf_dateinit(cal_type, ic_ymd, 0, rc)
         call chkrc(rc, sub//': error return from esmf_dateinit: setting tm_perp_date')
         tm_perp_calendar = .true.
      end if
   end if

! Set variables from "comtim.h interface" for backwards compatibility.

! Calculation of ending timestep number (nestep) assumes a constant stepsize.
   ntspday = 86400/dtime
   diff = esmf_timeinit()
   call esmf_datediff(start_date, stop_date, diff, islater, rc)
   call chkrc(rc, sub//': error return from esmf_datediff calculating nestep')
   call esmf_timeget(diff, ndays, nsecs, rc)
   call chkrc(rc, sub//': error return from esmf_timeget calculating nestep')
   nestep = ntspday*ndays + nsecs/dtime
   if ( mod(nsecs,dtime) /= 0 ) nestep = nestep + 1

! Print configuration summary to log file (stdout).

   if (masterproc) then
      call timemgr_print()
   end if

end subroutine timemgr_init
!=========================================================================================


subroutine timemgr_restart() 1,31

! Restart the ESMF time manager.
!
! NOTE - Assumptions:
!      1) The namelist variables have been set before this routine is called.
!         (set in control/parse_namelist.F90)
!         The stop date is the only thing that can be changed by the user on a restart.
!      2) Restart data have been read on the master process before this routine is called.
!         (timemgr_read_restart called from control/restart.F90::read_restart)

   implicit none

! Local variables
   character(len=*), parameter :: sub = 'timemgr_restart'
   integer :: rc                 ! return code
   type(esmf_date) :: start_date  ! start date for run
   type(esmf_date) :: stop_date   ! stop date for run
   type(esmf_date) :: temp_date   ! temporary date used in logic
   type(esmf_date) :: curr_date   ! date of data in restart file
   logical :: islater
   logical :: run_length_specified
   integer :: ymd, tod
! Some backwards compatibility stuff:
   type(esmf_time) :: diff
   integer :: ntspday, ndays, nsecs
!-----------------------------------------------------------------------------------------

#if ( defined SPMD ) 
   call mpibcast(rst_type,      1, mpiint, 0, mpicom)
   call mpibcast(rst_nstep,     1, mpiint, 0, mpicom)
   call mpibcast(rst_step_days, 1, mpiint, 0, mpicom)
   call mpibcast(rst_step_sec,  1, mpiint, 0, mpicom)
   call mpibcast(rst_start_ymd, 1, mpiint, 0, mpicom)
   call mpibcast(rst_start_tod, 1, mpiint, 0, mpicom)
   call mpibcast(rst_stop_ymd,  1, mpiint, 0, mpicom)
   call mpibcast(rst_stop_tod,  1, mpiint, 0, mpicom)
   call mpibcast(rst_ref_ymd,   1, mpiint, 0, mpicom)
   call mpibcast(rst_ref_tod,   1, mpiint, 0, mpicom)
   call mpibcast(rst_curr_ymd,  1, mpiint, 0, mpicom)
   call mpibcast(rst_curr_tod,  1, mpiint, 0, mpicom)
   call mpibcast(rst_perp_ymd,  1, mpiint, 0, mpicom)
   call mpibcast(rst_perp_cal,  1, mpilog, 0, mpicom)
#endif

! Initialize error handling.

   call esmf_errhandlersettype(esmf_err_return)

! Initialize calendar type.

   cal_type = rst_type
   if ( rst_type == esmf_no_leap ) then
      calendar = 'NO_LEAP'
   else if ( rst_type == esmf_gregorian ) then
      calendar = 'GREGORIAN'
   else
      write(6,*)sub,': unrecognized calendar type in restart file: ',rst_type
      call endrun
   end if

! Initialize the timestep.

   dtime = rst_step_days*86400 + rst_step_sec

! Initialize start date.

   start_date = esmf_dateinit(rst_type, rst_start_ymd, rst_start_tod, rc)
   call chkrc(rc, sub//': error return from esmf_dateinit: setting start_date')

! Initialize current date.

   curr_date = esmf_dateinit(rst_type, rst_curr_ymd, rst_curr_tod, rc)
   call chkrc(rc, sub//': error return from esmf_dateinit: setting curr_date')

! Initialize stop date.

   stop_date = esmf_dateinit(cal_type, 99991231, stop_tod, rc)
   run_length_specified = .false.

   if ( stop_ymd /= uninit_int ) then
      temp_date = esmf_dateinit(rst_type, stop_ymd, stop_tod, rc)
      call chkrc(rc, sub//': error processing stop_ymd')
      call esmf_dateislater(temp_date, stop_date, islater, rc)
      call chkrc(rc, sub//': error from esmf_dateislater while processing stop_ymd')
      if ( islater ) then
         stop_date = temp_date
      end if
      run_length_specified = .true.
   end if

   if ( nestep /= uninit_int ) then
      if ( nestep >= 0 ) then
         temp_date = esmf_dateincrementsec(start_date, dtime*nestep, rc)
      else
         temp_date = esmf_dateincrementday(start_date, -nestep, rc)
      end if
      call chkrc(rc, sub//': error processing nestep')
      call esmf_dateislater(temp_date, stop_date, islater, rc)
      call chkrc(rc, sub//': error from esmf_dateislater while processing nestep')
      if ( islater ) then
         stop_date = temp_date
      end if
      run_length_specified = .true.
   end if

   if ( nelapse /= uninit_int ) then
      if ( nelapse >= 0 ) then
         temp_date = esmf_dateincrementsec(curr_date, dtime*nelapse, rc)
      else
         temp_date = esmf_dateincrementday(curr_date, -nelapse, rc)
      end if
      call chkrc(rc, sub//': error processing nelapse')
      call esmf_dateislater(temp_date, stop_date, islater, rc)
      call chkrc(rc, sub//': error from esmf_dateislater while processing nelapse')
      if ( islater ) then
         stop_date = temp_date
      end if
      run_length_specified = .true.
   end if

   if ( .not. run_length_specified ) then
      stop_date = esmf_dateinit(rst_type, rst_stop_ymd, rst_stop_tod, rc)
   end if


! Check that stop date is later than current date.

   call esmf_dateislater(curr_date, stop_date, islater, rc)
   call chkrc(rc, sub//': error return from esmf_dateislater: comparing start and stop dates')
   if ( .not. islater ) then
      write(6,*)sub,': stop date must be specified later than current date: '
      call esmf_dateget(curr_date, ymd, tod)
      write(6,*)' Current date (ymd tod): ', ymd, tod
      call esmf_dateget(stop_date, ymd, tod)
      write(6,*)' Stop date (ymd tod): ', ymd, tod
      call endrun ('TIMEMGR_RESTART')
   end if
   call esmf_dateget(stop_date, rst_stop_ymd, rst_stop_tod)

! Restart a time manager.

   tm_id = esmf_timemgrrestartread(rst_type, rst_nstep, rst_step_days, rst_step_sec, rst_start_ymd, &
                                  rst_start_tod, rst_stop_ymd, rst_stop_tod, rst_ref_ymd, rst_ref_tod, &
                                  rst_curr_ymd, rst_curr_tod, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrrestartread')

! Advance the timestep.  Data from the restart file corresponds to the
! last timestep of the previous run.

   call advance_timestep()

!  Set flag that this is the first timestep of the restart run.

   tm_first_restart_step = .true.

! Initialize date used for perpetual calendar day calculation.

   if ( rst_perp_cal ) then
      tm_perp_date = esmf_dateinit(rst_type, rst_perp_ymd, 0, rc)
      call chkrc(rc, sub//': error return from esmf_dateinit: setting tm_perp_date')
      tm_perp_calendar = .true.
   end if

! Set variables from "comtim.h interface" for backwards compatibility.

! Calculation of ending timestep number (nestep) assumes a constant stepsize.
   ntspday = 86400/dtime
   diff = esmf_timeinit()
   call esmf_datediff(start_date, stop_date, diff, islater, rc)
   call chkrc(rc, sub//': error return from esmf_datediff calculating nestep')
   call esmf_timeget(diff, ndays, nsecs, rc)
   call chkrc(rc, sub//': error return from esmf_timeget calculating nestep')
   nestep = ntspday*ndays + nsecs/dtime
   if ( mod(nsecs,dtime) /= 0 ) nestep = nestep + 1

! Print configuration summary to log file (stdout).

   if (masterproc) then
      call timemgr_print()
   end if

end subroutine timemgr_restart
!=========================================================================================


subroutine timemgr_print() 2,1

   implicit none

! Local variables
   character(len=*), parameter :: sub = 'timemgr_print'
   integer :: rc
   integer :: day, sec, ymd, tod
   character(len=32) :: cal     ! Calendar to use in date calculations.
   integer ::&                  ! Data required to restart time manager:
      type      = uninit_int,  &! calendar type
      nstep     = uninit_int,  &! current step number
      step_days = uninit_int,  &! days component of timestep size
      step_sec  = uninit_int,  &! seconds component of timestep size
      start_ymd = uninit_int,  &! start date
      start_tod = uninit_int,  &! start time of day
      stop_ymd  = uninit_int,  &! stop date
      stop_tod  = uninit_int,  &! stop time of day
      ref_ymd   = uninit_int,  &! reference date
      ref_tod   = uninit_int,  &! reference time of day
      curr_ymd  = uninit_int,  &! current date
      curr_tod  = uninit_int    ! current time of day
!-----------------------------------------------------------------------------------------

   call esmf_timemgrrestartwrite(tm_id, type, nstep, step_days, step_sec, &
                                start_ymd, start_tod, stop_ymd, stop_tod, ref_ymd, &
                                ref_tod, curr_ymd, curr_tod, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrrestartwrite')

   write(6,*)' ********** Time Manager Configuration **********'

   if ( type == esmf_no_leap ) then
      cal = 'NO_LEAP'
   else if ( type == esmf_gregorian ) then
      cal = 'GREGORIAN'
   end if

   write(6,*)'  Calendar type:            ',trim(cal)
   write(6,*)'  Timestep size (seconds):  ', (step_days*86400 + step_sec)
   write(6,*)'  Start date (ymd tod):     ', start_ymd, start_tod
   write(6,*)'  Stop date (ymd tod):      ', stop_ymd, stop_tod
   write(6,*)'  Reference date (ymd tod): ', ref_ymd, ref_tod
   write(6,*)'  Current step number:      ', nstep
   write(6,*)'  Ending step number:       ', nestep
   write(6,*)'  Current date (ymd tod):   ', curr_ymd, curr_tod

   if ( tm_perp_calendar ) then
      call esmf_dateget(tm_perp_date, ymd, tod)
      write(6,*)'  Use perpetual diurnal cycle date (ymd): ', ymd
   end if

   write(6,*)' ************************************************'

end subroutine timemgr_print
!=========================================================================================


subroutine advance_timestep() 3,1

! Increment the timestep number.

   implicit none
   
! Local variables
   character(len=*), parameter :: sub = 'advance_timestep'
   integer :: rc
!-----------------------------------------------------------------------------------------

   call esmf_timemgradvance(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgradvance')

! Set first step flag off.

   tm_first_restart_step = .false.

end subroutine advance_timestep
!=========================================================================================


function get_step_size() 12,1

! Return the step size in seconds.

   implicit none
   
! Return value
   integer :: get_step_size

! Local variables
   character(len=*), parameter :: sub = 'get_step_size'
   integer :: days, seconds
   integer :: rc
!-----------------------------------------------------------------------------------------

   call esmf_timemgrgetstepsize(tm_id, days, seconds, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetstepsize')
   get_step_size = 86400*days + seconds

end function get_step_size
!=========================================================================================


function get_nstep() 15,1

! Return the timestep number.

   implicit none
   
! Return value
   integer :: get_nstep

! Local variables
   character(len=*), parameter :: sub = 'get_nstep'
   integer :: rc
!-----------------------------------------------------------------------------------------

   get_nstep = esmf_timemgrgetnstep(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetnstep')

end function get_nstep
!=========================================================================================


subroutine get_curr_date(yr, mon, day, tod, offset) 30,5

! Return date components valid at end of current timestep with an optional
! offset (positive or negative) in seconds.

   implicit none
   
! Arguments
   integer, intent(out) ::&
      yr,    &! year
      mon,   &! month
      day,   &! day of month
      tod     ! time of day (seconds past 0Z)

   integer, optional, intent(in) :: offset  ! Offset from current time in seconds.
                                            ! Positive for future times, negative 
                                            ! for previous times.

! Local variables
   character(len=*), parameter :: sub = 'get_curr_date'
   integer :: rc
   type(esmf_date) :: date
   type(esmf_time) :: off
   integer :: ymd
!-----------------------------------------------------------------------------------------

   date = esmf_timemgrgetcurrdate(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetcurrdate')

   if (present(offset)) then
      if (offset > 0) then
         date = esmf_dateincrementsec(date, offset, rc)
         call chkrc(rc, sub//': error incrementing current date')
      else if (offset < 0) then
         off = esmf_timeinit(0, -offset, rc)
         call chkrc(rc, sub//': error setting offset time type')
         date = esmf_datedecrement(date, off, rc)
         call chkrc(rc, sub//': error decrementing current date')
      end if
   end if

   call esmf_dateget(date, ymd, tod, rc)
   call chkrc(rc, sub//': error return from esmf_dateget')
   yr = ymd/10000
   mon = mod(ymd, 10000) / 100
   day = mod(ymd, 100)

end subroutine get_curr_date
!=========================================================================================


subroutine get_perp_date(yr, mon, day, tod, offset) 9,6

! Return time of day valid at end of current timestep and the components
! of the perpetual date (with an optional offset (positive or negative) in seconds.

   implicit none
   
! Arguments
   integer, intent(out) ::&
      yr,    &! year
      mon,   &! month
      day,   &! day of month
      tod     ! time of day (seconds past 0Z)

   integer, optional, intent(in) :: offset  ! Offset from current time in seconds.
                                            ! Positive for future times, negative 
                                            ! for previous times.

! Local variables
   character(len=*), parameter :: sub = 'get_perp_date'
   integer :: rc
   type(esmf_date) :: date
   type(esmf_time) :: off
   integer :: ymd, idum
!-----------------------------------------------------------------------------------------

   date = esmf_timemgrgetcurrdate(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetcurrdate')

   if (present(offset)) then
      if (offset > 0) then
         date = esmf_dateincrementsec(date, offset, rc)
         call chkrc(rc, sub//': error incrementing current date')
      else if (offset < 0) then
         off = esmf_timeinit(0, -offset, rc)
         call chkrc(rc, sub//': error setting offset time type')
         date = esmf_datedecrement(date, off, rc)
         call chkrc(rc, sub//': error decrementing current date')
      end if
   end if

   call esmf_dateget(date, idum, tod, rc)
   call chkrc(rc, sub//': error return from esmf_dateget')
   call esmf_dateget(tm_perp_date, ymd, idum, rc)
   call chkrc(rc, sub//': error return from esmf_dateget')
   yr = ymd/10000
   mon = mod(ymd, 10000) / 100
   day = mod(ymd, 100)

end subroutine get_perp_date
!=========================================================================================


subroutine get_prev_date(yr, mon, day, tod) 3,2

! Return date components valid at beginning of current timestep.

   implicit none
   
! Arguments
   integer, intent(out) ::&
      yr,    &! year
      mon,   &! month
      day,   &! day of month
      tod     ! time of day (seconds past 0Z)

! Local variables
   character(len=*), parameter :: sub = 'get_prev_date'
   integer :: rc
   type(esmf_date) :: date
   integer :: ymd
!-----------------------------------------------------------------------------------------

   date = esmf_timemgrgetprevdate(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetprevdate')

   call esmf_dateget(date, ymd, tod, rc)
   call chkrc(rc, sub//': error return from esmf_dateget')
   yr = ymd/10000
   mon = mod(ymd, 10000) / 100
   day = mod(ymd, 100)

end subroutine get_prev_date
!=========================================================================================


subroutine get_start_date(yr, mon, day, tod) 1,2

! Return date components valid at beginning of initial run.

   implicit none
   
! Arguments
   integer, intent(out) ::&
      yr,    &! year
      mon,   &! month
      day,   &! day of month
      tod     ! time of day (seconds past 0Z)

! Local variables
   character(len=*), parameter :: sub = 'get_start_date'
   integer :: rc
   type(esmf_date) :: date
   integer :: ymd
!-----------------------------------------------------------------------------------------

   date = esmf_timemgrgetstartdate(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetstartdate')

   call esmf_dateget(date, ymd, tod, rc)
   call chkrc(rc, sub//': error return from esmf_dateget')
   yr = ymd/10000
   mon = mod(ymd, 10000) / 100
   day = mod(ymd, 100)

end subroutine get_start_date
!=========================================================================================


subroutine get_ref_date(yr, mon, day, tod) 2,2

! Return date components of the reference date.

   implicit none
   
! Arguments
   integer, intent(out) ::&
      yr,    &! year
      mon,   &! month
      day,   &! day of month
      tod     ! time of day (seconds past 0Z)

! Local variables
   character(len=*), parameter :: sub = 'get_ref_date'
   integer :: rc
   type(esmf_date) :: date
   integer :: ymd
!-----------------------------------------------------------------------------------------

   date = esmf_timemgrgetbasedate(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetbasedate')

   call esmf_dateget(date, ymd, tod, rc)
   call chkrc(rc, sub//': error return from esmf_dateget')
   yr = ymd/10000
   mon = mod(ymd, 10000) / 100
   day = mod(ymd, 100)

end subroutine get_ref_date
!=========================================================================================


subroutine get_curr_time(days, seconds) 4,4

! Return time components valid at end of current timestep.
! Current time is the time interval between the current date and the reference date.

   implicit none
   
! Arguments
   integer, intent(out) ::&
      days,   &! number of whole days in time interval
      seconds  ! remaining seconds in time interval

! Local variables
   character(len=*), parameter :: sub = 'get_curr_time'
   integer :: rc
   type(esmf_date) :: cdate, rdate
   type(esmf_time) :: diff
   logical :: islater
!-----------------------------------------------------------------------------------------

   cdate = esmf_timemgrgetcurrdate(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetcurrdate')

   rdate = esmf_timemgrgetbasedate(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetbasedate')

   call esmf_datediff(rdate, cdate, diff, islater, rc)
   call chkrc(rc, sub//': error return from esmf_datediff')

   call esmf_timeget(diff, days, seconds, rc)
   call chkrc(rc, sub//': error return from esmf_timeget')

end subroutine get_curr_time
!=========================================================================================


function get_curr_calday(offset) 16,7

! Return calendar day at end of current timestep with optional offset.
! Calendar day 1.0 = 0Z on Jan 1.

   implicit none
   
! Arguments
   integer, optional, intent(in) :: offset  ! Offset from current time in seconds.
                                            ! Positive for future times, negative 
                                            ! for previous times.
! Return value
   real(r8) :: get_curr_calday

! Local variables
   character(len=*), parameter :: sub = 'get_curr_calday'
   integer :: rc
   type(esmf_date) :: date
   type(esmf_time) :: off
   integer :: ymd, tod
!-----------------------------------------------------------------------------------------

   date = esmf_timemgrgetcurrdate(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetcurrdate')

   if (present(offset)) then
      if (offset > 0) then
         date = esmf_dateincrementsec(date, offset, rc)
         call chkrc(rc, sub//': error incrementing current date')
      else if (offset < 0) then
         off = esmf_timeinit(0, -offset, rc)
         call chkrc(rc, sub//': error setting offset time type')
         date = esmf_datedecrement(date, off, rc)
         call chkrc(rc, sub//': error decrementing current date')
      end if
   end if

   if ( tm_perp_calendar ) then
      call esmf_dateget(date, ymd, tod, rc)
      call chkrc(rc, sub//': error return from esmf_dateget')
      date = esmf_dateincrementsec(tm_perp_date, tod, rc)
      call chkrc(rc, sub//': error return from esmf_dateincrementsec')
   end if

   get_curr_calday = esmf_dategetfltdayofyear(date, rc)
   call chkrc(rc, sub//': error return from esmf_dategetfltdayofyear')

end function get_curr_calday
!=========================================================================================


function is_end_curr_day(),1

! Return true if current timestep is last timestep in current day.

   implicit none
   
! Return value
   logical :: is_end_curr_day

! Local variables
   integer ::&
      yr,    &! year
      mon,   &! month
      day,   &! day of month
      tod     ! time of day (seconds past 0Z)
!-----------------------------------------------------------------------------------------

   call get_curr_date(yr, mon, day, tod)
   is_end_curr_day = (tod == 0)

end function is_end_curr_day
!=========================================================================================


function is_end_curr_month(),1

! Return true if current timestep is last timestep in current month.

   implicit none
   
! Return value
   logical :: is_end_curr_month

! Local variables
   integer ::&
      yr,    &! year
      mon,   &! month
      day,   &! day of month
      tod     ! time of day (seconds past 0Z)
!-----------------------------------------------------------------------------------------

   call get_curr_date(yr, mon, day, tod)
   is_end_curr_month = (day == 1  .and.  tod == 0)

end function is_end_curr_month
!=========================================================================================


function is_first_step(),1

! Return true on first step of initial run only.

   implicit none
   
! Return value
   logical :: is_first_step

! Local variables
   character(len=*), parameter :: sub = 'is_first_step'
   integer :: rc
   integer :: nstep
!-----------------------------------------------------------------------------------------

   nstep = esmf_timemgrgetnstep(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrgetnstep')
   is_first_step = (nstep == 0)

end function is_first_step
!=========================================================================================


function is_first_restart_step()

! Return true on first step of restart run only.

   implicit none
   
! Return value
   logical :: is_first_restart_step
!-----------------------------------------------------------------------------------------

   is_first_restart_step = tm_first_restart_step

end function is_first_restart_step
!=========================================================================================


function is_last_step(),1

! Return true on last timestep.

   implicit none
   
! Return value
   logical :: is_last_step

! Local variables
   character(len=*), parameter :: sub = 'is_last_step'
   integer :: rc
!-----------------------------------------------------------------------------------------

   is_last_step = esmf_timemgrlaststep(tm_id, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrlaststep')

end function is_last_step
!=========================================================================================


function is_perpetual()

! Return true on last timestep.

   implicit none
   
! Return value
   logical :: is_perpetual
!-----------------------------------------------------------------------------------------

   is_perpetual = tm_perp_calendar

end function is_perpetual
!=========================================================================================


subroutine timemgr_write_restart(ftn_unit) 1,3

! Write information needed on restart to a binary Fortran file.
! It is assumed that this routine is called only from the master proc if in SPMD mode.

   implicit none

! Arguments
   integer, intent(in) :: ftn_unit  ! Fortran unit number

! Local variables
   character(len=*), parameter :: sub = 'timemgr_write_restart'
   integer :: rc   ! return code
   integer :: tod
!-----------------------------------------------------------------------------------------

   call esmf_timemgrrestartwrite(tm_id, rst_type, rst_nstep, rst_step_days, rst_step_sec, &
                                rst_start_ymd, rst_start_tod, rst_stop_ymd, rst_stop_tod, rst_ref_ymd, &
                                rst_ref_tod, rst_curr_ymd, rst_curr_tod, rc)
   call chkrc(rc, sub//': error return from esmf_timemgrrestartwrite')

   if ( tm_perp_calendar ) then
      call esmf_dateget(tm_perp_date, rst_perp_ymd, tod, rc)
      call chkrc(rc, sub//': error return from esmf_dateget')
      rst_perp_cal = tm_perp_calendar
   end if

   write(ftn_unit, iostat=rc) rst_type, rst_nstep, rst_step_days, rst_step_sec, &
                              rst_start_ymd, rst_start_tod, rst_stop_ymd, rst_stop_tod, rst_ref_ymd, &
                              rst_ref_tod, rst_curr_ymd, rst_curr_tod, rst_perp_ymd, rst_perp_cal

   if (rc /= 0 ) then
      write (6,*) 'WRITE iostat= ',rc,' on i/o unit = ',ftn_unit
      call endrun ('TIMEMGR_WRITE_RESTART')
   end if

end subroutine timemgr_write_restart
!=========================================================================================


subroutine timemgr_read_restart(ftn_unit) 1,1

! Read information needed to restart from a binary Fortran file.
! It is assumed that this routine is called only from the master proc if in SPMD mode.

   implicit none

! Arguments
   integer, intent(in) :: ftn_unit  ! Fortran unit number

! Local variables
   character(len=*), parameter :: sub = 'timemgr_read_restart'
   integer :: rc   ! return code
!-----------------------------------------------------------------------------------------

   read(ftn_unit, iostat=rc) rst_type, rst_nstep, rst_step_days, rst_step_sec, &
                             rst_start_ymd, rst_start_tod, rst_stop_ymd, rst_stop_tod, rst_ref_ymd, &
                             rst_ref_tod, rst_curr_ymd, rst_curr_tod, rst_perp_ymd, rst_perp_cal

   if (rc /= 0 ) then
      write (6,*) 'READ iostat= ',rc,' on i/o unit = ',ftn_unit
      call endrun ('TIMEMGR_READ_RESTART')
   end if

end subroutine timemgr_read_restart
!=========================================================================================


subroutine timemgr_datediff(ymd1, tod1, ymd2, tod2, days) 1,4

! Calculate the difference (ymd2,tod2) - (ymd1,tod1) and return the result in days.

   implicit none

! Arguments
   integer, intent(in) ::&
      ymd1,    &! date1 in yyyymmdd format
      tod1,    &! time of day relative to date1 (seconds past 0Z)
      ymd2,    &! date2 in yyyymmdd format
      tod2      ! time of day relative to date2 (seconds past 0Z)

   real(r8) :: days ! (ymd2,tod2)-(ymd1,tod1) in days

! Local variables
   character(len=*), parameter :: sub = 'timemgr_datediff'
   integer :: rc   ! return code

   type(esmf_date) :: date1
   type(esmf_date) :: date2
   type(esmf_time) :: diff
   logical :: islater
   integer :: ndays, nsecs
!-----------------------------------------------------------------------------------------

   date1 = esmf_dateinit(cal_type, ymd1, tod1, rc)
   call chkrc(rc, sub//': error return from esmf_dateinit: setting date1')

   date2 = esmf_dateinit(cal_type, ymd2, tod2, rc)
   call chkrc(rc, sub//': error return from esmf_dateinit: setting date2')

   diff = esmf_timeinit()
   call esmf_datediff(date1, date2, diff, islater, rc)
   call chkrc(rc, sub//': error return from esmf_datediff')
   call esmf_timeget(diff, ndays, nsecs, rc)
   call chkrc(rc, sub//': error return from esmf_timeget')
   days = ndays + nsecs/86400._r8
   if (.not. islater) days = -days

end subroutine timemgr_datediff
!=========================================================================================


subroutine chkrc(rc, mes) 68,1
   implicit none
   integer, intent(in)          :: rc   ! return code from time management library
   character(len=*), intent(in) :: mes  ! error message
   if ( rc == esmf_success ) return
   write(6,*) mes
   call esmf_errprint(rc)
   call endrun ('CHKRC')
end subroutine chkrc

end module time_manager