!-------------------------------------------------------------------------------------------------
module time_mod
!-------------------------------------------------------------------------------------------------
  implicit none
!-------------------------------------------------------------------------------------------------
  public :: timer_reset,timer_end_start,timer_start,timer_end,timer_diff,timer_get
  public :: time_mod_int,init_time_mod,datestr,datenum,datevec
  public :: isleap,countleaps,dtime_now
  public :: Gettimer
  public :: get_doy
  private
!-------------------------------------------------------------------------------------------------
  interface init_time_mod
    module procedure time_mod_int
  end interface init_time_mod
  interface Gettimer
    module procedure timer_get
  end interface Gettimer
!-------------------------------------------------------------------------------------------------
  interface datenum
    module procedure datenum_int_s,    &
                     datenum_int_v,    &
                     datenum_r8_s,     &
                     datenum_r8_v,     &
                     datenum_r8_ymd,   &
                     datenum_r8_ymdhms,&
                     dtime_now
  end interface datenum
!-------------------------------------------------------------------------------------------------
  interface datevec
    module procedure datevec_int,      &
                     datevec_r8,       &
                     datevec_r8_flag,  &
                     datevec_s
  end interface datevec
!-------------------------------------------------------------------------------------------------
  interface datestr
    module procedure datestr_v,        &
                     datestr_n_int,    &
                     datestr_n_r8
  end interface datestr

  interface get_doy
    module procedure get_doy,get_doy_it
  end interface get_doy

!-------------------------------------------------------------------------------------------------
  integer :: month(12)=[31,28,31,30,31,30,31,31,30,31,30,31]
  integer :: normal_yeardays=365
  integer :: refdays=0,dateref(6)=[0,1,1,0,0,0]
  integer :: calendar_type=0,leapflag=1
  ! calendar_type=0 : normal calendar with leap years.
  ! calendar_type=1 : normal calendar without leap years.
  ! calendar_type=2 : simple calendar with 30 days in each month.
!-------------------------------------------------------------------------------------------------
#define MAXTIMER 100
  real*8,private:: rtcb(MAXTIMER),rtcl(MAXTIMER)
!#define HAVE_CPU_TIME
#define HAVE_SYSTEM_CLOCK
!-------------------------------------------------------------------------------------------------
  contains
!-------------------------------------------------------------------------------------------------
!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
!-------------------------------------------------------------------------------------------------
#ifndef HAVE_CPU_TIME
#ifdef HAVE_SYSTEM_CLOCK
  real*8 function cpu_time()
    integer :: count
    integer,save :: last_count=-1,count_rate, count_max
    real(8),save :: dtime
    if(last_count/=-1)then
      call system_clock(count)
      if(count<last_count)then
        dtime=dtime+count_max/float(count_rate)
      endif
    else
      call system_clock(count,count_rate,count_max)
      dtime=0
    endif
    last_count=count
    cpu_time=dtime+count/float(count_rate)
  end function cpu_time
#else
  real*8 function cpu_time()
    integer :: vals(8)
    integer,save :: oday=-1
    real(8),save :: dtime
    call date_and_time(values=vals)
    if(vals(3)/=oday)dtime=86400.d0
    oday=vals(3)
    cpu_time=dtime+vals(5)*3600.d0+vals(6)*60.d0+vals(7)+vals(8)*0.001d0
  end function cpu_time
#endif
#endif
  subroutine timer_reset(iid)
    integer,optional,intent(in) :: iid
    if(present(iid))then
      rtcl(iid)=0;rtcb(iid)=cpu_time()
    else
      rtcl=0;rtcb=cpu_time()
    endif
  end subroutine timer_reset

  subroutine timer_end_start(iide,iids)
    integer,intent(in) :: iide,iids
    real*8 :: rtcc
    rtcc=cpu_time()
    if(iide>=1)rtcl(iide)=rtcl(iide)+(rtcc-rtcb(iide))
    if(iids>=1)rtcb(iids)=rtcc
  end subroutine timer_end_start

  subroutine timer_start(iid)
    integer,intent(in) :: iid
    rtcb(iid)=cpu_time()
  end subroutine timer_start

  subroutine timer_end(iid)
    integer,intent(in) :: iid
    rtcl(iid)=rtcl(iid)+(cpu_time()-rtcb(iid))
  end subroutine timer_end

  real*8 function timer_diff(iid)
    integer,intent(in) :: iid
    timer_diff=(cpu_time()-rtcb(iid)) !+rtcl(iid)
  end function timer_diff

  real*8 function timer_get(iid)
    integer,intent(in) :: iid
    timer_get=rtcl(iid)
  end function timer_get
!-------------------------------------------------------------------------------------------------
!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
!-------------------------------------------------------------------------------------------------
  subroutine time_mod_int(dateref_,calendar_type_)
    integer,intent(in),optional :: dateref_(6)
    integer,intent(in),optional :: calendar_type_
    ! calendar_type_=0 normal calendar with leap years.
    ! calendar_type_=1 normal calendar without leap years.
    ! calendar_type_=2 simple calendar with 30 days in each month.
    calendar_type=0;if(present(calendar_type_))calendar_type=calendar_type_
    if(calendar_type==0)then
      leapflag=1
      normal_yeardays=365
      month=[31,28,31,30,31,30,31,31,30,31,30,31]
    elseif(calendar_type==1)then
      leapflag=0
      normal_yeardays=365
      month=[31,28,31,30,31,30,31,31,30,31,30,31]
    elseif(calendar_type==2)then
      leapflag=0
      normal_yeardays=360
      month=30
    endif
    dateref=[0,1,1,0,0,0]
    if(present(dateref_))dateref=dateref_
    refdays=datenum_int0(dateref)
  end subroutine time_mod_int
!-------------------------------------------------------------------------------------------------
!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
!-------------------------------------------------------------------------------------------------
  integer function datenum_int0(datev)
    integer,intent(in) :: datev(:)
    integer,parameter :: dateref(6)=[0,1,1,0,0,0]
    integer :: mon
    datenum_int0=normal_yeardays*(datev(1)-dateref(1)) &
                -dateref(3)
    do mon=1,datev(2)-1
      datenum_int0=datenum_int0+month(mon)
    enddo
    datenum_int0=datenum_int0 &
                +datev(3)     &
                +countleaps(dateref,datev)
  end function datenum_int0

  integer function datenum_int_v(datev,mode)
    integer,intent(in) :: datev(:)
    integer,intent(in) :: mode
    datenum_int_v=datenum_int0(datev)-refdays
  end function datenum_int_v

  integer function datenum_int_s(ctime,mode)
    character(len=*),intent(in) :: ctime
    integer,intent(in) :: mode
    datenum_int_s=datenum_int_v(datevec(ctime),mode)
  end function datenum_int_s

  real(8) function datenum_r8_s(ctime)
    character(len=*),intent(in) :: ctime
    datenum_r8_s=datenum_r8_v(datevec(ctime))
  end function datenum_r8_s

  real(8) function datenum_r8_v(datev)
    integer,intent(in) :: datev(:)
    datenum_r8_v=datenum_int_v(datev,1) &
                +datev(4)/24.d0         &
                +datev(5)/1440.d0       &
                +datev(6)/86400.d0
  end function datenum_r8_v

  real(8) function datenum_r8_ymd(year,mon,day)
    integer,intent(in) :: year,mon,day
    datenum_r8_ymd=datenum_r8_v([year,mon,day,0,0,0])
  end function datenum_r8_ymd

  real(8) function datenum_r8_ymdhms(year,mon,day,hour,mint,secnd)
    integer,intent(in) :: year,mon,day,hour,mint,secnd
    datenum_r8_ymdhms=datenum_r8_v([year,mon,day,hour,mint,secnd])
  end function datenum_r8_ymdhms

  real(8) function dtime_now()
    integer :: val(8)
    call date_and_time(values=val)
    dtime_now=datenum_r8_v([val(1:3),val(5:7)])
  end function dtime_now

  integer function get_doy(dtime)
    real(8),intent(in) :: dtime
    integer :: itime(6)
    itime=datevec(dtime)
    get_doy=dtime-datenum([itime(1),1,0,0,0,0])
  end function get_doy

  integer function get_doy_it(itime)
    integer,intent(in) :: itime(6)
    get_doy_it=datenum(itime)-datenum([itime(1),1,0,0,0,0])
  end function get_doy_it

!-------------------------------------------------------------------------------------------------
!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
!-------------------------------------------------------------------------------------------------
  function datevec_int(dtime)
    integer,intent(in) :: dtime
    integer :: datevec_int(6)
    !-----------------------------
    integer :: rest_days
    integer :: imon,lp
    datevec_int(1:6)=[0,1,1,0,0,0]
    datevec_int(1)=int((dtime+refdays)/normal_yeardays)
    rest_days=(dtime+refdays)-datenum_int0(datevec_int)
    do while (rest_days<0)
      datevec_int(1)=datevec_int(1)-1
      rest_days=rest_days+normal_yeardays        &
                         +isleap(datevec_int(1))
    enddo
    do imon=1,12
      lp=0;if(imon==2)lp=isleap(datevec_int(1))
      if(rest_days>=(month(imon)+lp))then
        rest_days=rest_days-month(imon)-lp
      else
        datevec_int(2)=imon
        datevec_int(3)=rest_days+1
        exit
      endif
    enddo
  end function datevec_int

  function datevec_r8(dtime)
    real(8),intent(in) :: dtime
    integer :: datevec_r8(6)
    !-----------------------------
    integer :: idays,secends
    real(8) :: resttime
    idays=dtime
    resttime=dtime-idays
    if((1.d0-resttime)<(1.d0/86400.d0))then
      idays=idays+1
      resttime=0.d0
    endif
    datevec_r8=datevec_int(idays);  secends=nint(resttime*86400)
    datevec_r8(4)=int(secends/3600);secends=mod(secends,3600)
    datevec_r8(5)=int(secends/60)
    datevec_r8(6)=mod(secends,60)
  end function datevec_r8

  function datevec_r8_flag(dtime,flag)
    real(8),intent(in) :: dtime
    integer,intent(in) :: flag
    integer :: datevec_r8_flag(6)
    !-----------------------------
    if(flag==1)datevec_r8_flag=datevec_r8(dtime)
    if(flag==2)datevec_r8_flag=datevec_r8(dtime/24.d0)
    if(flag==3)datevec_r8_flag=datevec_r8(dtime/1440.d0)
    if(flag==4)datevec_r8_flag=datevec_r8(dtime/86400.d0)
  end function datevec_r8_flag

  function datevec_s(ctime)
    character(len=14),intent(in) :: ctime
    integer :: datevec_s(6)
    !-----------------------------
    read(ctime,'(i4.4,5i2.2)')datevec_s
  end function datevec_s
!-------------------------------------------------------------------------------------------------
!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
!-------------------------------------------------------------------------------------------------
  character(len=14) function datestr_v(itime)
    integer,intent(in) :: itime(6)
    write(datestr_v,'(i4.4,5i2.2)')itime
  end function datestr_v

  character(len=14) function datestr_n_int(dtime)
    integer,intent(in) :: dtime
    integer :: itime(6)
    itime=datevec(dtime)
    datestr_n_int=datestr_v(itime)
  end function datestr_n_int

  character(len=14) function datestr_n_r8(dtime)
    real(8),intent(in) :: dtime
    integer :: itime(6)
    itime=datevec(dtime)
    datestr_n_r8=datestr_v(itime)
  end function datestr_n_r8

!-------------------------------------------------------------------------------------------------
!@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
!-------------------------------------------------------------------------------------------------
  integer function countleaps(datev1,datev2)
    integer,intent(in) :: datev1(3),datev2(3)
    integer :: iy1,iy2,iy
    iy1=datev1(1);if(datev1(2)>2)iy1=datev1(1)+1
    iy2=datev2(1);if(datev2(2)==1 .or. (datev2(2)==2 .and. datev2(3)<=29))iy2=datev2(1)-1
    countleaps=0
    do iy=iy1,iy2
      countleaps=countleaps+isleap(iy)
    enddo
  end function countleaps

  function isleap(iyear)
    integer :: isleap
    integer,intent(in) :: iyear
    isleap=0
    if((mod(iyear,4)==0 .and. mod(iyear,100)/=0) .or. mod(iyear,400)==0)isleap=1
    isleap=isleap*leapflag
  end function isleap
!-------------------------------------------------------------------------------------------------
end module time_mod
!-------------------------------------------------------------------------------------------------

