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 extension time() returns a 32-bit timestamp, much like the POSIX function time(3):

! time.f90
program main
    implicit none (type, external)

    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 time in .beats (Swatch Internet Time) to standard output. The time zone difference to UTC in minutes is substracted from mins, and the difference of one hour to Biel Mean Time (BMT) is added to hrs to calculate the timestamp beats:

! beats.f90
program main
    implicit none (type, external)

    integer :: dt(8)
    integer :: hrs, mins, secs, zone
    real    :: beats

    call date_and_time(values=dt)

    zone = dt(4)
    hrs  = dt(5)
    mins = dt(6)
    secs = dt(7)

    beats = (((hrs + 1) * 3600) + ((mins - zone) * 60) + secs) / 86.4
    print '("Beats: @", f0.2)', beats
end program main

Running the executable outputs:

$ gfortran13 -o beats beats.f90
$ ./beats
Beats: @847.32
Mandelbrot set in Fortran
Fig. 1: The example Mandelbrot program

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:

! mandelbrot.f90
program main
    implicit none (type, external)
    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 + y * 3.0 / HEIGHT

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

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

        write (*, *)
    end do

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

        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:

$ gfortran13 -o mandelbrot mandelbrot.f90
$ ./mandelbrot

The figure is printed to standard output (fig. 1).

system_clock()
Fig. 2: Measuring the execution time with system_clock()

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=int32 arguments it represents milliseconds, while for kind=int64 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 measures the time a task takes to finish:

! density.f90
program main
    !! Example program that prints plot of a density function to standard
    !! output, as well as the required time in seconds. The code has been
    !! adapted from:
    !!
    !!   A. Colin Day: FORTRAN Techniques with Special Reference to
    !!   Non-Numerical Applications, Cambridge University Press, 1972
    !!
    use, intrinsic :: iso_fortran_env, only: i8 => int64, r8 => real64
    implicit none (type, external)

    character, parameter :: MARK(6) = [ ' ', '-', '+', '*', 'o', 'x' ]

    character        :: line(120)
    integer          :: i, j, k
    integer(kind=i8) :: rate, t1, t2
    real(kind=r8)    :: d, dt

    ! Get first clock count.
    call system_clock(count=t1, count_rate=rate)

    do i = 1, 60
        do j = 1, 120
            d = sqrt((dble(i - 30) / 0.6)**2 + dble(j - 60)**2)
            k = int(5 * f(d / 30.0) + 1)
            k = 1 + mod(k - 1, 6)
            line(j) = MARK(k)
        end do
        print '(120a1)', line
    end do

    ! Get second clock count and calculate time delta.
    call system_clock(count=t2)
    dt = (t2 - t1) / dble(rate)

    print '("dt: ", f8.6, " seconds")', dt
contains
    pure real(kind=r8) function f(x)
        real(kind=r8), intent(in) :: x
        real(kind=r8) :: a, b

        a = exp(-x * x)
        b = cos(x * 3.0 / a)
        f = a * b * b
    end function f
end program main

The result may be inaccurate if 32-bit and 64-bit types for clock count and count rate are mixed. Compile and run the example with:

$ gfortran13 -o density density.f90
$ ./density

The plot of the density function and the execution time are printed to console (fig. 2).

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 Pi, and measures the time the computer takes for this task.

! pi.f90
program main
    use, intrinsic :: iso_fortran_env, only: i8 => int64, r8 => real64
    use, intrinsic :: omp_lib
    implicit none (type, external)

    integer(kind=i8), parameter :: N = 10_i8**8

    real(kind=r8) :: t1, t2
    real(kind=r8) :: pi

    call random_seed()

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

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

        integer(kind=i8) :: c, i
        real(kind=r8)    :: r(2)

        c = 0

        do i = 1, n
            call random_number(r)
            if (r(1)**2 + r(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:

$ gfortran13 -fopenmp -o pi pi.f90
$ ./pi
dt: 2.543 s
Pi: 3.14145016

Executing the program on an Intel Core i5 (Kaby Lake), 108 iterations take 2.543 seconds.

RFC 2822

The Internet Message Format (RFC 2822) defines 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 in RFC 2822 format, such as 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
    use, intrinsic :: iso_fortran_env, only: i8 => int64

    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 :: RFC_FMT = &
        '(a, ", ", i0.2, " ", a, " ", i4, " ", i0.2, ":", i0.2, ":", i0.2, " ", a)'

    character(len=31) :: rfc2822
    character(len=5)  :: z
    integer(kind=i8)  :: d, dt(8)

    call date_and_time(values=dt, zone=z)

    d = 1 + modulo(dt(1) + ((dt(1) - 1) / 4) - ((dt(1) - 1) / 100) + ((dt(1) - 1) / 400), 7_i8)
    write (rfc2822, RFC_FMT) DAYS(d), dt(3), MONTHS(dt(2)), dt(1), dt(5), dt(6), dt(7), z
end function rfc2822

ISO 8601

The following function iso8601() returns the current date and time as a 29 characters long string in ISO 8601 format: 1970-01-01T00:00:00.000+00:00.

function iso8601()
    !! Returns current date and time in ISO 8601 format.
    character(len=*), parameter :: ISO_FMT = &
        '(i4, 2("-", i2.2), "T", 2(i0.2, ":"), i0.2, ".", i0.3, a, ":", a)'
    character(len=29) :: iso8601
    character(len=5)  :: zone
    integer           :: dt(8)

    call date_and_time(values=dt, zone=zone)

    write (iso8601, ISO_FMT) dt(1), dt(2), dt(3), dt(5), dt(6), &
                             dt(7), dt(8), zone(1:3), zone(4:5)
end function iso8601

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
    implicit none

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

    integer(kind=c_long) :: ts

    ts = c_time(0_c_long)
    print '(i0)', ts
end program main

The example outputs the current time as a Unix timestamp:

$ gfortran13 -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 pointer to the formatted string from c_asctime(). The fortran-unix bindings for Fortran 2008 provide the necessary interfaces:

! posix.f90
program main
    use :: unix
    implicit none (type, external)

    character(len=:), allocatable :: str ! Date and time string.
    type(c_tm)                    :: tm  ! Broken-down time.

    ! 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

    call c_f_str_ptr(c_asctime(tm), str)

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

The 25th character in str is new line \n. The subroutine c_f_str_ptr() is part of fortran-unix and converts a C char pointer to Fortran character. If the library is installed to /opt, compile, link, and run the example with:

$ gfortran13 -I/opt/include/libfortran-unix -o posix posix.f90 /opt/lib/libfortran-unix.a
$ ./posix
Date and Time: Thu Oct  1 12:00:00 2020

Fortran Libraries

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