Date and Time

Modern Fortran features some basic time routines. On POSIX-compliant systems, the ISO C binding interface allows us to access additional timing routines.

Current 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):

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

    call date_and_time(values=dt)
    print '(i4, 5(a, i2.2))', 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:

$ ./datetime
2019/08/12 21:50:23
Beats: @868.32

CPU Time

The intrinsic Fortran 95 routine cpu_time() returns the ellapsed CPU time in microseconds. The example 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
        im = -1.5 + real(y) * 3.0 / real(HEIGHT)

        do x = 0, WIDTH
            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, " seconds")', t2 - t1
contains
    function mandelbrot(c, max_iter, threshold)
        !! Calculates Mandelbrot set.
        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:

$ gfortran9 -o mandelbrot mandelbrot.f90
$ ./mandelbrot
Mandelbrot set in Fortran
Fig. 1: The example Mandelbrot program

System Clock

The system_clock(count, count_rate, count_max) routine was introduced with 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:

$ gfortran -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)                :: x, y
        integer(kind=8)             :: c, i

        c = 0

        do i = 1, n
            call random_number(x)
            call random_number(y)

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

        pi = (real(c, kind=8) / n) * 4
    end function monte_carlo_pi
end program main

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

$ gfortran9 -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.

Unix Time

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. A 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) :: unix

    unix = c_time(int(0, kind=8))
    print *, unix
end program main

The example outputs the current time as a Unix timestamp:

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

If an error occurs, time() returns the value -1.