Date and Time

Modern Fortran features some basic date and time routines. On POSIX-compliant systems, ISO C binding interface may be implemented to access additional timing routines:

Time

Most compilers provide a time function through extensions. These implementations are compiler-depended and not part of any Fortran standard. The GNU Fortran function time() returns a 32-bit timestamp, much like the POSIX function time(3):

! time.f90
program main
    implicit none

    print '(i0)', time()  ! 32 bit
    print '(i0)', time8() ! 64 bit
end program main

The non-standard function time8() returns a 64-bit integer instead.

Date and Time

The routine date_and_time(date, time, zone, values) returns date and time information from the real-time system clock.

call date_and_time([date][, time][, zone][, values])
Argument Type Description
date character(len=8) Date in form ccyymmdd.
time character(len=10) Time in form hhmmss.sss.
zone character(len=5) Zone in form (+-)hhmm, representing the difference with respect to UTC.
values integer(8) Array with year (1), month (2), day of month (3), time difference with UTC in minutes (4), hour (5), minutes (6), seconds (7), and milliseconds (8).

The following example prints the current date and time in UTC and in Swatch Internet Time (.beats):

! beats.f90
program main
    implicit none
    integer :: dt(8)
    real    :: beats

    call date_and_time(values=dt)
    print '(i4, 5(a, i2.2), " UTC")', dt(1), '/', dt(2), '/', dt(3), ' ', &
                                      dt(5), ':', dt(6), ':', dt(7)

    beats = (dt(7) + ((dt(6) - dt(4) + 60) * 60) + (dt(5) * 3600)) / 86.4
    print '("Beats: @", f0.2)', beats
end program main

Running the executable outputs:

$ gfortran10 -o beats beats.f90
$ ./beats
2019/08/12 21:50:23 UTC
Beats: @868.32

CPU Time

The intrinsic Fortran 95 routine cpu_time() returns the ellapsed CPU time in microseconds. The next example program measures the time required to compute the Mandelbrot set (fig. 1):

! mandelbrot.f90
program main
    implicit none
    integer, parameter :: WIDTH     = 80
    integer, parameter :: HEIGHT    = 40
    integer, parameter :: MAX_ITER  = 90
    real,    parameter :: THRESHOLD = 2.0
    integer            :: x, y
    real               :: re, im
    real               :: t1, t2

    call cpu_time(t1)

    do y = 0, HEIGHT - 1
        im = -1.5 + real(y) * 3.0 / real(HEIGHT)

        do x = 0, WIDTH - 1
            re = -2.0 + real(x) * 3.0 / real(WIDTH)

            if (mandelbrot(cmplx(re, im), MAX_ITER, THRESHOLD) < 10) then
                write (*, '(a)', advance='no') ' '
            else
                write (*, '(a)', advance='no') '*'
            end if
        end do

        write (*, '(a)')
    end do

    call cpu_time(t2)
    print '("Time: ", f8.6, " sec")', t2 - t1
contains
    function mandelbrot(c, max_iter, threshold)
        !! Calculates Mandelbrot value.
        complex, intent(in) :: c
        integer, intent(in) :: max_iter
        real,    intent(in) :: threshold
        integer             :: mandelbrot
        complex             :: z

        z = (0.0, 0.0)

        do mandelbrot = 0, max_iter
            z = z**2 + c

            if (abs(z) > threshold) &
                exit
        end do
    end function mandelbrot
end program main

Compile and run the example with:

$ gfortran10 -o mandelbrot mandelbrot.f90
$ ./mandelbrot

The figure is printed to standard output.

Mandelbrot set in Fortran
Fig. 1: The example Mandelbrot program

System Clock

The system_clock(count, count_rate, count_max) routine was introduced in Fortran 90 and returns the processor clock, the number of ticks per second, and the maximum clock value. Depending on the platform, up to nanosecond resolution is provided. If there is no clock, count is set to -huge(count), and count_rate and count_max are set to zero.

call system_clock(count, count_rate, count_max)
Argument Type Description
count integer The processor clock. For kind=4 arguments it represents milliseconds, while for kind=8 arguments (and larger integer kinds), count represents micro- or nanoseconds, depending on the resolution of the underlying platform clock.
count_rate integer System depended number of ticks per second.
count_max integer Maximum clock value, usually huge(count_max).

The following example just outputs the values to screen:

! clock.f90
program main
    implicit none
    integer(kind=8) :: count, count_rate, count_max

    call system_clock(count, count_rate, count_max)
    print *, count, count_rate, count_max
end program main

The count value is returned in nanosecond resolution:

$ gfortran10 -o clock clock.f90
$ ./clock
      513061764742145           1000000000  9223372036854775807

OpenMP

The thread-safe OpenMP function omp_get_wtime() returns the number of seconds since the initial value of the real-time clock. OpenMP directives are not required in order to call the function.

The following example implements a simple Monte Carlo simulation that estimates the number Pi, and measures the time the computer needs for this task.

! pi.f90
program main
    use, intrinsic :: omp_lib
    implicit none
    integer(kind=8), parameter :: N = 10**7
    real(kind=8)               :: t1, t2
    real(kind=8)               :: pi

    call random_seed()

    t1 = omp_get_wtime()
    pi = monte_carlo_pi(N)
    t2 = omp_get_wtime()

    print '("t:  ", f0.3, " s")', t2 - t1
    print '("Pi: ", f0.8)', pi
contains
    function monte_carlo_pi(n) result(pi)
        integer(kind=8), intent(in) :: n
        real(kind=8)                :: pi
        real(kind=8)                :: xy(2)
        integer(kind=8)             :: c, i

        c = 0

        do i = 1, n
            call random_number(xy)

            if (xy(1)**2 + xy(2)**2 < 1) &
                c = c + 1
        end do

        pi = (dble(c) / n) * 4
    end function monte_carlo_pi
end program main

Using GNU Fortran, the -fopenmp flag is required to link OpenMP:

$ gfortran10 -fopenmp -o pi pi.f90
$ ./pi
t:  1.229 s
Pi: 3.14110994

On an Intel Core i7 (Ivy Bridge), 107 iterations take about 1.2 seconds.

RFC 2822

The Internet Message Format (RFC 2822) includes a date and time specification for text messages that are sent between computers, for instance, by e-mail. The following Fortran function rfc2822() returns a 31 characters long string, like Thu, 01 Sep 2016 10:11:12 -0500.

function rfc2822()
    ! Returns current date and time in RFC 2822 format:
    !     https://www.ietf.org/rfc/rfc2822.txt
    character(len=3), parameter :: DAYS(7)    = [ 'Sun', 'Mon', 'Thu', 'Wed', 'Thu', 'Fri', 'Sat' ]
    character(len=3), parameter :: MONTHS(12) = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', &
                                                  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
    character(len=*), parameter :: DT_FMT     = '(a, ", ", i0.2, " ", a, " ", i4, " ", ' // &
                                                'i0.2, ":", i0.2, ":", i0.2, " ", a)'
    character(len=31) :: rfc2822
    character(len=5)  :: z
    integer(kind=8)   :: dt(8), w

    call date_and_time(zone=z, values=dt)
    w = 1 + modulo(dt(1) + int((dt(1) - 1) / 4) - int((dt(1) - 1) / 100) + int((dt(1) - 1) / 400), &
                   int(7, kind=8))
    write (rfc2822, DT_FMT) DAYS(w), dt(3), MONTHS(dt(2)), dt(1), dt(5), dt(6), dt(7), z
end function rfc2822

POSIX

The POSIX standards include several date and time functions, which are accessible from Fortran via appropriate ISO C binding interfaces.

The POSIX function time(3) returns the time in seconds since 0 hours, 0 minutes, 0 seconds, January 1, 1970, Coordinated Universal Time, also known as Unix epoch. An ISO C binding interface is necessary to call the function from Fortran:

! timestamp.f90
program main
    use, intrinsic :: iso_c_binding, only: c_long
    implicit none

    interface
        ! time_t time(time_t *tloc)
        function c_time(tloc) bind(c, name='time')
            import :: c_long
            integer(kind=c_long), intent(in), value :: tloc
            integer(kind=c_long)                    :: c_time
        end function c_time
    end interface

    integer(kind=8) :: ts

    unix = c_time(int(0, kind=8))
    print '(i0)', ts
end program main

The example outputs the current time as a Unix timestamp:

$ gfortran10 -o timestamp timestamp.f90
$ ./timestamp
1589814676

The asctime(3) function in libc converts the broken-down time given as an argument into a string in the form Thu Oct 1 12:00:00 2020\n\0. We have to fill the broken-down derived type c_tm with date and time values to get a C char array pointer to the formatted string from c_asctime(). The fortran-unix bindings for Fortran 2008 provide the necessary interfaces:

! posix.f90
program main
    use, intrinsic :: iso_c_binding, only: c_ptr
    use :: unix, only: c_asctime, c_f_str_ptr, c_tm
    implicit none
    character(len=24) :: str ! Date and time string.
    type(c_tm)        :: tm  ! Broken-down time.
    type(c_ptr)       :: ptr ! C pointer to char array.

    ! Print date and time string (of 2020-10-01 12:00:00).
    tm  = c_tm(tm_sec   = 0, &              ! seconds
               tm_min   = 0, &              ! minutes
               tm_hour  = 12, &             ! hours
               tm_mday  = 1, &              ! day of the month
               tm_mon   = 9, &              ! month
               tm_year  = 2020 - 1900, &    ! year
               tm_wday  = 4, &              ! day of the week
               tm_yday  = 0, &              ! day in the year
               tm_isdst = 0)                ! daylight saving time

    ptr = c_asctime(tm)
    call c_f_str_ptr(ptr, str)

    print '("Date and Time: ", a)', str
end program main

The subroutine c_f_str_ptr() is part of fortran-unix and converts a C char pointer to Fortran character. Compile, link, and run the example with:

$ gfortran10 -o posix posix.f90 libfortran-unix.a
$ ./posix
Date and Time: Thu Oct  1 12:00:00 2020

Libraries

Additional time and date routines are provided by libraries for modern Fortran: