Search code examples
timefortrannetcdffortran90

Fortran rounding decimals to .0 from netcdf file


I have a fortran 90 function that is meant to parse a time stamp in the form day as %Y%m%d.%f where %f is fraction of a day /48 and return an array with year, month, day, fraction of day.

function parseTimeStamp (timeStamp)

    implicit none

    real, dimension(5) :: parseTimeStamp
    real, intent(in)   :: timeStamp
    real               :: date, moment
    integer            :: intdate, day, month, year, t

    date = floor(timeStamp) ! remove the part days
    parseTimeStamp(4) = timeStamp - date ! save the part days

    intdate = int(date)
    day = mod(intdate,100);   intdate = intdate / 100
    month = mod(intdate,100); intdate = intdate / 100
    year = intdate
    parseTimeStamp(1) = real(year)
    parseTimeStamp(2) = real(month)
    parseTimeStamp(3) = real(day)

  end function parseTimeStamp

The issue is the output shows fraction of the day always at 0. When printing the timestamp (!print *, timeStamp) I get the date without fraction of the day 48 times before rolling over to the next day, even when I know with 100% certainty the data being read contains the proper fraction.

ex: I am getting

20220101.0 20220101.0 20220101.0 .... 20220102.0 20220102.0 ...

instead of

20220101.0 20220101.02083 20220101.04167 .... 20220102.0 20220102.02083 ...

I've tried using several different input files and have confirmed that the input files contain the part day data.


Solution

  • I think you would be better creating a user-defined type to hold your date and time variables than to try to return an array. Year, month and day are more naturally whole numbers (integer type). If you want higher precision use kind=real64 from iso_fortran_env (worth looking up: it has other goodies in that make codes portable).

    program test
       use iso_fortran_env
       implicit none
       integer y, m, d           ! year, month, day
       real(real64) f            ! fraction of a day
    
       call parseTimeStamp( "20220101.02083", y, m, d, f )
       write( *, "( 3(a, ': ', i0, :, / ) )" ) "year", y, "month", m, "day", d
       write( *, "( a, ': ', f7.5 )" ) "fraction", f
    
    contains
    
       subroutine parseTimeStamp( dateTime, year, month, day, dayfraction )
          character(len=*), intent(in) :: dateTime
          integer, intent(out) :: year, month, day
          real(real64), intent(out) :: dayfraction
          real(real64) temp
    
          ! *** TO DO *** check that timestamp is well formed before anything else
    
          read( dateTime(1:4), * ) year
          read( dateTime(5:6), * ) month
          read( dateTime(7: ), * ) temp
          day = temp
          dayfraction = temp - day
    
       end subroutine parseTimeStamp
    
    end program test
    
    year: 2022
    month: 1
    day: 1
    fraction: 0.02083
    

    If you want to go with a user-defined type then you can return that from a function:

    module timemod
       use iso_fortran_env
       implicit none
    
       type DTime
          integer year
          integer month
          integer day
          real(real64) fraction  ! fraction of a day
       end type DTime
    
    contains
    
       type(DTime) function parseTimeStamp( dateStamp ) result( res )
          character(len=*), intent(in) :: dateStamp
          real(real64) temp
    
          read( dateStamp(1:4), * ) res%year
          read( dateStamp(5:6), * ) res%month
          read( dateStamp(7: ), * ) temp
          res%day = temp
          res%fraction = temp - res%day
    
       end function parseTimeStamp
    
    end module timemod
    
    !=======================================================================
    
    program test
       use iso_fortran_env
       use timemod
       implicit none
    
       write( *, "( 3( i0, / ), f7.5 )" ) parseTimeStamp( "20220101.02083" )
    
    end program test