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
- 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).
- 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:
- datetime: Modern Fortran library for simple, time-zone independent, date and time management
- datetime-fortran: Date and time manipulation library for Fortran 2008
- fortran-datetime: Another Fortran date and time library
- fortran-unix: Fortran 2008 interface bindings to POSIX date and time functions
- ftime: Basic Fortran 2003 timing routines.
- M_time: Fortran module for manipulating and presenting date and time values
< Key Capture | [Index] | Sleep > |