!= æ—¥æ™‚å·®ã®ç®—å‡º.
!= Evaluate difference of date.
!
! Authors::   Yasuhiro MORIKAWA
! Version::   $Id: dccaldatedifference.f90,v 1.7 2010-09-24 07:07:31 morikawa Exp $
! Tag Name::  $Name:  $
! Copyright:: Copyright (C) GFD Dennou Club, 2009-. All rights reserved.
! License::   See COPYRIGHT[link:../../COPYRIGHT]
!
! ã“ã®ãƒ•ã‚¡ã‚¤ãƒ«ã«è¨˜è¼‰ã•ã‚Œã‚‹æ‰‹ç¶šãç¾¤ã¯ dc_calendar ãƒ¢ã‚¸ãƒ¥ãƒ¼ãƒ«ã‹ã‚‰æä¾›ã•ã‚Œã¾ã™.
!
! Procedures described in this file are provided from "dc_calendar" module.
!
function DCCalDateDifference1( start_date, end_date, cal ) result(sec)
  !
  ! æ—¥æ™‚å·®ã‚’ç®—å‡ºã—ã¾ã™.
  !
  ! çœç•¥å¯èƒ½å¼•æ•° *cal* ãŒçœç•¥ã•ã‚ŒãŸå ´åˆã«ã¯, æ—¥æ™‚å·®ã®ç®—å‡ºã«
  ! dc_calendar å†…éƒ¨ã§ä¿æŒã•ã‚Œã‚‹æš¦ãŒç”¨ã„ã‚‰ã‚Œã¾ã™.
  ! *cal* ãŒçœç•¥ã•ã‚Œãªã„å ´åˆã«ã¯ãã®å¤‰æ•°ã«è¨å®šã•ã‚ŒãŸæš¦ãŒç”¨ã„ã‚‰ã‚Œã¾ã™.
  !
  ! Evaluate difference of date.
  !
  ! If an optional argument *cal* is omitted,
  ! information of calendar that is stored in the "dc_calendar"
  ! is used for evaluation of difference of date.
  ! If *cal* is not omitted, information of the variable is used.
  !

  use dc_calendar_internal, only: default_cal, default_cal_set, &
    & dccaltype_str, dccaldate_str2usym
  use dc_calendar_types, only: DC_CAL, DC_CAL_DATE, &
    & UNIT_SYMBOL_YEAR, UNIT_SYMBOL_MONTH, UNIT_SYMBOL_DAY, &
    & UNIT_SYMBOL_HOUR, UNIT_SYMBOL_MIN, UNIT_SYMBOL_SEC, &
    & CAL_USER_DEFINED, &
    & CAL_CYCLIC, CAL_NOLEAP, CAL_JULIAN, CAL_GREGORIAN, CAL_360DAY
  use dc_error, only: StoreError, DC_NOERR, DC_EBADUNIT, DC_ENOTINIT
  use dc_message, only: MessageNotify
  use dc_trace, only: BeginSub, EndSub
  use dc_types, only: DP, TOKEN, STRING
  implicit none
  real(DP):: sec
                              ! *start_date* ã¨ *end_date* ã¨ã®å·® (ç§’æ•°).
                              ! Difference (seconds) between *start_date* and *end_date*.
  type(DC_CAL_DATE), intent(in):: start_date
                              ! èµ·ç‚¹ã¨ãªã‚‹æ—¥æ™‚.
                              ! Date of origin.
  type(DC_CAL_DATE), intent(in):: end_date
                              ! çµ‚ç‚¹ã¨ãªã‚‹æ—¥æ™‚.
                              ! Date of terminus.
  type(DC_CAL), intent(in), optional, target:: cal
                              ! æš¦æƒ…å ±ã‚’åŽã‚ãŸã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆ.
                              !
                              ! An object that stores information of
                              ! calendar.

  ! ä½œæ¥å¤‰æ•°
  ! Work variables
  !
  type(DC_CAL), pointer:: calp =>null()
  real(DP):: start_year, start_day, start_sec, start_neg_offset_day
  real(DP)::   end_year,   end_day,   end_sec,   end_neg_offset_day
  integer:: day_in_4years, day_in_400years
  integer:: start_year_int, end_year_int
  integer:: i, j
  character(*), parameter:: subname = 'DCCalDateDifference1'
continue
  ! ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã®ãƒã‚¤ãƒ³ã‚¿å‰²ä»˜
  ! Associate pointer of an object
  !
  if ( present( cal ) ) then
    calp => cal
  else
    calp => default_cal
    if ( .not. calp % initialized ) call default_cal_set
  end if

  ! åˆæœŸè¨å®šã®ãƒã‚§ãƒƒã‚¯
  ! Check initialization
  !
  if ( .not. calp % initialized ) then
    sec = 0.0_DP
    return
  end if

  if ( .not. start_date % initialized ) then
    sec = 0.0_DP
    return
  end if

  if ( .not. end_date % initialized ) then
    sec = 0.0_DP
    return
  end if

  start_neg_offset_day = 0
  end_neg_offset_day   = 0

  start_year_int = start_date % year
  end_year_int   = end_date % year

  ! æ—¥ã¸ã®å¤‰æ›
  ! Convert into days
  !
  select case( calp % cal_type )
  case( CAL_JULIAN )

    day_in_4years = 1461

    ! å¹´ãŒè² ã®å ´åˆï¼Œ400 å¹´å˜ä½ã§å¼•ãç®—ã—ä¸‹é§„ã‚’å±¥ã‹ã›ã‚‹
    !
    do while ( start_year_int < 1 )
      start_neg_offset_day =   start_neg_offset_day &
        &                    + day_in_4years * 100
      start_year_int       =   start_year_int &
        &                    + 400
    end do

    ! start_date ã®æ—¥ã¸ã®å¤‰æ›
    ! Convert start_date into days
    !
    if ( ( start_year_int - 1 ) > 4 ) then
      start_day = int( ( start_year_int - 1 ) / 4 ) * day_in_4years
      start_year = mod( start_year_int - 1, 4 ) + 1
    else
      start_day  = 0
      start_year = start_year_int
    end if

    start_day = start_day + ( start_year - 1 ) * sum ( calp % day_in_month(:) )
    do i = 1, start_date % month - 1
      if ( start_year == 4 .and. i == 2 ) then
        start_day = start_day + 29
      else
        start_day = start_day + calp % day_in_month(i)
      end if
    end do
    start_day = start_day + start_date % day

    ! å¹´ãŒè² ã®å ´åˆï¼Œ400 å¹´å˜ä½ã§å¼•ãç®—ã—ä¸‹é§„ã‚’å±¥ã‹ã›ã‚‹
    !
    do while ( end_year_int < 1 )
      end_neg_offset_day =   end_neg_offset_day &
        &                  + day_in_4years * 100
      end_year_int       =   end_year_int &
        &                  + 400
    end do

    ! end_date ã®æ—¥ã¸ã®å¤‰æ›
    ! Convert end_date into days
    !
    if ( ( end_year_int - 1 ) > 4 ) then
      end_day = int( ( end_year_int - 1 ) / 4 ) * day_in_4years
      end_year = mod( end_year_int - 1, 4 ) + 1
    else
      end_day  = 0
      end_year = end_year_int
    end if

    end_day = end_day + ( end_year - 1 ) * sum ( calp % day_in_month(:) )
    do i = 1, end_date % month - 1
      if ( end_year == 4 .and. i == 2 ) then
        end_day = end_day + 29
      else
        end_day = end_day + calp % day_in_month(i)
      end if
    end do
    end_day = end_day + end_date % day

  case( CAL_GREGORIAN )

    day_in_400years = 146097

    ! å¹´ãŒè² ã®å ´åˆï¼Œ400 å¹´å˜ä½ã§å¼•ãç®—ã—ä¸‹é§„ã‚’å±¥ã‹ã›ã‚‹
    !
    do while ( start_year_int < 1 )
      start_neg_offset_day =   start_neg_offset_day &
        &                    + day_in_400years
      start_year_int       =   start_year_int &
        &                    + 400
    end do

    ! start_date ã®æ—¥ã¸ã®å¤‰æ›
    ! Convert start_date into days
    !
    if ( ( start_year_int - 1 ) > 400 ) then
      start_day = int( ( start_year_int - 1 ) / 400 ) * day_in_400years
      start_year = mod( start_year_int - 1, 400 ) + 1
    else
      start_day  = 0
      start_year = start_year_int
    end if

    do j = 1, int( start_year - 1 )
      do i = 1, calp % month_in_year
        if ( i == 2 ) then
          if     ( mod( j, 400 ) == 0 ) then
            start_day = start_day + 29
          elseif ( mod( j, 100 ) == 0 ) then
            start_day = start_day + 28
          elseif ( mod( j, 4   ) == 0 ) then
            start_day = start_day + 29
          else
            start_day = start_day + 28
          end if
        else
          start_day = start_day + calp % day_in_month(i)
        end if
      end do
    end do

    do i = 1, start_date % month - 1
      if ( i == 2 ) then
        if     ( mod( start_year, 400.0_DP ) == 0 ) then
          start_day = start_day + 29
        elseif ( mod( start_year, 100.0_DP ) == 0 ) then
          start_day = start_day + 28
        elseif ( mod( start_year, 4.0_DP   ) == 0 ) then
          start_day = start_day + 29
        else
          start_day = start_day + 28
        end if
      else
        start_day = start_day + calp % day_in_month(i)
      end if
    end do

    start_day = start_day + start_date % day

    ! å¹´ãŒè² ã®å ´åˆï¼Œ400 å¹´å˜ä½ã§å¼•ãç®—ã—ä¸‹é§„ã‚’å±¥ã‹ã›ã‚‹
    !
    do while ( end_year_int < 1 )
      end_neg_offset_day =   end_neg_offset_day &
        &                  + day_in_400years
      end_year_int       =   end_year_int &
        &                  + 400
    end do

    ! end_date ã®æ—¥ã¸ã®å¤‰æ›
    ! Convert end_date into days
    !
    if ( ( end_year_int - 1 ) > 400 ) then
      end_day = int( ( end_year_int - 1 ) / 400 ) * day_in_400years
      end_year = mod( end_year_int - 1, 400 ) + 1
    else
      end_day  = 0
      end_year = end_year_int
    end if

    do j = 1, int( end_year - 1 )
      do i = 1, calp % month_in_year
        if ( i == 2 ) then
          if     ( mod( j, 400 ) == 0 ) then
            end_day = end_day + 29
          elseif ( mod( j, 100 ) == 0 ) then
            end_day = end_day + 28
          elseif ( mod( j, 4   ) == 0 ) then
            end_day = end_day + 29
          else
            end_day = end_day + 28
          end if
        else
          end_day = end_day + calp % day_in_month(i)
        end if
      end do
    end do

    do i = 1, end_date % month - 1
      if ( i == 2 ) then
        if     ( mod( end_year, 400.0_DP ) == 0 ) then
          end_day = end_day + 29
        elseif ( mod( end_year, 100.0_DP ) == 0 ) then
          end_day = end_day + 28
        elseif ( mod( end_year, 4.0_DP   ) == 0 ) then
          end_day = end_day + 29
        else
          end_day = end_day + 28
        end if
      else
        end_day = end_day + calp % day_in_month(i)
      end if
    end do

    end_day = end_day + end_date % day

  case default
    ! start_date ã®æ—¥ã¸ã®å¤‰æ›
    ! Convert start_date into days
    !
    start_day = ( start_year_int - 1 ) * sum ( calp % day_in_month(:) )
    do i = 1, start_date % month - 1
      start_day = start_day + calp % day_in_month(i)
    end do
    start_day = start_day + start_date % day

    ! end_date ã®æ—¥ã¸ã®å¤‰æ›
    ! Convert end_date into days
    !
    end_day = ( end_year_int - 1 ) * sum ( calp % day_in_month(:) )
    do i = 1, end_date % month - 1
      end_day = end_day + calp % day_in_month(i)
    end do
    end_day = end_day + end_date % day
  end select

  ! start_date ã®ç§’ã¸ã®å¤‰æ›
  ! Convert start_date into seconds
  !
  start_sec =   ( start_day - 1 - start_neg_offset_day ) &
      &                           * calp % hour_in_day &
      &                           * calp % min_in_hour &
      &                           * calp % sec_in_min  &
      &       + start_date % hour * calp % min_in_hour &
      &                           * calp % sec_in_min  &
      &       + start_date % min  * calp % sec_in_min  &
      &       + start_date % sec

  ! end_date ã®ç§’ã¸ã®å¤‰æ›
  ! Convert end_date into seconds
  !
  end_sec =   ( end_day - 1 - end_neg_offset_day ) &
      &                       * calp % hour_in_day &
      &                       * calp % min_in_hour &
      &                       * calp % sec_in_min  &
      &     + end_date % hour * calp % min_in_hour &
      &                       * calp % sec_in_min  &
      &     + end_date % min  * calp % sec_in_min  &
      &     + end_date % sec

  ! å·®åˆ†ã®è¨ˆç®—
  ! Calculate difference
  !
  sec = end_sec - start_sec

  ! çµ‚äº†å‡¦ç†, ä¾‹å¤–å‡¦ç†
  ! Termination and Exception handling
  !
999 continue
  nullify( calp )
end function DCCalDateDifference1

!!$
!!$
!!$subroutine DCCalConvertByUnit1( in_time, in_unit, out_unit, out_time, cal, err )
!!$  use dc_calendar_internal, only: default_cal, default_cal_set, &
!!$    & dccaltype_str, dccaldate_str2usym
!!$  use dc_calendar_types, only: DC_CAL, &
!!$    & UNIT_SYMBOL_YEAR, UNIT_SYMBOL_MONTH, UNIT_SYMBOL_DAY, &
!!$    & UNIT_SYMBOL_HOUR, UNIT_SYMBOL_MIN, UNIT_SYMBOL_SEC
!!$  use dc_error, only: StoreError, DC_NOERR, DC_EBADUNIT, DC_ENOTINIT
!!$  use dc_message, only: MessageNotify
!!$  use dc_trace, only: BeginSub, EndSub
!!$  use dc_types, only: DP, TOKEN, STRING
!!$  implicit none
!!$  real(DP), intent(in):: in_time
!!$  character(*), intent(in):: in_unit
!!$  character(*), intent(in):: out_unit
!!$  real(DP), intent(out):: out_time
!!$  type(DC_CAL), intent(in), optional, target:: cal
!!$  logical, intent(out), optional:: err
!!$                              ! ä¾‹å¤–å‡¦ç†ç”¨ãƒ•ãƒ©ã‚°.
!!$                              ! ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã§ã¯, ã“ã®æ‰‹ç¶šãå†…ã§ã‚¨ãƒ©ãƒ¼ãŒ
!!$                              ! ç”Ÿã˜ãŸå ´åˆ, ãƒ—ãƒã‚°ãƒ©ãƒ ã¯å¼·åˆ¶çµ‚äº†ã—ã¾ã™.
!!$                              ! å¼•æ•° *err* ãŒä¸Žãˆã‚‰ã‚Œã‚‹å ´åˆ,
!!$                              ! ãƒ—ãƒã‚°ãƒ©ãƒ ã¯å¼·åˆ¶çµ‚äº†ã›ãš, ä»£ã‚ã‚Šã«
!!$                              ! *err* ã« .true. ãŒä»£å…¥ã•ã‚Œã¾ã™.
!!$                              !
!!$                              ! Exception handling flag.
!!$                              ! By default, when error occur in
!!$                              ! this procedure, the program aborts.
!!$                              ! If this *err* argument is given,
!!$                              ! .true. is substituted to *err* and
!!$                              ! the program does not abort.
!!$
!!$  ! ä½œæ¥å¤‰æ•°
!!$  ! Work variables
!!$  !
!!$  type(DC_CAL), pointer:: calp =>null()
!!$  real(DP):: in_timew
!!$  integer:: in_unit_sym, out_unit_sym
!!$  integer:: stat
!!$  character(STRING):: cause_c
!!$  character(*), parameter:: subname = 'DCCalConvertByUnit1'
!!$continue
!!$  call BeginSub( subname )
!!$  stat = DC_NOERR
!!$  cause_c = ''
!!$
!!$  ! ã‚ªãƒ–ã‚¸ã‚§ã‚¯ãƒˆã®ãƒã‚¤ãƒ³ã‚¿å‰²ä»˜
!!$  ! Associate pointer of an object
!!$  !
!!$  if ( present( cal ) ) then
!!$    calp => cal
!!$  else
!!$    calp => default_cal
!!$    if ( .not. calp % initialized ) call default_cal_set
!!$  end if
!!$
!!$  ! åˆæœŸè¨å®šã®ãƒã‚§ãƒƒã‚¯
!!$  ! Check initialization
!!$  !
!!$  if ( .not. calp % initialized ) then
!!$    stat = DC_ENOTINIT
!!$    cause_c = 'DC_CAL'
!!$    goto 999
!!$  end if
!!$
!!$  ! å˜ä½ã®è§£é‡ˆ
!!$  ! Parse units
!!$  !
!!$  in_unit_sym  = dccaldate_str2usym( in_unit )
!!$  out_unit_sym = dccaldate_str2usym( out_unit )
!!$
!!$  ! æ•°å€¤ã®å¤‰æ›
!!$  ! Convert a value
!!$  !
!!$  select case(in_unit_sym)
!!$  case(UNIT_SYMBOL_DAY)
!!$    in_timew = in_time * calp % hour_in_day &
!!$      &                * calp % min_in_hour &
!!$      &                * calp % sec_in_min
!!$  case(UNIT_SYMBOL_HOUR)
!!$    in_timew = in_time * calp % min_in_hour &
!!$      &                * calp % sec_in_min
!!$  case(UNIT_SYMBOL_MIN)
!!$    in_timew = in_time * calp % sec_in_min
!!$  case(UNIT_SYMBOL_SEC)
!!$    in_timew = in_time
!!$  case default
!!$    cause_c = in_unit
!!$    call MessageNotify('W', subname, 'in_unit=<%c> is invalid. (ONLY day,hour,min,sec are valid)', &
!!$      & c1 = trim(in_unit) )
!!$    stat = DC_EBADUNIT
!!$    goto 999
!!$  end select
!!$
!!$  select case(out_unit_sym)
!!$  case(UNIT_SYMBOL_DAY)
!!$    out_time = in_timew / calp % hour_in_day &
!!$      &                 / calp % min_in_hour &
!!$      &                 / calp % sec_in_min
!!$  case(UNIT_SYMBOL_HOUR)
!!$    out_time = in_timew / calp % min_in_hour &
!!$      &                 / calp % sec_in_min
!!$  case(UNIT_SYMBOL_MIN)
!!$    out_time = in_timew / calp % sec_in_min
!!$  case(UNIT_SYMBOL_SEC)
!!$    out_time = in_timew
!!$  case default
!!$    cause_c = out_unit
!!$    call MessageNotify('W', subname, 'out_unit=<%c> is invalid. (ONLY day,hour,min,sec are valid)', &
!!$      & c1 = trim(out_unit) )
!!$    stat = DC_EBADUNIT
!!$    goto 999
!!$  end select
!!$
!!$  ! çµ‚äº†å‡¦ç†, ä¾‹å¤–å‡¦ç†
!!$  ! Termination and Exception handling
!!$  !
!!$999 continue
!!$  nullify( calp )
!!$  call StoreError( stat, subname, err, cause_c )
!!$  call EndSub( subname )
!!$end subroutine DCCalConvertByUnit1