module datetime_params
  !
  ! Options description
  integer(kind=4), parameter :: optfrom=1
  integer(kind=4), parameter :: optto=2
  !
  ! Date and time formats
  integer(kind=4), parameter :: mformats=14
  character(len=11), parameter :: formats(mformats) =  &
    (/ 'YEAR       ','MONTH      ','DAY        ','HOUR       ','MINUTE     ',  &
       'SECONDS    ','JULIAN     ','MJD        ','ISO        ','GAG_DATE   ',  &
       'YYYYMMDD   ','DD-MMM-YYYY','RADIAN     ','SEXAGESIMAL' /)
  !
  integer(kind=4), parameter :: mfield=6  ! year, month, day, hour, minutes, seconds
  !
  ! The order of the 6 first matters
  integer(kind=4), parameter :: fmt_year       = 1
  integer(kind=4), parameter :: fmt_month      = 2
  integer(kind=4), parameter :: fmt_day        = 3
  integer(kind=4), parameter :: fmt_hour       = 4
  integer(kind=4), parameter :: fmt_minute     = 5
  integer(kind=4), parameter :: fmt_seconds    = 6
  integer(kind=4), parameter :: fmt_julian     = 7
  integer(kind=4), parameter :: fmt_mjd        = 8
  integer(kind=4), parameter :: fmt_iso        = 9
  integer(kind=4), parameter :: fmt_gagdate    = 10
  integer(kind=4), parameter :: fmt_yyyymmdd   = 11
  integer(kind=4), parameter :: fmt_ddmmmyyyy  = 12
  integer(kind=4), parameter :: fmt_radian     = 13
  integer(kind=4), parameter :: fmt_sexagesimal= 14
  !
end module datetime_params
!
subroutine sic_datetime(line,error)
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_datetime
  use sic_types
  use datetime_params
  !---------------------------------------------------------------------
  ! @ private
  !  Support routine for command
  !    DATETIME InValue1 [Invalue2] OutFormat Outvar1 [Outvar2]
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: line
  logical,          intent(inout) :: error
  ! Local
  integer(kind=size_length) :: nelem
  ! /FROM: input variables
  integer(kind=4), parameter :: minput=mfield  ! By construction there are at most 6 input sources
  integer(kind=4) :: ninput
  type(datetime_spec_t) :: in(minput)
  ! /TO: output variables
  integer(kind=4), parameter :: moutput=mformats  ! Arbitrary (no limit in the
    ! design), use mformats so that we can test all output formats at once.
  integer(kind=4) :: noutput
  type(datetime_spec_t) :: out(moutput)
  !
  ninput = 0
  noutput = 0
  !
  call sic_datetime_parse(line,in,ninput,out,noutput,nelem,error)
  if (error)  goto 10
  !
  call sic_datetime_do(in,ninput,out,noutput,nelem,error)
  if (error)  goto 10
  !
10 continue
  call sic_datetime_clean(in,ninput,out,noutput,error)
  if (error)  return
  !
end subroutine sic_datetime
!
subroutine sic_datetime_parse(line,in,ninput,out,noutput,nelem,error)
  use gbl_format
  use phys_const
  use gbl_message
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_datetime_parse
  use sic_types
  use datetime_params
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  character(len=*),          intent(in)    :: line     !
  type(datetime_spec_t),     intent(out)   :: in(:)    !
  integer(kind=4),           intent(inout) :: ninput   ! Useful size of in(:)
  type(datetime_spec_t),     intent(out)   :: out(:)   !
  integer(kind=4),           intent(inout) :: noutput  ! Useful size of out(:)
  integer(kind=size_length), intent(out)   :: nelem    ! Size of problem
  logical,                   intent(inout) :: error    ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='DATETIME'
  character(len=16) :: argum,forma
  integer(kind=4) :: nc,iforma,iarg
  integer(kind=4) :: i,shift(mfield)
  character(len=varname_length) :: varname
  integer(kind=size_length), parameter :: one=1_8
  logical :: got(mfield)
  integer(kind=4), parameter :: scratch=-1
  !
  ! Sanity checks
  if (.not.sic_present(optfrom,0) .or. .not.sic_present(optto,0)) then
    call sic_message(seve%e,rname,'Options /FROM and /TO must be present')
    error = .true.
    return
  endif
  !
  !---------------------------------------------------------------------
  ! /FROM
  !
  ninput = 0
  got(:) = .false.
  !
  ! 1st argument?
  call sic_ke(line,optfrom,1,argum,nc,.true.,error)
  if (error)  return
  !
  if (argum.eq.'NOW') then
    ! Get current date-time
    if (sic_present(optfrom,2)) then
      call sic_message(seve%e,rname,'No more arguments allowed after keyword "NOW"')
      error = .true.
      return
    endif
    shift(:) = 0
    call getnow(shift)
    !
  else
    !
    do iarg=1,sic_narg(optfrom),2
      call sic_ke(line,optfrom,iarg+1,argum,nc,.true.,error)
      if (error)  return
      call sic_ambigs(rname,argum,forma,iforma,formats,mformats,error)
      if (error)  return
      !
      ninput = ninput+1
      in(ninput)%isvar = .true.  ! Always for input
      in(ninput)%form = iforma
      !
      select case (forma)
      case ('YEAR')
        if (setgot_error(fmt_year,error)) return
        call sic_prevnexti4(iarg,fmt_year,in(ninput)%desc,error)
        if (error)  return
        !
      case ('MONTH')
        if (setgot_error(fmt_month,error)) return
        call sic_prevnexti4(iarg,fmt_month,in(ninput)%desc,error)
        if (error)  return
        !
      case ('DAY')
        if (setgot_error(fmt_day,error)) return
        call sic_prevnexti4(iarg,fmt_day,in(ninput)%desc,error)
        if (error)  return
        !
      case ('HOUR')
        if (setgot_error(fmt_hour,error)) return
        call sic_prevnexti4(iarg,fmt_hour,in(ninput)%desc,error)
        if (error)  return
        !
      case ('MINUTE')
        if (setgot_error(fmt_minute,error)) return
        call sic_prevnexti4(iarg,fmt_minute,in(ninput)%desc,error)
        if (error)  return
        !
      case ('SECONDS')
        if (setgot_error(fmt_seconds,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        !
      case ('JULIAN')
        if (setgot_error(fmt_year,error)) return
        if (setgot_error(fmt_month,error)) return
        if (setgot_error(fmt_day,error)) return
        if (setgot_error(fmt_hour,error)) return
        if (setgot_error(fmt_minute,error)) return
        if (setgot_error(fmt_seconds,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        !
      case ('MJD')
        if (setgot_error(fmt_year,error)) return
        if (setgot_error(fmt_month,error)) return
        if (setgot_error(fmt_day,error)) return
        if (setgot_error(fmt_hour,error)) return
        if (setgot_error(fmt_minute,error)) return
        if (setgot_error(fmt_seconds,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        !
      case ('ISO')
        if (setgot_error(fmt_year,error)) return
        if (setgot_error(fmt_month,error)) return
        if (setgot_error(fmt_day,error)) return
        if (setgot_error(fmt_hour,error)) return
        if (setgot_error(fmt_minute,error)) return
        if (setgot_error(fmt_seconds,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        if (in(ninput)%desc%type.le.0) then
          call sic_message(seve%e,rname,'/FROM ISO argument must be a character string')
          error = .true.
          return
        endif
        !
      case ('GAG_DATE')
        if (setgot_error(fmt_year,error)) return
        if (setgot_error(fmt_month,error)) return
        if (setgot_error(fmt_day,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        if (in(ninput)%desc%type.ne.fmt_i4 .and. in(ninput)%desc%type.ne.fmt_i8) then
          call sic_message(seve%e,rname,'/FROM GAG_DATE argument must be integer')
          error = .true.
          return
        endif
        !
      case ('YYYYMMDD')
        if (setgot_error(fmt_year,error)) return
        if (setgot_error(fmt_month,error)) return
        if (setgot_error(fmt_day,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        if (in(ninput)%desc%type.le.0) then
          call sic_message(seve%e,rname,'/FROM YYYYMMDD argument must be a character string')
          error = .true.
          return
        endif
        !
      case ('DD-MMM-YYYY')
        if (setgot_error(fmt_year,error)) return
        if (setgot_error(fmt_month,error)) return
        if (setgot_error(fmt_day,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        if (in(ninput)%desc%type.le.0) then
          call sic_message(seve%e,rname,'/FROM DD-MMM-YYYY argument must be a character string')
          error = .true.
          return
        endif
        !
      case ('RADIAN')
        if (setgot_error(fmt_hour,error)) return
        if (setgot_error(fmt_minute,error)) return
        if (setgot_error(fmt_seconds,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        !
      case ('SEXAGESIMAL')
        if (setgot_error(fmt_hour,error)) return
        if (setgot_error(fmt_minute,error)) return
        if (setgot_error(fmt_seconds,error)) return
        call sic_de(line,optfrom,iarg,in(ninput)%desc,.true.,error)
        if (error)  return
        if (in(ninput)%desc%type.le.0) then
          call sic_message(seve%e,rname,'/FROM SEXAGESIMAL argument must be a character string')
          error = .true.
          return
        endif
        !
      end select
      !
    enddo  ! iarg
  endif  ! Not NOW
  !
  nelem = one  ! Size of (vectorized) problem
  do i=1,ninput
    in(i)%nelem = desc_nelem(in(i)%desc)
    if (nelem.eq.1) then
      nelem = in(i)%nelem
    elseif (in(i)%nelem.eq.1) then
      ! Found a scalar variable: accepted for combination with array variables
      continue
    elseif (in(i)%nelem.ne.nelem) then
      call sic_message(seve%e,rname,'Input dimensions mismatch')
      error = .true.
      return
    endif
  enddo
  !
  !---------------------------------------------------------------------
  ! /TO
  !
  noutput = 0
  do iarg=1,sic_narg(optto),2
    call sic_ch(line,optto,iarg,varname,nc,.true.,error)
    if (error)  return
    call sic_ke(line,optto,iarg+1,argum,nc,.true.,error)
    if (error)  return
    call sic_ambigs(rname,argum,forma,iforma,formats,mformats,error)
    if (error)  return
    !
    noutput = noutput+1
    if (noutput.gt.size(out)) then
      call sic_message(seve%e,rname,'Too many output variables')
      error = .true.
      return
    endif
    out(noutput)%form = iforma
    !
    out(noutput)%isvar = varname.ne.'*'
    if (out(noutput)%isvar) then
      ! Store result in a variable
      call sic_de(line,optto,iarg,out(noutput)%desc,.true.,error)
      if (error)  return
      if (out(noutput)%desc%status.eq.scratch) then
        call sic_message(seve%e,rname,  &
          '/TO '//trim(forma)//': scratch variables are not allowed')
        error = .true.
        return
      endif
      if (out(noutput)%desc%readonly) then
        call sic_message(seve%e,rname,  &
          '/TO '//trim(forma)//': readonly variables can not be modified')
        error = .true.
        return
      endif
      !
      select case (iforma)
      case (fmt_year,fmt_month,fmt_day,fmt_hour,fmt_minute,fmt_gagdate)
        if (out(noutput)%desc%type.ne.fmt_i4 .and. out(noutput)%desc%type.ne.fmt_i8) then
          ! For symmetry with /FROM, see comments there.
          call sic_message(seve%e,rname,  &
            '/TO '//trim(forma)//' variable must be integer')
          error = .true.
          return
        endif
      case (fmt_iso,fmt_yyyymmdd,fmt_ddmmmyyyy,fmt_sexagesimal)
        if (out(noutput)%desc%type.le.0) then
          call sic_message(seve%e,rname,  &
            '/TO '//trim(forma)//' variable must be character string')
          error = .true.
          return
        endif
      end select
      !
    else
      ! Output is *, i.e. terminal, as many times as there are elements
      out(noutput)%desc%ndim = 1  ! For computing nelem below
      out(noutput)%desc%dims(1) = nelem
    endif
    !
    out(noutput)%nelem = desc_nelem(out(noutput)%desc)
    if (out(noutput)%nelem.ne.nelem) then
      call sic_message(seve%e,rname,'Input/output dimensions mismatch')
      error = .true.
      return
    endif
    !
  enddo
  !
contains
  !
  subroutine getnow(shift)
    !-------------------------------------------------------------------
    ! Create 6 scratch variables describing "now", more or less a shift
    ! on some fields
    !-------------------------------------------------------------------
    integer(kind=4), intent(in) :: shift(mfield)
    ! Global
    include 'gbl_memory.inc'
    ! Local
    integer(kind=4) :: values(6),i,i4
    real(kind=4) :: subsec
    real(kind=8) :: r8
    !
    call sic_c_idatetime(values,subsec)
    !
    ! Year, Month, Day, Hour, Minute are I*4
    do i=1,5
      in(i)%form = i
      i4 = values(i)+shift(i)
      call sic_incarnate(i4,in(i)%desc,error)
      if (error)  return
    enddo
    !
    ! Seconds is R*8
    in(6)%form = fmt_seconds
    r8 = values(6)+shift(6)+subsec
    call sic_incarnate(r8,in(6)%desc,error)
    if (error)  return
    !
    ninput = mfield
    got(:) = .true.
  end subroutine getnow
  !
  function setgot_error(igot,error)
    !-------------------------------------------------------------------
    ! Set got(igot) to .true., if not already .true.. Else return with
    ! error flag
    !-------------------------------------------------------------------
    logical :: setgot_error
    integer(kind=4), intent(in)    :: igot
    logical,         intent(inout) :: error
    if (got(igot)) then
      call sic_message(seve%e,rname,trim(formats(igot))//' is specified twice')
      error = .true.
    endif
    got(igot) = .true.
    setgot_error = error
  end function setgot_error
  !
  subroutine sic_prevnexti4(iarg,ifield,desc,error)
    !-------------------------------------------------------------------
    ! Decode PREVIOUS, NEXT, or integer value
    !-------------------------------------------------------------------
    integer(kind=4),        intent(in)    :: iarg    ! Position on command line
    integer(kind=4),        intent(in)    :: ifield  ! 1 year, 2 month, ...
    type(sic_descriptor_t), intent(out)   :: desc    !
    logical,                intent(inout) :: error   ! Logical error flag
    ! Local
    character(len=8), parameter :: prevnext(2) = (/ 'PREVIOUS','NEXT    ' /)
    integer(kind=4) :: ikey,nc
    character(len=16) :: argum
    character(len=8) :: key
    integer(kind=4) :: shift(mfield)
    !
    call sic_ke(line,optfrom,iarg,argum,nc,.true.,error)
    if (error)  return
    call sic_ambigs_sub(rname,argum,key,ikey,prevnext,2,error)
    if (error)  return
    !
    if (ikey.eq.0) then
      call sic_de(line,optfrom,iarg,desc,.true.,error)
      if (error)  return
      if (in(ninput)%desc%type.ne.fmt_i4 .and. in(ninput)%desc%type.ne.fmt_i8) then
        ! What would be the meaning of /FROM 2015.5 YEAR 6 MONTH ?
        ! + Beware of implicit rounding if we accept floats
        call sic_message(seve%e,rname,'/FROM '//trim(formats(ifield))//' argument must be integer')
        error = .true.
        return
      endif
    elseif (ikey.eq.1) then  ! PREVIOUS
      shift(:) = 0
      shift(ifield) = -1
      call getnow(shift)
    elseif (ikey.eq.2) then  ! NEXT
      shift(:) = 0
      shift(ifield) = +1
      call getnow(shift)
    endif
  end subroutine sic_prevnexti4
  !
end subroutine sic_datetime_parse
!
subroutine sic_datetime_do(in,ninput,out,noutput,nelem,error)
  use gbl_format
  use phys_const
  use gbl_message
  use sic_dependencies_interfaces
  use sic_interfaces, except_this=>sic_datetime_do
  use sic_types
  use datetime_params
  !---------------------------------------------------------------------
  ! @ private
  !---------------------------------------------------------------------
  type(datetime_spec_t),     intent(in)    :: in(:)
  integer(kind=4),           intent(in)    :: ninput
  type(datetime_spec_t),     intent(in)    :: out(:)
  integer(kind=4),           intent(in)    :: noutput
  integer(kind=size_length), intent(in)    :: nelem
  logical,                   intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='DATETIME'
  character(len=80) :: strval
  integer(kind=4) :: ier,i,gagdate
  integer(kind=size_length) :: ielem,ival
  integer(kind=4) :: year,month,day,hour,minute
  real(kind=8) :: seconds,mjd,ut,r8
  character(len=message_length) :: mess
  !
  do ielem=1,nelem
    !
    ! Default. These defaults are important, e.g.
    !  /FROM 2016 YEAR         is understood as 2016-01-01 00:00:00.000
    !  /FROM 2016 YEAR 2 MONTH is understood as 2016-02-01 00:00:00.000
    !  /FROM 2016 YEAR 100 DAY is understood as 2016-04-09 00:00:00.000 (100th day of year)
    year = 1970
    month = 1
    day = 1
    hour = 0
    minute = 0
    seconds = 0.d0
    !
    ! /FROM
    !
    do i=1,ninput
      if (in(i)%nelem.eq.1) then
        ival = 1
      else
        ival = ielem
      endif
      !
      select case (in(i)%form)
      case (fmt_year)
        call sic_descriptor_getval(in(i)%desc,ival,year,error)
        if (error)  return
        !
      case (fmt_month)
        call sic_descriptor_getval(in(i)%desc,ival,month,error)
        if (error)  return
        !
      case (fmt_day)
        call sic_descriptor_getval(in(i)%desc,ival,day,error)
        if (error)  return
        !
      case (fmt_hour)
        call sic_descriptor_getval(in(i)%desc,ival,hour,error)
        if (error)  return
        !
      case (fmt_minute)
        call sic_descriptor_getval(in(i)%desc,ival,minute,error)
        if (error)  return
        !
      case (fmt_seconds)
        call sic_descriptor_getval(in(i)%desc,ival,seconds,error)
        if (error)  return
        !
      case (fmt_julian)
        call sic_descriptor_getval(in(i)%desc,ival,mjd,error)
        if (error)  return
        mjd = mjd - mjdzero_in_jd  ! From JD to MJD
        call gag_mjd2gregorian(mjd,year,month,day,error)
        if (error)  return
        call deg2dms((mjd-floor(mjd))*24.d0,hour,minute,seconds)
        !
      case (fmt_mjd)
        call sic_descriptor_getval(in(i)%desc,ival,mjd,error)
        if (error)  return
        call gag_mjd2gregorian(mjd,year,month,day,error)
        if (error)  return
        call deg2dms((mjd-floor(mjd))*24.d0,hour,minute,seconds)
        !
      case (fmt_iso)
        call sic_descriptor_getval(in(i)%desc,ival,strval,error)
        if (error)  return
        read(strval( 1: 4),'(i4)',iostat=ier)  year
        if (ier.ne.0) goto 100
        read(strval( 6: 7),'(i2)',iostat=ier)  month
        if (ier.ne.0) goto 100
        read(strval( 9:10),'(i2)',iostat=ier)  day
        if (ier.ne.0) goto 100
        read(strval(12:13),'(i2)',iostat=ier)  hour
        if (ier.ne.0) goto 100
        read(strval(15:16),'(i2)',iostat=ier)  minute
        if (ier.ne.0) goto 100
        read(strval(18:23),*,     iostat=ier)  seconds
        if (ier.ne.0) goto 100
        !
      case (fmt_gagdate)
        call sic_descriptor_getval(in(i)%desc,ival,gagdate,error)
        if (error)  return
        call gag_jdat(gagdate,day,month,year)
        !
      case (fmt_yyyymmdd)
        call sic_descriptor_getval(in(i)%desc,ival,strval,error)
        if (error)  return
        read(strval(1:4),'(i4)',iostat=ier)  year
        if (ier.ne.0) goto 100
        read(strval(5:6),'(i2)',iostat=ier)  month
        if (ier.ne.0) goto 100
        read(strval(7:8),'(i2)',iostat=ier)  day
        if (ier.ne.0) goto 100
        !
      case (fmt_ddmmmyyyy)
        call sic_descriptor_getval(in(i)%desc,ival,strval,error)
        if (error)  return
        call gag_fromdate(strval,gagdate,error)
        if (error) return
        call gag_jdat(gagdate,day,month,year)
        !
      case (fmt_radian)
        call sic_descriptor_getval(in(i)%desc,ival,r8,error)
        if (error)  return
        call deg2dms(r8*12.d0/pi,hour,minute,seconds)
        !
      case (fmt_sexagesimal)
        call sic_descriptor_getval(in(i)%desc,ival,strval,error)
        if (error)  return
        call sic_sexa(strval,len_trim(strval),r8,error)
        if (error) then
          call sic_message(seve%e,rname,  &
            'Invalid sexagesimal string "'//trim(strval)//'"')
          return
        endif
        call deg2dms(r8,hour,minute,seconds)
        !
      end select
    enddo  ! ninput
    !
    ! Merge year+month+day+hour+minute+seconds
    ! Because these quantities may be beyond their usual ranges, we add them
    ! crudely and go back:
    call gag_gregorian2mjd(year,month,1,mjd,ier)  ! Add year + month only (1st day)
    if (ier.ne.0) then
      call sic_message(seve%e,rname,'Error in year and/or month')
      error = .true.
      return
    endif
    mjd = mjd + day-1 + hour/24.d0 + minute/1440.d0 + seconds/86400.d0
    !
    ! /TO
    !
    if (.not.all(out(1:noutput)%isvar))  mess = ""
    do i=1,noutput
      !
      select case (out(i)%form)
      case (fmt_year)
        call gag_mjd2gregorian(mjd,year,month,day,error)
        if (error)  return
        call sic_datetime_setvalue(out(i),ielem,year,mess,error)
        if (error)  return
        !
      case (fmt_month)
        call gag_mjd2gregorian(mjd,year,month,day,error)
        if (error)  return
        call sic_datetime_setvalue(out(i),ielem,month,mess,error)
        if (error)  return
        !
      case (fmt_day)
        call gag_mjd2gregorian(mjd,year,month,day,error)
        if (error)  return
        call sic_datetime_setvalue(out(i),ielem,day,mess,error)
        if (error)  return
        !
      case (fmt_hour)
        call deg2dms((mjd-floor(mjd))*24.d0,hour,minute,seconds)
        call sic_datetime_setvalue(out(i),ielem,hour,mess,error)
        if (error)  return
        !
      case (fmt_minute)
        call deg2dms((mjd-floor(mjd))*24.d0,hour,minute,seconds)
        call sic_datetime_setvalue(out(i),ielem,minute,mess,error)
        if (error)  return
        !
      case (fmt_seconds)
        call deg2dms((mjd-floor(mjd))*24.d0,hour,minute,seconds)
        call sic_datetime_setvalue(out(i),ielem,seconds,mess,error)
        if (error)  return
        !
      case (fmt_mjd)
        call sic_datetime_setvalue(out(i),ielem,mjd,mess,error)
        if (error)  return
        !
      case (fmt_julian)
        call sic_datetime_setvalue(out(i),ielem,mjdzero_in_jd+mjd,mess,error)
        if (error)  return
        !
      case (fmt_iso)
        call gag_mjd2isodate(mjd,strval,error)
        if (error)  return
        call sic_datetime_setvalue(out(i),ielem,strval,mess,error)
        if (error)  return
        !
      case (fmt_gagdate)
        call gag_mjd2gagut(mjd,gagdate,ut,error)
        if (error)  return
        call sic_datetime_setvalue(out(i),ielem,gagdate,mess,error)
        if (error)  return
        !
      case (fmt_yyyymmdd)
        call gag_mjd2gagut(mjd,gagdate,ut,error)
        if (error)  return
        call gag_toyyyymmdd(gagdate,strval,error)
        if (error)  return
        call sic_datetime_setvalue(out(i),ielem,strval,mess,error)
        if (error)  return
        !
      case (fmt_ddmmmyyyy)
        call gag_mjd2gagut(mjd,gagdate,ut,error)
        if (error)  return
        call gag_todate(gagdate,strval,error)
        if (error)  return
        call sic_datetime_setvalue(out(i),ielem,strval,mess,error)
        if (error)  return
        !
      case (fmt_radian)
        call gag_mjd2gagut(mjd,gagdate,ut,error)
        if (error)  return
        call sic_datetime_setvalue(out(i),ielem,ut,mess,error)
        if (error)  return
        !
      case (fmt_sexagesimal)
        call gag_mjd2gagut(mjd,gagdate,ut,error)
        if (error)  return
        call rad2sexa(ut,24,strval,left=.true.)
        call sic_datetime_setvalue(out(i),ielem,strval,mess,error)
        if (error)  return
        !
      end select
    enddo  ! noutput
    !
    if (.not.all(out(1:noutput)%isvar))  call sic_message(seve%r,rname,mess)
    !
  enddo  ! nelem
  return
  !
  ! Read error
100 continue
  call sic_message(seve%e,rname,'Read error for value "'//trim(strval)//'"')
  error = .true.
  !
end subroutine sic_datetime_do
!
subroutine sic_datetime_setvalue_i4(out,ielem,i4,obuf,error)
  use gildas_def
  use sic_interfaces, except_this=>sic_datetime_setvalue_i4
  use sic_types
  !---------------------------------------------------------------------
  ! @ private-generic sic_datetime_setvalue
  !---------------------------------------------------------------------
  type(datetime_spec_t),     intent(in)    :: out
  integer(kind=size_length), intent(in)    :: ielem
  integer(kind=4),           intent(in)    :: i4
  character(len=*),          intent(inout) :: obuf
  logical,                   intent(inout) :: error
  ! Local
  integer(kind=4) :: nc
  !
  if (out%isvar) then
    ! Write in out%desc
    call sic_descriptor_fill(out%desc,ielem,i4,error)
  else
    ! Write in obuf
    nc = len_trim(obuf)
    write(obuf(nc+2:),'(I0)') i4
  endif
end subroutine sic_datetime_setvalue_i4
!
subroutine sic_datetime_setvalue_r8(out,ielem,r8,obuf,error)
  use gildas_def
  use sic_interfaces, except_this=>sic_datetime_setvalue_r8
  use sic_types
  !---------------------------------------------------------------------
  ! @ private-generic sic_datetime_setvalue
  !---------------------------------------------------------------------
  type(datetime_spec_t),     intent(in)    :: out
  integer(kind=size_length), intent(in)    :: ielem
  real(kind=8),              intent(in)    :: r8
  character(len=*),          intent(inout) :: obuf
  logical,                   intent(inout) :: error
  ! Local
  integer(kind=4) :: nc
  !
  if (out%isvar) then
    ! Write in out%desc
    call sic_descriptor_fill(out%desc,ielem,r8,error)
  else
    ! Write in obuf
    nc = len_trim(obuf)
    write(obuf(nc+2:),'(1PG25.16)') r8
  endif
end subroutine sic_datetime_setvalue_r8
!
subroutine sic_datetime_setvalue_ch(out,ielem,ch,obuf,error)
  use gildas_def
  use sic_interfaces, except_this=>sic_datetime_setvalue_ch
  use sic_types
  !---------------------------------------------------------------------
  ! @ private-generic sic_datetime_setvalue
  !---------------------------------------------------------------------
  type(datetime_spec_t),     intent(in)    :: out
  integer(kind=size_length), intent(in)    :: ielem
  character(len=*),          intent(in)    :: ch
  character(len=*),          intent(inout) :: obuf
  logical,                   intent(inout) :: error
  ! Local
  integer(kind=4) :: nc
  !
  if (out%isvar) then
    ! Write in out%desc
    call sic_descriptor_fill(out%desc,ielem,ch,error)
  else
    ! Write in obuf
    nc = len_trim(obuf)
    write(obuf(nc+2:),'(A)') ch
  endif
end subroutine sic_datetime_setvalue_ch
!
subroutine sic_datetime_clean(in,ninput,out,noutput,error)
  use sic_dependencies_interfaces
  use sic_types
  !---------------------------------------------------------------------
  ! @ private
  !  Clean the temporaries created for command execution
  !---------------------------------------------------------------------
  type(datetime_spec_t), intent(inout) :: in(:)    !
  integer(kind=4),       intent(in)    :: ninput   ! Useful size of in(:)
  type(datetime_spec_t), intent(inout) :: out(:)   !
  integer(kind=4),       intent(in)    :: noutput  ! Useful size of out(:)
  logical,               intent(inout) :: error    !
  ! Local
  integer(kind=4) :: i
  !
  do i=1,ninput
    call sic_volatile(in(i)%desc)
  enddo
  !
  do i=1,noutput
    ! There can be scratch variable in case of user mistake, e.g. /TO 3.14 RADIAN
    call sic_volatile(out(i)%desc)
  enddo
  !
end subroutine sic_datetime_clean
