subroutine gag_fromdate(date,idate,error)
  use gsys_interfaces, except_this=>gag_fromdate
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  !  Converts the date '01-JAN-1984' in internal code
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: date   ! String date
  integer(kind=4),  intent(out)   :: idate  ! Internal coded date value
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='DATE'
  character(len=3) :: cm(12),m
  integer(kind=4) :: id,iy,im,ier
  character(len=1) :: d1,d2
  ! Data
  data cm /'JAN','FEB','MAR','APR','MAY','JUN',  &
           'JUL','AUG','SEP','OCT','NOV','DEC'/
  !
  error=.false.
  if (date.eq.'*') return
  read(date,'(I2,A1,A3,A1,I4)',iostat=ier) id,d1,m,d2,iy
  if (ier.ne.0 .or. d1.ne.'-' .or. d2.ne.'-') then
    call gsys_message(seve%e,rname,'Date conversion error')
    error=.true.
    return
  endif
  !
  call sic_upper(m)
  do im=1,12
    if (cm(im).eq.m) then
      call gag_datj(id,im,iy,idate)
      return
    endif
  enddo
  !
  call gsys_message(seve%e,rname,'Date conversion error')
  error=.true.
  return
  !
end subroutine gag_fromdate
!
subroutine gag_todate(idate,date,error)
  use gsys_interfaces, except_this=>gag_todate
  !---------------------------------------------------------------------
  ! @ public
  ! Converts the internal code to formatted date
  !---------------------------------------------------------------------
  integer(kind=4),  intent(in)    :: idate  ! Internal coded date value
  character(len=*), intent(out)   :: date   ! String date
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=3) :: cm(12)
  integer(kind=4) :: id,iy,im,ier
  ! Data
  data cm /'JAN','FEB','MAR','APR','MAY','JUN',  &
           'JUL','AUG','SEP','OCT','NOV','DEC'/
  !
  call gag_jdat(idate,id,im,iy)
  write(date,1000,iostat=ier) id,cm(im),iy
1000 format(i2.2,'-',a,'-',i4.4)
end subroutine gag_todate
!
subroutine gag_fromyyyymmdd(string,dobs,error)
  use gsys_interfaces, except_this=>gag_fromyyyymmdd
  !---------------------------------------------------------------------
  ! @ public
  ! Decode a string date in the format "YYYYMMDD" into a gag date
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: string  !
  integer(kind=4),  intent(out)   :: dobs    !
  logical,          intent(inout) :: error   !
  ! Local
  integer(kind=4) :: iy,im,id,ier
  !
  read(string(1:4),'(I4)',iostat=ier)  iy
  if (ier.ne.0) then
    error = .true.
    return
  endif
  read(string(5:6),'(I2)',iostat=ier)  im
  if (ier.ne.0) then
    error = .true.
    return
  endif
  read(string(7:8),'(I2)',iostat=ier)  id
  if (ier.ne.0) then
    error = .true.
    return
  endif
  !
  call gag_datj(id,im,iy,dobs)
  !
end subroutine gag_fromyyyymmdd
!
subroutine gag_toyyyymmdd(dobs,string,error)
  use gsys_interfaces, except_this=>gag_toyyyymmdd
  !---------------------------------------------------------------------
  ! @ public
  ! Encode a gag date to a string date in the format "YYYYMMDD"
  !---------------------------------------------------------------------
  integer(kind=4),  intent(in)    :: dobs    !
  character(len=*), intent(out)   :: string  !
  logical,          intent(inout) :: error   !
  ! Local
  integer(kind=4) :: iy,im,id,ier
  !
  call gag_jdat(dobs,id,im,iy)
  !
  string = ''  ! First fill the whole string with blanks
  !
  write(string(1:4),'(I4.4)',iostat=ier)  iy
  if (ier.ne.0) then
    error = .true.
    return
  endif
  write(string(5:6),'(I2.2)',iostat=ier)  im
  if (ier.ne.0) then
    error = .true.
    return
  endif
  write(string(7:8),'(I2.2)',iostat=ier)  id
  if (ier.ne.0) then
    error = .true.
    return
  endif
  !
end subroutine gag_toyyyymmdd
!
subroutine gag_isodate(date,idate,error)
  use gsys_interfaces, except_this=>gag_isodate
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  !   Converts the ISO #8601 date '1996-10-14T10:34:55.345' in internal
  ! code.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: date   ! String date
  integer(kind=4),  intent(out)   :: idate  ! Internal coded date value
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='DATE'
  integer(kind=4) :: id,iy,im,ier
  !
  if (date.eq.'*') return
  ! This reads only the date digits, not the time digit (short form of
  ! ISO/IAU#8601 date format
  read(date,'(I4,1X,I2,1X,I2)',iostat=ier) iy,im,id
  if (ier.ne.0) then
    call gsys_message(seve%e,rname,'Wrong ISO Date format, conversion error')
    error=.true.
    return
  endif
  !
  call gag_datj(id,im,iy,idate)
  return
  !
  call gsys_message(seve%e,rname,'Date conversion error')
  error=.true.
  return
  !
end subroutine gag_isodate
!
subroutine gag_toisodate(idate,date,error)
  use gsys_interfaces, except_this=>gag_toisodate
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  ! Converts the internal code to ISO formatted date
  !---------------------------------------------------------------------
  integer(kind=4),  intent(in)    :: idate  ! Internal coded date value
  character(len=*), intent(out)   :: date   ! String date
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='DATE'
  integer(kind=4) :: id,iy,im,ier
  !
  error=.false.
  call gag_jdat(idate,id,im,iy)
  write(date,1000,iostat=ier) iy,im,id
  if (ier.ne.0) then
    call gsys_message(seve%e,rname,'Unable to write ISO Date format')
    error=.true.
    return
  endif
  !
1000 format(i4.4,'-',i2.2,'-',i2.2)
end subroutine gag_toisodate
!
subroutine isodate_to_gagdate(date,error)
  use gsys_interfaces, except_this=>isodate_to_gagdate
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  ! Converts an ISO date representation to a GAG (DD-Monthname-YY) date
  !---------------------------------------------------------------------
  character(len=*), intent(inout) :: date   ! String date
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='DATE'
  integer(kind=4) :: id,iy,im,ier
  !
  error=.false.
  read(date,'(I4,1X,I2,1X,I2)',iostat=ier) iy,im,id
  if (ier.ne.0) then
    call gsys_message(seve%e,rname,'Wrong ISO Date format, conversion error')
    error=.true.
    return
  endif
  write(date,1000,iostat=ier) iy,im,id
  if (ier.ne.0) then
    call gsys_message(seve%e,rname,'Unable to write ISO Date format')
    error=.true.
    return
  endif
  !
1000 format(i4.4,'-',i2.2,'-',i2.2)
end subroutine isodate_to_gagdate
!
subroutine gagdate_to_isodate(date,error)
  use gsys_interfaces, except_this=>gagdate_to_isodate
  use gbl_message
  !---------------------------------------------------------------------
  ! @ public
  ! Converts an ISO date representation to a GAG (DD-Monthname-YY) date
  !---------------------------------------------------------------------
  character(len=*), intent(inout) :: date   ! String date
  logical,          intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='DATE'
  character(len=3) :: cm(12),m
  integer(kind=4) :: id,iy,im,ier
  ! Data
  data cm /'JAN','FEB','MAR','APR','MAY','JUN',  &
           'JUL','AUG','SEP','OCT','NOV','DEC'/
  !
  error=.false.
  read(date,'(I2,1X,A,1X,I4)',iostat=ier) id,m,iy
  if (ier.ne.0) then
    call gsys_message(seve%e,rname,'Wrong Date format, ISO conversion error')
    error=.true.
    return
  endif
  call sic_upper(m)
  do im=1,12
    if (cm(im).eq.m) then
      write(date,1000,iostat=ier) iy,im,id
      if (ier.ne.0) then
        call gsys_message(seve%e,rname,'Unable to write ISO Date format')
        error=.true.
        return
      endif
      return
    endif
  enddo
  call gsys_message(seve%e,rname,'Wrong Date format, ISO conversion error')
  error=.true.
  return
1000 format(i4.4,'-',i2.2,'-',i2.2)
end subroutine gagdate_to_isodate
!
subroutine gag_datj(id,im,iy,jour)
  use gsys_interfaces, except_this=>gag_datj
  !---------------------------------------------------------------------
  ! @ public
  !   Convert a date (id/im/iy) in number of days till the 01/01/1984
  !---------------------------------------------------------------------
  integer(kind=4), intent(in)  :: id    ! Day number
  integer(kind=4), intent(in)  :: im    ! Month number
  integer(kind=4), intent(in)  :: iy    ! Year number
  integer(kind=4), intent(out) :: jour  ! Elapsed day count
  ! Local
  integer(kind=4) :: id1,im1
  integer(kind=4) :: ideb(13),ibiss,jour4
  integer(kind=4), parameter :: izero=2025
  ! Data
  data ideb/0,31,59,90,120,151,181,212,243,273,304,334,365/
  !
  im1 = max(1,im)
  im1 = min(12,im1)
  ibiss=gag_julda(iy+1)-gag_julda(iy)-365
  id1 = max(1,id)
  if (im1.ne.2) id1 = min(id1,(ideb(im1+1)-ideb(im1)))
  if (im1.eq.2) id1 = min(id1,(ideb(im1+1)+ibiss-ideb(im1)))
  jour4=id1+ideb(im1)
  if (im1.ge.3) jour4=jour4+ibiss
  jour4=jour4+gag_julda(iy)
  jour=jour4
  !
end subroutine gag_datj
!
subroutine gag_jdat(jour,id,im,iy)
  use gsys_interfaces, except_this=>gag_jdat
  !---------------------------------------------------------------------
  ! @ public
  ! Reverse conversion
  !---------------------------------------------------------------------
  integer(kind=4), intent(in)  :: jour  ! Elapsed day count
  integer(kind=4), intent(out) :: id    ! Day number
  integer(kind=4), intent(out) :: im    ! Month number
  integer(kind=4), intent(out) :: iy    ! Year number
  ! Local
  integer(kind=4) :: ideb(13),ibiss,j,m
  integer(kind=4), parameter :: izero=2025
  ! Data
  data ideb/0,31,59,90,120,151,181,212,243,273,304,334,365/
  !
  iy=izero+jour/365
1 j=jour-gag_julda(iy)
  if (j.le.0) then
    iy=iy-1
    goto 1
  elseif (j.gt.365) then
    ! Special case for 31 december of leap years...
    if (j.eq.366) then
      ibiss=gag_julda(iy+1)-gag_julda(iy)-365
      if (ibiss.eq.0) then
        iy=iy+1
        goto 1
      endif
    else
      iy=iy+1
      goto 1
    endif
  endif
  ibiss=gag_julda(iy+1)-gag_julda(iy)-365
  do m=12,1,-1
    id=j-ideb(m)
    if (m.ge.3) id=id-ibiss
    if (id.gt.0) exit
  enddo
  im=m
end subroutine gag_jdat
!
function gag_julda(nyr)
  !---------------------------------------------------------------------
  ! @ private
  !  Returns Julian date of 1 day of Year
  !---------------------------------------------------------------------
  integer(kind=4) :: gag_julda  ! Date returned
  integer(kind=4), intent(in) :: nyr  !
  ! Local
  integer(kind=4) :: nyrm1,ic,nadd
  integer(kind=4), parameter :: izero=2025
  !
  nyrm1=nyr-1
  ic=nyrm1/100
  nadd=nyrm1/4-ic+ic/4
  gag_julda=365.*(nyrm1-izero)+nadd
end function gag_julda
!
subroutine gag_isodate2mjd(timestring,mjd,error)
  use gbl_message
  use gsys_interfaces, except_this=>gag_isodate2mjd
  !---------------------------------------------------------------------
  ! @ public
  ! Convert an ISO #8601 date string of the form:
  !   2015-01-09T08:58:19.230000
  ! into a Modified Julian Date value.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: timestring
  real(kind=8),     intent(out)   :: mjd
  logical,          intent(inout) :: error
  ! local
  integer(kind=4) :: intyear,intmonth,intday,inthours,intminutes,ier
  real(kind=8) :: seconds
  character(len=message_length) :: mess
  character(len=*), parameter :: rname='ISODATE2MJD'
  !
  read(timestring( 1: 4),'(i4)',iostat=ier) intyear
  if (ier.ne.0)  goto 10
  read(timestring( 6: 7),'(i2)',iostat=ier) intmonth
  if (ier.ne.0)  goto 10
  read(timestring( 9:10),'(i2)',iostat=ier) intday
  if (ier.ne.0)  goto 10
  call gag_gregorian2mjd(intyear,intmonth,intday,mjd,ier)
  if (ier.ne.0)  goto 10
  read(timestring(12:13),'(i2)',iostat=ier) inthours
  if (ier.ne.0)  goto 10
  read(timestring(15:16),'(i2)',iostat=ier) intminutes
  if (ier.ne.0)  goto 10
  read(timestring(18:23),*,     iostat=ier) seconds
  if (ier.ne.0)  goto 10
  mjd = mjd+(inthours+(intminutes+seconds/60d0)/60d0)/24d0
  return
  !
  ! Read error
10 continue
  write(mess,'(3a)') 'Can not translate string ''',trim(timestring),''''
  call gsys_message(seve%e,rname,mess)
  error = .true.
  return
end subroutine gag_isodate2mjd
!
subroutine gag_gregorian2mjd(iy,im,id,djm,ier)
  use gsys_interfaces, except_this=>gag_gregorian2mjd
  !---------------------------------------------------------------------
  ! @ private
  ! Gregorian Calendar to Modified Julian Date
  ! MJD is not computed when ier.ne.0
  !
  ! The year must be -4699 (i.e. 4700BC) or later.
  !
  ! The algorithm is derived from that of Hatcher 1984
  ! (QJRAS 25, 53-55).
  !
  ! Derived from P.T.Wallace   Starlink   11 March 1998
  ! Copyright (C) 1998 Rutherford Appleton Laboratory
  !---------------------------------------------------------------------
  integer(kind=4), intent(in)  :: iy  ! year in Gregorian calendar
  integer(kind=4), intent(in)  :: im  ! month in Gregorian calendar
  integer(kind=4), intent(in)  :: id  ! day in Gregorian calendar
  real(kind=8),    intent(out) :: djm ! Modified Julian Date (JD-2400000.5) for 0 hrs
  integer(kind=4), intent(out) :: ier ! Status  (0 = OK, 1 = bad year, 2 = bad month, 3 = bad day)
  ! local
  integer :: mtab(12) !  Month lengths in days
  data mtab / 31,28,31,30,31,30,31,31,30,31,30,31 /
  !
  ! Preset status
  ier=0
  ! Validate year
  if (iy.lt.-4699) then
     ier=1
  else
     ! Validate month
     if (im.ge.1.and.im.le.12) then
        ! Allow for leap year
        if (mod(iy,4).eq.0) then
           mtab(2)=29
        else
           mtab(2)=28
        endif
        if (mod(iy,100).eq.0.and.mod(iy,400).ne.0) mtab(2)=28
        ! Validate day
        if (id.lt.1.or.id.gt.mtab(im)) ier=3
        ! Modified Julian Date
        djm=dble((1461*(iy-(12-im)/10+4712))/4   &
             +(306*mod(im+9,12)+5)/10   &
             -(3*((iy-(12-im)/10+4900)/100))/4   &
             +id-2399904)
     else
        ! Bad month
        ier=2
     endif
  endif
end subroutine gag_gregorian2mjd
!
subroutine gag_mjd2isodate(mjd,timestring,error)
  use phys_const
  use gbl_message
  use gsys_interfaces, except_this=>gag_mjd2isodate
  !---------------------------------------------------------------------
  ! @ public
  ! Convert a Modified Julian Date value to an ISO #8601 date string
  ! of the form:
  !   2015-01-09T08:58:19.230000
  !   123456789 123456789 123456
  !---------------------------------------------------------------------
  real(kind=8),     intent(in)    :: mjd
  character(len=*), intent(out)   :: timestring
  logical,          intent(inout) :: error
  ! local
  character(len=*), parameter :: rname='MJD2ISODATE'
  integer(kind=4) :: iye,imo,ida
  real(kind=8) :: se
  character(len=30) :: isodate  ! Larger than 26 for safety
  !
  call gag_mjd2gregorian(mjd,iye,imo,ida,error)
  if (error)  return
  !
  ! Use an intermediate string of appropriate length
  write(isodate,100) iye,imo,ida
  !
  se = (mjd-floor(mjd))*2.d0*pi  ! Remainder in radians
  call rad2sexa(se,24,isodate(12:),left=.true.)
  !
  ! Copy (may be truncate) the intermediate string to the output string
  timestring = isodate
  !
100 format(I4,'-',I2.2,'-',I2.2,'T')
  !
end subroutine gag_mjd2isodate
!
subroutine gag_mjd2gregorian(mjd,iy,im,id,error)
  use phys_const
  use gsys_interfaces, except_this=>gag_mjd2gregorian
  !---------------------------------------------------------------------
  ! @ private
  ! Modified Julian Date to Gregorian Calendar
  !---------------------------------------------------------------------
  real(kind=8),    intent(in)    :: mjd    ! Modified Julian Date (JD-2400000.5) for 0 hrs
  integer(kind=4), intent(out)   :: iy     ! Year in Gregorian calendar
  integer(kind=4), intent(out)   :: im     ! Month in Gregorian calendar
  integer(kind=4), intent(out)   :: id     ! Day in Gregorian calendar
  logical(kind=4), intent(inout) :: error  ! Logical error flag
  ! Local
  real(kind=8) :: jd,jdf
  integer(kind=4) :: jdi,l,n
  !
  jd = mjdzero_in_jd + floor(mjd)  ! Julian day
  jdi = floor(jd,kind=4)           ! Integer Julian day
  jdf = jd-real(jdi,kind=8)+0.5d0  ! Fractional part of day
  !
  ! Next calendar day?
  if (jdf.ge.1.d0) then
    jdf = jdf-1.d0
    jdi = jdi+1
  endif
  !
  l = jdi + 68569
  n = floor(4*l/146097.d0,kind=4)
  !
  l = l-floor((146097*n+3)/4.d0,kind=4)
  iy = floor(4000*(l+1)/1461001.d0,kind=4)
  !
  l = l-floor(1461*iy/4.d0,kind=4)+31
  im = floor(80*l/2447.d0,kind=4)
  !
  id = l-floor(2447*im/80.d0,kind=4)
  !
  l = floor(im/11.d0,kind=4)
  !
  im = im+2-12*l
  iy = 100*(n-49)+iy+l
  !
end subroutine gag_mjd2gregorian
!
subroutine gag_gagut2mjd(date,ut,mjd,error)
  use phys_const
  !---------------------------------------------------------------------
  ! @ public
  !  Convert a GAG_DATE + UT into a MJD date
  !---------------------------------------------------------------------
  integer(kind=4), intent(in)    :: date   ! [gag]
  real(kind=8),    intent(in)    :: ut     ! [rad] Day starts at 0 radians
  real(kind=8),    intent(out)   :: mjd    !
  logical,         intent(inout) :: error  !
  !
  mjd = gagzero_in_mjd+date+ut/2d0/pi
  !
end subroutine gag_gagut2mjd
!
subroutine gag_mjd2gagut(mjd,date,ut,error)
  use phys_const
  !---------------------------------------------------------------------
  ! @ public
  !  Convert a MJD date into GAG_DATE + UT
  !---------------------------------------------------------------------
  real(kind=8),    intent(in)    :: mjd
  integer(kind=4), intent(out)   :: date
  real(kind=8),    intent(out)   :: ut
  logical,         intent(inout) :: error
  !
  date = floor(mjd)
  ut = 2d0*pi*(mjd-real(date,kind=8))
  date = date-gagzero_in_mjd
  !
end subroutine gag_mjd2gagut
!
subroutine sic_date(datetime)
  use gsys_interfaces, except_this=>sic_date
  !---------------------------------------------------------------------
  ! @ public
  !  Return the current date-time in Gildas format
  !     DD-MMM-YYYY hh:mm:ss.ss
  !     123456789 123456789 123
  ! Use 23 character strings for full date-time, or shorter strings
  ! for truncated values.
  !---------------------------------------------------------------------
  character(len=*), intent(out) :: datetime
  ! Local
  character(len=23) :: good_chain
  !
  call sic_c_datetime(good_chain)
  datetime = good_chain
  call sic_upper(datetime)
end subroutine sic_date
!
subroutine sic_isodate(datetime)
  use gsys_interfaces, except_this=>sic_isodate
  !---------------------------------------------------------------------
  ! @ public
  !  Return the current date-time in ISO #8601:
  !     1996-10-14T10:34:55.345
  !     123456789 123456789 123
  ! Use 23 character strings for full date-time, or shorter strings
  ! for truncated values.
  !---------------------------------------------------------------------
  character(len=*), intent(out) :: datetime
  ! Local
  character(len=23) :: good_chain
  !
  call sic_c_isodatetime(good_chain)
  datetime = good_chain
end subroutine sic_isodate
!
subroutine sic_gagdate(gagdate)
  use gsys_interfaces, except_this=>sic_gagdate
  !---------------------------------------------------------------------
  ! @ public
  !  Return the current UTC date in GAG_DATE format
  !---------------------------------------------------------------------
  integer(kind=4), intent(out) :: gagdate
  ! Local
  integer(kind=4) :: values(6)
  real(kind=4) :: frac
  !
  call sic_c_idatetime(values,frac)
  !
  call gag_datj(values(3),values(2),values(1),gagdate)
  !
end subroutine sic_gagdate
