The stdlib_datetime module provides types and procedures for date, time, and duration handling. It defines two primary derived types — datetime_type for representing specific points in time and timedelta_type for representing durations — along with arithmetic operators, comparison operators, ISO 8601 parsing/formatting, and calendar utilities.
No C-bindings or parameterized derived types are required. The module uses only standard Fortran intrinsics, calendar arithmetic, and string manipulation.
Experimental
datetime_typeRepresents a specific point in time.
| Component | Type | Default | Description |
|---|---|---|---|
year |
integer |
1 | Year (1–9999) |
month |
integer |
1 | Month (1–12) |
day |
integer |
1 | Day (1–31) |
hour |
integer |
0 | Hour (0–23) |
minute |
integer |
0 | Minute (0–59) |
second |
integer |
0 | Second (0–59) |
millisecond |
integer |
0 | Millisecond (0–999) |
utc_offset_minutes |
integer |
0 | UTC offset in minutes. Minutes are used because all real-world UTC offsets are whole multiples of minutes (ISO 8601 specifies offsets as ±HH:MM). |
timedelta_typeRepresents a duration or interval. After normalization, seconds is always in [0, 86399] and milliseconds is always in [0, 999] (i.e., they are never negative). For negative durations, only the days component is negative while seconds and milliseconds remain non-negative.
| Component | Type | Default | Description |
|---|---|---|---|
days |
integer |
0 | Number of days (can be negative for negative durations) |
seconds |
integer |
0 | Seconds, always in [0, 86399] after normalization |
milliseconds |
integer |
0 | Milliseconds, always in [0, 999] after normalization |
datetime — Create from componentsExperimental
Pure function.
Creates a datetime_type from individual components. All arguments are optional; if omitted, they default to the type's initial values: year=1, month=1, day=1, hour=0, minute=0, second=0, millisecond=0, utc_offset_minutes=0.
Note: Unlike parse_datetime, this constructor does not perform strict bounds checking on the provided values (e.g., passing month=13 is not checked) to remain highly efficient in tight loops. The caller is responsible for ensuring the values form a valid date block.
dt = datetime ([year] [, month] [, day] [, hour] [, minute] [, second] [, millisecond] [, utc_offset_minutes])
All arguments are optional with intent(in) and type integer. If no arguments are provided, all components default to the type's initial values (year=1, month=1, day=1, hour=0, minute=0, second=0, millisecond=0, utc_offset_minutes=0).
A datetime_type value with components equal to those provided, or to the default ones.
timedelta — Create from mixed unitsExperimental
Pure function.
Creates a normalized timedelta_type. Accepts mixed units (days, hours, minutes, seconds, milliseconds) and normalizes them. If no arguments are provided, all components default to 0.
td = timedelta ([days] [, hours] [, minutes] [, seconds] [, milliseconds])
All arguments are optional with intent(in) and type integer. If no arguments are provided, all components default to 0.
A normalized timedelta_type value with seconds in [0, 86399] and milliseconds in [0, 999].
now — Current local timeExperimental
Function (not pure; calls the intrinsic date_and_time which is impure).
Returns the current local date and time from the system clock.
dt = now ()
now_utc — Current UTC timeExperimental
Function (not pure; calls now() internally).
Returns the current UTC date and time.
dt = now_utc ()
epoch — Unix epochExperimental
Pure function.
Returns the Unix epoch: 1970-01-01T00:00:00Z. This is a mathematical constant (a fixed date) and does not depend on the operating system.
dt = epoch ()
| Expression | Result Type | Description |
|---|---|---|
datetime + timedelta |
datetime_type |
Add duration to timestamp |
timedelta + datetime |
datetime_type |
Commutative form |
timedelta + timedelta |
timedelta_type |
Add two durations |
datetime - timedelta |
datetime_type |
Subtract duration |
datetime - datetime |
timedelta_type |
Difference between two timestamps |
timedelta - timedelta |
timedelta_type |
Subtract durations |
-timedelta |
timedelta_type |
Negate duration |
All six comparison operators are provided for both datetime_type and timedelta_type: ==, /=, <, <=, >, >=.
Important: datetime_type comparisons convert both operands to UTC internally, so comparing across timezones works correctly.
parse_datetime — Parse ISO 8601 stringExperimental
Function (not pure; dummy arguments in a pure function must be intent(in), which contradicts the intent(out) status argument stat).
Parses an ISO 8601 date/time string into a datetime_type.
dt = parse_datetime (str [, stat])
str: character(len=*), intent(in). The ISO 8601 string to parse.
stat (optional): integer, intent(out). Returns 0 on success, non-zero on error.
YYYY-MM-DDYYYY-MM-DDTHH:MM:SSYYYY-MM-DDTHH:MM:SSZYYYY-MM-DDTHH:MM:SS+HH:MMYYYY-MM-DDTHH:MM:SS.fZ (variable precision fractional seconds up to milliseconds)YYYY-MM-DDTHH:MM:SS.f+HH:MMFor the YYYY-MM-DDTHH:MM:SS form without a timezone designator, the value is interpreted as UTC, and utc_offset_minutes is set to 0. There is currently no cross-platform standard way to get a time zone offset for arbitrary past/future dates, so defaulting to UTC serves as an absolute, safe coordinate. Forms with Z or an explicit offset use the specified UTC offset.
format_datetime — Format as ISO 8601Experimental
Pure function.
Formats a datetime_type as an ISO 8601 string.
str = format_datetime (dt)
character(:), allocatable — e.g. "2026-03-17T12:00:00Z" or "2026-03-17T23:05:15+05:30".
format_timedelta — Format durationExperimental
Pure function.
Formats a timedelta_type as a human-readable string.
str = format_timedelta (td)
character(:), allocatable — e.g. "30 days, 01:30:00".
is_leap_yearExperimental
Pure elemental function / interface.
Returns .true. if the given year (or datetime's year) is a leap year.
result = is_leap_year (year) or (dt)
days_in_monthExperimental
Pure function.
Returns the number of days in a given month and year.
d = days_in_month (month, year)
days_in_yearExperimental
Pure function.
Returns 365 or 366.
d = days_in_year (year)
day_of_yearExperimental
Pure function.
Returns the ordinal day (1–366).
doy = day_of_year (dt)
day_of_weekExperimental
Pure function.
Returns the ISO weekday (1=Monday, ..., 7=Sunday).
dow = day_of_week (dt)
to_utcExperimental
Pure function.
Converts a datetime_type to UTC.
utc_dt = to_utc (dt)
total_secondsExperimental
Pure function.
Returns the total duration as real(dp).
secs = total_seconds (td)
program example_datetime
!! Demonstrate the stdlib_datetime module functionality.
use stdlib_datetime
implicit none
type(datetime_type) :: t1, t2, t3
type(timedelta_type) :: duration
integer :: stat
print '(A)', '=== stdlib_datetime Example ==='
print *
! 1. Get the current local time
t1 = now()
print '(A,A)', 'Current local time: ', format_datetime(t1)
! 2. Get the current UTC time
t2 = now_utc()
print '(A,A)', 'Current UTC time: ', format_datetime(t2)
! 3. Parse an ISO 8601 string
t2 = parse_datetime('2026-03-17T12:00:00Z', stat)
if (stat /= 0) then
print '(A)', 'ERROR: Failed to parse date string!'
stop 1
end if
print '(A,A)', 'Parsed datetime: ', format_datetime(t2)
! 4. Calculate the difference between two datetimes
duration = t1 - t2
print '(A,A)', 'Duration (t1-t2): ', &
format_timedelta(duration)
! 5. Add 30 days to a date
t3 = t2 + timedelta_type(30, 0, 0)
print '(A,A)', 'After adding 30d: ', format_datetime(t3)
! 6. Add mixed units (1 day, 6 hours, 30 minutes)
t3 = t2 + timedelta(days=1, hours=6, minutes=30)
print '(A,A)', 'After +1d 6h 30m: ', format_datetime(t3)
! 7. Calendar utilities
print *
print '(A)', '=== Calendar Utilities ==='
print '(A,L1)', 'Is 2024 leap year? ', is_leap_year(2024)
print '(A,L1)', 'Is 2026 leap year? ', is_leap_year(2026)
print '(A,I0)', 'Days in Feb 2024: ', &
days_in_month(2, 2024)
print '(A,I0)', 'Days in Feb 2026: ', &
days_in_month(2, 2026)
print '(A,I0)', 'Day of year (t2): ', day_of_year(t2)
print '(A,I0)', 'Day of week (t2): ', day_of_week(t2)
print '(A)', ' (1=Mon, 2=Tue, ..., 7=Sun)'
! 8. Comparison operators
print *
print '(A)', '=== Comparisons ==='
t2 = parse_datetime('2026-03-17T12:00:00Z')
t3 = parse_datetime('2026-03-18T12:00:00Z')
print '(A,L1)', 'Mar17 < Mar18? ', t2 < t3
print '(A,L1)', 'Mar17 == Mar17? ', t2 == t2
! Cross-timezone equality: 12:00 UTC == 17:30 IST
t2 = datetime_type(2026, 3, 17, 12, 0, 0, 0, 0)
t3 = datetime_type(2026, 3, 17, 17, 30, 0, 0, 330)
print '(A,L1)', '12:00Z == 17:30+05:30? ', t2 == t3
! 9. Unix epoch
print *
print '(A,A)', 'Unix epoch: ', &
format_datetime(epoch())
print *
print '(A)', 'Done!'
end program example_datetime