Skip to content

Commit 923f75f

Browse files
authored
Merge pull request #1148 from JAi-SATHVIK/upstream-cpy
feat: implementation of the stdlib_datetime module
2 parents 9d7e28d + 82a249f commit 923f75f

10 files changed

Lines changed: 1911 additions & 0 deletions

File tree

doc/specs/stdlib_datetime.md

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
---
2+
title: datetime
3+
---
4+
5+
[TOC]
6+
7+
## Introduction
8+
9+
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.
10+
11+
No C-bindings or parameterized derived types are required. The module uses only standard Fortran intrinsics, calendar arithmetic, and string manipulation.
12+
13+
### Status
14+
15+
Experimental
16+
17+
## Derived Types
18+
19+
### `datetime_type`
20+
21+
Represents a specific point in time.
22+
23+
| Component | Type | Default | Description |
24+
|-----------|------|---------|-------------|
25+
| `year` | `integer` | 1 | Year (1–9999) |
26+
| `month` | `integer` | 1 | Month (1–12) |
27+
| `day` | `integer` | 1 | Day (1–31) |
28+
| `hour` | `integer` | 0 | Hour (0–23) |
29+
| `minute` | `integer` | 0 | Minute (0–59) |
30+
| `second` | `integer` | 0 | Second (0–59) |
31+
| `millisecond` | `integer` | 0 | Millisecond (0–999) |
32+
| `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`). |
33+
34+
### `timedelta_type`
35+
36+
Represents 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.
37+
38+
| Component | Type | Default | Description |
39+
|-----------|------|---------|-------------|
40+
| `days` | `integer` | 0 | Number of days (can be negative for negative durations) |
41+
| `seconds` | `integer` | 0 | Seconds, always in [0, 86399] after normalization |
42+
| `milliseconds` | `integer` | 0 | Milliseconds, always in [0, 999] after normalization |
43+
44+
## Constructors
45+
46+
### `datetime` — Create from components
47+
48+
#### Status
49+
50+
Experimental
51+
52+
#### Class
53+
54+
Pure function.
55+
56+
#### Description
57+
58+
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`.
59+
60+
*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.
61+
62+
#### Syntax
63+
64+
`dt = ` [[stdlib_datetime(module):datetime(function)]] `([year] [, month] [, day] [, hour] [, minute] [, second] [, millisecond] [, utc_offset_minutes])`
65+
66+
#### Arguments
67+
68+
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`).
69+
70+
#### Return value
71+
72+
A `datetime_type` value with components equal to those provided, or to the default ones.
73+
74+
### `timedelta` — Create from mixed units
75+
76+
#### Status
77+
78+
Experimental
79+
80+
#### Class
81+
82+
Pure function.
83+
84+
#### Description
85+
86+
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`.
87+
88+
#### Syntax
89+
90+
`td = ` [[stdlib_datetime(module):timedelta(function)]] `([days] [, hours] [, minutes] [, seconds] [, milliseconds])`
91+
92+
#### Arguments
93+
94+
All arguments are optional with `intent(in)` and type `integer`. If no arguments are provided, all components default to `0`.
95+
96+
#### Return value
97+
98+
A normalized `timedelta_type` value with `seconds` in [0, 86399] and `milliseconds` in [0, 999].
99+
100+
### `now` — Current local time
101+
102+
#### Status
103+
104+
Experimental
105+
106+
#### Class
107+
108+
Function (not `pure`; calls the intrinsic `date_and_time` which is impure).
109+
110+
#### Description
111+
112+
Returns the current local date and time from the system clock.
113+
114+
#### Syntax
115+
116+
`dt = ` [[stdlib_datetime(module):now(function)]] `()`
117+
118+
### `now_utc` — Current UTC time
119+
120+
#### Status
121+
122+
Experimental
123+
124+
#### Class
125+
126+
Function (not `pure`; calls `now()` internally).
127+
128+
#### Description
129+
130+
Returns the current UTC date and time.
131+
132+
#### Syntax
133+
134+
`dt = ` [[stdlib_datetime(module):now_utc(function)]] `()`
135+
136+
### `epoch` — Unix epoch
137+
138+
#### Status
139+
140+
Experimental
141+
142+
#### Class
143+
144+
Pure function.
145+
146+
#### Description
147+
148+
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.
149+
150+
#### Syntax
151+
152+
`dt = ` [[stdlib_datetime(module):epoch(function)]] `()`
153+
154+
## Operators
155+
156+
### Arithmetic
157+
158+
| Expression | Result Type | Description |
159+
|------------|-------------|-------------|
160+
| `datetime + timedelta` | `datetime_type` | Add duration to timestamp |
161+
| `timedelta + datetime` | `datetime_type` | Commutative form |
162+
| `timedelta + timedelta` | `timedelta_type` | Add two durations |
163+
| `datetime - timedelta` | `datetime_type` | Subtract duration |
164+
| `datetime - datetime` | `timedelta_type` | Difference between two timestamps |
165+
| `timedelta - timedelta` | `timedelta_type` | Subtract durations |
166+
| `-timedelta` | `timedelta_type` | Negate duration |
167+
168+
### Comparison
169+
170+
All six comparison operators are provided for both `datetime_type` and `timedelta_type`: `==`, `/=`, `<`, `<=`, `>`, `>=`.
171+
172+
**Important:** `datetime_type` comparisons convert both operands to UTC internally, so comparing across timezones works correctly.
173+
174+
## Parsing and Formatting
175+
176+
### `parse_datetime` — Parse ISO 8601 string
177+
178+
#### Status
179+
180+
Experimental
181+
182+
#### Class
183+
184+
Function (not `pure`; dummy arguments in a `pure function` must be `intent(in)`, which contradicts the `intent(out)` status argument `stat`).
185+
186+
#### Description
187+
188+
Parses an ISO 8601 date/time string into a `datetime_type`.
189+
190+
#### Syntax
191+
192+
`dt = ` [[stdlib_datetime(module):parse_datetime(function)]] `(str [, stat])`
193+
194+
#### Arguments
195+
196+
`str`: `character(len=*)`, `intent(in)`. The ISO 8601 string to parse.
197+
198+
`stat` (optional): `integer`, `intent(out)`. Returns 0 on success, non-zero on error.
199+
200+
#### Supported formats
201+
202+
- `YYYY-MM-DD`
203+
- `YYYY-MM-DDTHH:MM:SS`
204+
- `YYYY-MM-DDTHH:MM:SSZ`
205+
- `YYYY-MM-DDTHH:MM:SS+HH:MM`
206+
- `YYYY-MM-DDTHH:MM:SS.fZ` (variable precision fractional seconds up to milliseconds)
207+
- `YYYY-MM-DDTHH:MM:SS.f+HH:MM`
208+
209+
For 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.
210+
211+
### `format_datetime` — Format as ISO 8601
212+
213+
#### Status
214+
215+
Experimental
216+
217+
#### Class
218+
219+
Pure function.
220+
221+
#### Description
222+
223+
Formats a `datetime_type` as an ISO 8601 string.
224+
225+
#### Syntax
226+
227+
`str = ` [[stdlib_datetime(module):format_datetime(function)]] `(dt)`
228+
229+
#### Return value
230+
231+
`character(:), allocatable` — e.g. `"2026-03-17T12:00:00Z"` or `"2026-03-17T23:05:15+05:30"`.
232+
233+
### `format_timedelta` — Format duration
234+
235+
#### Status
236+
237+
Experimental
238+
239+
#### Class
240+
241+
Pure function.
242+
243+
#### Description
244+
245+
Formats a `timedelta_type` as a human-readable string.
246+
247+
#### Syntax
248+
249+
`str = ` [[stdlib_datetime(module):format_timedelta(function)]] `(td)`
250+
251+
#### Return value
252+
253+
`character(:), allocatable` — e.g. `"30 days, 01:30:00"`.
254+
255+
## Utility Functions
256+
257+
### `is_leap_year`
258+
259+
#### Status
260+
261+
Experimental
262+
263+
#### Class
264+
265+
Pure elemental function / interface.
266+
267+
#### Description
268+
269+
Returns `.true.` if the given year (or datetime's year) is a leap year.
270+
271+
#### Syntax
272+
273+
`result = ` [[stdlib_datetime(module):is_leap_year(interface)]] `(year)` or `(dt)`
274+
275+
### `days_in_month`
276+
277+
#### Status
278+
279+
Experimental
280+
281+
#### Class
282+
283+
Pure function.
284+
285+
#### Description
286+
287+
Returns the number of days in a given month and year.
288+
289+
#### Syntax
290+
291+
`d = ` [[stdlib_datetime(module):days_in_month(function)]] `(month, year)`
292+
293+
### `days_in_year`
294+
295+
#### Status
296+
297+
Experimental
298+
299+
#### Class
300+
301+
Pure function.
302+
303+
#### Description
304+
305+
Returns 365 or 366.
306+
307+
#### Syntax
308+
309+
`d = ` [[stdlib_datetime(module):days_in_year(function)]] `(year)`
310+
311+
### `day_of_year`
312+
313+
#### Status
314+
315+
Experimental
316+
317+
#### Class
318+
319+
Pure function.
320+
321+
#### Description
322+
323+
Returns the ordinal day (1–366).
324+
325+
#### Syntax
326+
327+
`doy = ` [[stdlib_datetime(module):day_of_year(function)]] `(dt)`
328+
329+
### `day_of_week`
330+
331+
#### Status
332+
333+
Experimental
334+
335+
#### Class
336+
337+
Pure function.
338+
339+
#### Description
340+
341+
Returns the ISO weekday (1=Monday, ..., 7=Sunday).
342+
343+
#### Syntax
344+
345+
`dow = ` [[stdlib_datetime(module):day_of_week(function)]] `(dt)`
346+
347+
### `to_utc`
348+
349+
#### Status
350+
351+
Experimental
352+
353+
#### Class
354+
355+
Pure function.
356+
357+
#### Description
358+
359+
Converts a `datetime_type` to UTC.
360+
361+
#### Syntax
362+
363+
`utc_dt = ` [[stdlib_datetime(module):to_utc(function)]] `(dt)`
364+
365+
### `total_seconds`
366+
367+
#### Status
368+
369+
Experimental
370+
371+
#### Class
372+
373+
Pure function.
374+
375+
#### Description
376+
377+
Returns the total duration as `real(dp)`.
378+
379+
#### Syntax
380+
381+
`secs = ` [[stdlib_datetime(module):total_seconds(function)]] `(td)`
382+
383+
## Example
384+
385+
```fortran
386+
{!example/datetime/example_datetime_usage.f90!}
387+
```

example/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ if (STDLIB_BITSETS)
2525
add_subdirectory(bitsets)
2626
endif()
2727
add_subdirectory(constants)
28+
add_subdirectory(datetime)
2829
add_subdirectory(error)
2930
if (STDLIB_HASHMAPS)
3031
add_subdirectory(hashmaps)

example/datetime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ADD_EXAMPLE(datetime_usage)

0 commit comments

Comments
 (0)