Skip to content

Commit 43334ff

Browse files
colinleachBethanyG
authored andcommitted
The datetime concept
1 parent 7e5a83d commit 43334ff

File tree

5 files changed

+396
-3
lines changed

5 files changed

+396
-3
lines changed

concepts/datetime/.meta/config.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"blurb": "Handling dates and imes in software is a surprisingly complex challenge. Python supplies a variety of classes to cover most use case.",
3+
"authors": ["BethanyG", "colinleach"],
4+
"contributors": []
5+
}

concepts/datetime/about.md

+369
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
# About
2+
3+
_"Dates and times are something we teach to young children. How hard can it be?"_
4+
5+
Many programmers have made that mistake, and the subsequent experience tends to be negative to their health and happiness.
6+
7+
Anyone doing non-trivial programming with dates and times should at least be prepared to understand and mitigate potential problems.
8+
9+
## The `datetime` module
10+
11+
In python, a wide range of date and time functionality is collected in the [`datetime`][datetime] module.
12+
This can be supplemented by other libraries, but `datetime` is central and often sufficient.
13+
14+
There are five major classes within `datetime`:
15+
- `datetime.date` for simple dates
16+
- `datetime.time` for simple times
17+
- `datetime.datetime` combines date, time and optionally timezone information
18+
- `datetime.timedelta` for intervals
19+
- `datetime.timezone` to handle the reality that few people use UTC
20+
21+
___Notation detail:___ A `datetime.time` or `datetime.datetime` object that includes timezone information is said to be _aware_, otherwise it is _naive_.
22+
A `datetime.date` object is always naive.
23+
24+
As `datetime` is a large module with many methods and attributes, only some of the most common will be discussed here.
25+
26+
You are encouraged to explore the [full documentation][datetime].
27+
Dates and times are complex but important, so the Python developers have put many years of effort into trying to support most use cases.
28+
29+
Perhaps the most frequent needs are:
30+
31+
- Parse some appropriate input format to construct a `datetime` object.
32+
This often uses [`strptime()`][strptime-strftime].
33+
- Get the required numerical or string format from a `datetime` object.
34+
String output often uses [`strftime()`][strptime-strftime].
35+
- Apply an offset to a `date`, `time` or `datetime` to create a new object (of the same type).
36+
- Calculate the interval between two such objects.
37+
- Get the current date and/or time.
38+
This will be obtained from the host computer and converted to a Python object.
39+
40+
41+
### Date and time formats
42+
43+
There are many ways to write dates and times, which tend to be culturally-specific.
44+
All-number dates such as "7/6/23" are ambiguous, confusing, and have led to many expensive mistakes in multinational organizations.
45+
46+
The international standard is defined in [`ISO 8601`][ISO8601], with two main advantages:
47+
- Parsing is quick and unambiguous.
48+
- Sorting is easy, as the datetime can be treated as text.
49+
50+
An example:
51+
52+
```python
53+
>>> from datetime import datetime
54+
>>> datetime.now(timezone.utc).isoformat()
55+
'2023-12-04T17:54:13.014513+00:00'
56+
```
57+
58+
This is built up from various parts, with only the date fields required:
59+
- `YYYY-MM-DD`
60+
- Optionally, `Thh:mm:ss`
61+
- Optionally, microseconds after the decimal point.
62+
- Optionally, timezone offset from UTC with a sign and `hh:mm` value.
63+
64+
Internally, `date`, `time` and `datetime` are stored as Python objects with separate attributes for year, month, etc.
65+
Examples of this will be shown below, when each class is discussed.
66+
67+
Most computer operating systems use POSIX timestamps: the number of seconds since `1970-01-01T00:00:00+00.00`.
68+
The `datetime` module makes it easy to import these.
69+
70+
For code which interacts mainly with computers rather than humans, it may be worth investigating the separate [`time`][time] module, which provides more complete support for POSIX timestamps.
71+
72+
## The [`datetime.date`][datetime-date] class
73+
74+
[`datetime.date`][datetime-date] is a relatively small and simple date-only class, with no understanding of times or timezones.
75+
76+
```python
77+
>>> from datetime import date
78+
>>> date.today()
79+
datetime.date(2023, 12, 4)
80+
>>> date.today().isoformat()
81+
'2023-12-04'
82+
```
83+
84+
The default display has the same `date(year, month, day)` syntax as the default constructor.
85+
A `date` object can also be created from an ISO 8601 date string or a POSIX timestamp.
86+
87+
```python
88+
>>> date(1969, 7, 20)
89+
datetime.date(1969, 7, 20)
90+
91+
>>> date.fromisoformat('1969-07-20')
92+
datetime.date(1969, 7, 20)
93+
94+
>>> date.fromisoformat('1969-07-20') == date(1969, 7, 20)
95+
True
96+
```
97+
98+
Individual parts of the date can be accessed as instance attributes:
99+
100+
```python
101+
>>> date.today().month # in December
102+
12
103+
```
104+
105+
There are a number of other methods, mostly related to output formats.
106+
See the [class documentation][datetime-date] for details.
107+
108+
`datetime.date` is designed to be fairly minimalist, to keep simple applications simple.
109+
110+
If your application is ever likely to need times or timezones, it may be better to use `datetime.datetime` from the start.
111+
112+
For more complex date-only applications, compare `datetime.date` with [`calendar`][calendar] and decide which better fits your needs.
113+
114+
## The [`datetime.time`][datetime-time] class
115+
116+
`datetime.time` is the basic time-only class.
117+
It has no understanding of dates: times automatically roll over to `time(0, 0, 0)` at midnight.
118+
119+
Timezone information can optionally be included.
120+
121+
The full constructor format is `timezone.time(hour, min, sec, microsec, timezone)`.
122+
123+
All the parameters are optional: numerical values will default to `0`, timezone to `None`.
124+
125+
```python
126+
>>> from datetime import time
127+
>>> time()
128+
datetime.time(0, 0)
129+
>>> time(14, 30, 23)
130+
datetime.time(14, 30, 23)
131+
```
132+
133+
Starting from an ISO 8601 format may be more readable in some cases:
134+
135+
```python
136+
>>> time.fromisoformat('15:17:01-07:00') # mid-afternoon in Arizona
137+
datetime.time(15, 17, 1, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))
138+
```
139+
140+
Timezones will be discussed in more detail below.
141+
142+
Arithmetic is not possible with `datetime.time` objects, but they do support comparisons.
143+
144+
```python
145+
>>> time1 = time(14, 45)
146+
>>> time2 = time(16, 21, 30)
147+
>>> time1 > time2
148+
False
149+
```
150+
151+
As with `date`, individual parts are available as instance attributes:
152+
153+
```python
154+
>>> time(16, 21, 30).hour
155+
16
156+
```
157+
158+
For other methods and properties, see the [class documentation][datetime-time].
159+
Much of it relates to working with timezones.
160+
161+
## The [`datetime.datetime`][datetime-datetime] class
162+
163+
`datetime.datetime` combines most of the features of the `date` and `time` classes and adds some extras.
164+
165+
It is the most versatile of these three classes, at the cost of some additional complexity.
166+
167+
```python
168+
>>> from datetime import datetime
169+
170+
>>> datetime.now()
171+
datetime.datetime(2023, 12, 4, 15, 45, 50, 66178)
172+
173+
>>> datetime.now().isoformat()
174+
'2023-12-04T15:46:30.311480'
175+
```
176+
177+
As with `date`, the default constructor has the same syntax as the default display.
178+
179+
The year, month and day parameters are required. Time parameters default to `0`. Timezone defaults to `None`, as in the example above.
180+
181+
Keeping all these parameters straight can be a challenge, so the ISO format may be preferable:
182+
183+
```python
184+
>>> datetime.fromisoformat('2023-12-04T15:53+05:30') # Delhi time
185+
datetime.datetime(2023, 12, 4, 15, 53, tzinfo=datetime.timezone(datetime.timedelta(seconds=19800)))
186+
```
187+
188+
Much of the functionality in `datetime.datetime` will be familar from `date` and time.
189+
190+
One addition that may be useful is `combine(date, time)` which constructs a `datetime` instance from a `date` and a `time` instance (and optionally a timezone).
191+
192+
```python
193+
>>> today = date.today()
194+
>>> current_time = time(4, 5)
195+
196+
>>> datetime.combine(today, current_time)
197+
datetime.datetime(2023, 12, 4, 4, 5)
198+
199+
>>> datetime.combine(today, current_time).isoformat()
200+
'2023-12-04T04:05:00'
201+
```
202+
203+
For other methods and properties, see the [class documentation][datetime-time].
204+
Much of it relates to working with timezones.
205+
206+
## The [`datetime.timedelta`][datetime-timedelta] class
207+
208+
A `timedelta` is an interval of time, the difference between two `datetime` instances.
209+
210+
Be careful with the constructor.
211+
The parameters are in an order you may not expect:
212+
213+
`datetime.timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0)`
214+
215+
Thus, it is generally safer to treat these as keyword rather than positional parameters:
216+
217+
```python
218+
>>> from datetime import timedelta
219+
>>> timedelta(weeks=3, hours=5)
220+
datetime.timedelta(days=21, seconds=18000)
221+
```
222+
223+
This illustrates the reason for the strange sequence: only days, seconds and microseconds are stored internally.
224+
Other parameters are provided as a convenience for the programmer, but will be converted.
225+
226+
Similarly, floating-point input values will be converted to integer days, seconds and microseconds.
227+
228+
With a `datetime` and a `timedelta` it is possible to add and subtract them:
229+
230+
```python
231+
>>> now = datetime.now()
232+
>>> now.isoformat()
233+
'2023-12-04T16:24:07.242274'
234+
235+
>>> later = now + timedelta(hours = 2.5) # 2.5 hours later
236+
>>> later.isoformat()
237+
'2023-12-04T18:54:07.242274'
238+
```
239+
240+
Alternatively, substract one `datetime` from another to get the `timedelta`:
241+
242+
```python
243+
>>> dt1 = datetime.fromisoformat('2023-12-04T16:45')
244+
>>> dt2 = datetime.fromisoformat('2023-07-23T09:16')
245+
246+
>>> dt1 - dt2
247+
datetime.timedelta(days=134, seconds=26940)
248+
249+
>>> str(dt2 - dt1) # formatted string output
250+
'-135 days, 16:31:00'
251+
```
252+
253+
Other arithmetic operations are supported, including:
254+
- Multiplying or dividing a `timedelta` by and `int` or `float`
255+
- Dividing one `timedelta` by another to get a `float`
256+
- Integer division (with `//`) and modulus (with `%`) with two `timedelta` instances.
257+
258+
See the [class documentation][datetime-timedelta] for details.
259+
260+
261+
## The [`datetime.tzinfo`][datetime-tzinfo] class and its sub-classes
262+
263+
Dealing with timezones can be challanging.
264+
Quoting the Python documentation:
265+
266+
_"The rules for time adjustment across the world are more political than rational, change frequently, and there is no standard suitable for every application aside from UTC."_
267+
268+
Python provides various libraries to help deal with this situation.
269+
A brief summary is given below, but anyone wishing to write reliable timezone-aware software cannot avoid reading the full documentation.
270+
271+
The [`datetime.tzinfo`][datetime-tzinfo] class is an [`abstract base class`][ABC].
272+
ABCs are a relatively advanced topic, but the essential point is that `tzinfo` cannot be instantiated directly.
273+
274+
Instead, this ABC provides a starting point for timezone-related subclasses to derive from.
275+
276+
[`datetime.timezone`][datetime-timezone] is a simple, concrete subclass for situations with a fixed offset from UTC.
277+
A limitation of `timezone` is that it has no understanding of Daylight Savings Time (DST) adjustments, it just stores a constant `timedelta` in seconds.
278+
279+
```python
280+
>>> datetime.fromisoformat('2023-12-04T15:53+03:00')
281+
datetime.datetime(2023, 12, 4, 15, 53, tzinfo=datetime.timezone(datetime.timedelta(seconds=10800)))
282+
```
283+
284+
[`zoneinfo.ZoneInfo`][zoneinfo] is a more sophisticated subclass, available in the standard library though not part of `datetime`.
285+
By linking to the [`tzdata`][tzdata] database, `ZoneInfo` understands DST issues worldwide.
286+
Multiple updates per year try, as far as possible, to remain up to date with unexpected changes.
287+
288+
`ZoneInfo`, via `tzdata`, has access to the [`IANA`][IANA] timezone database, and so can work with timezone names in a `region/city` format.
289+
The full list of [`tznames`][IANA-names] also includes many shorter aliases.
290+
291+
```python
292+
>>> from zoneinfo import ZoneInfo
293+
294+
>>> dt = datetime(2020, 10, 31, 12, tzinfo=ZoneInfo("Europe/Helsinki"))
295+
>>> print(dt)
296+
2020-10-31 12:00:00+02:00 # 2h ahead of UTC
297+
>>> dt.tzname()
298+
'EET' # Eastern European Time
299+
300+
>>> dt_subtract = dt - timedelta(days=7) # previous week
301+
>>> print(dt_subtract)
302+
2023-10-24 12:00:00+03:00 # now 3h ahead of UTC
303+
>>> dt_subtract.tzname()
304+
'EEST' # Eastern European Summer Time
305+
```
306+
307+
## The [`strptime()` and `strftime()`][strptime-strftime] methods
308+
309+
The `datetime.datetime` class supports a complementary pair of methods:
310+
- `strptime()` parses a string representation to a `datetime` object.
311+
- `strftime()` outputs a string representation of a `datetime` object.
312+
313+
Only `strftime()` is available in `datetime.date` and `datetime.time`.
314+
315+
A wide variety of format codes is available.
316+
Some of the common ones are shown in the examples below, but see the [official documentation][strptime-strftime] for the full list.
317+
These format codes are copied directly from C, and may be familiar to programmers who have worked in other languages.
318+
319+
```python
320+
>>> date_string = '14/10/23 23:59:59.999999'
321+
>>> format_string = '%d/%m/%y %H:%M:%S.%f'
322+
>>> dt = datetime.strptime(date_string, format_string)
323+
>>> dt
324+
datetime.datetime(2023, 10, 14, 23, 59, 59, 999999)
325+
326+
>>> dt.strftime('%a %d %b %Y, %I:%M%p')
327+
'Sat 14 Oct 2023, 11:59PM'
328+
```
329+
330+
## Related modules
331+
332+
This Concept has concentrated on the [`datetime`][datetime] module.
333+
334+
Python has other modules which work with dates and times.
335+
336+
### The [`time`][time] module
337+
Optimized for working with computer timestanps, for example in software logs.
338+
339+
Not to be confused with `datetime.time`, a completely separate class.
340+
341+
### The [`calendar`][calendar] module
342+
An alternative to `datetime.date`, `calendar` is more sophisticated in dealing with dates across a wide span of historical and future time.
343+
344+
It also has CSS methods to halp with displaying calendars.
345+
346+
347+
### The [`zoneinfo`][zoneinfo] module
348+
Mainly consisting of the `ZoneInfo` class, a subclass of `datetime.tzinfo` which supports the [IANA database][IANA] and automatic DST adjustments.
349+
350+
351+
352+
[ISO8601]: https://en.wikipedia.org/wiki/ISO_8601
353+
[datetime]: https://docs.python.org/3/library/datetime.html
354+
[datetime-date]: https://docs.python.org/3/library/datetime.html#date-objects
355+
[datetime-time]: https://docs.python.org/3/library/datetime.html#time-objects
356+
[datetime-datetime]: https://docs.python.org/3/library/datetime.html#datetime-objects
357+
[datetime-timedelta]: https://docs.python.org/3/library/datetime.html#timedelta-objects
358+
[datetime-tzinfo]: https://docs.python.org/3/library/datetime.html#tzinfo-objects
359+
[datetime-timezone]: https://docs.python.org/3/library/datetime.html#timezone-objects
360+
[strptime-strftime]: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior
361+
[time]: https://docs.python.org/3/library/time.html
362+
[calendar]: https://docs.python.org/3/library/calendar.html
363+
[ABC]: https://docs.python.org/3/library/abc.html
364+
[zoneinfo]: https://docs.python.org/3/library/zoneinfo.html
365+
[tzdata]: https://peps.python.org/pep-0615/
366+
[IANA]: https://en.wikipedia.org/wiki/Tz_database
367+
[IANA-names]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
368+
369+

concepts/datetime/introduction.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#TODO: Add introduction for this concept.

concepts/datetime/links.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"url": "https://docs.python.org/3/library/datetime.html/",
4+
"description": "The datetime module, containing date, time, datetime, timeddelta and tzinfo classes."
5+
},
6+
{
7+
"url": "https://docs.python.org/3/library/time.html/",
8+
"description": "The time module, optimized for working with POSIX timestamps."
9+
},
10+
{
11+
"url": "https://en.wikipedia.org/wiki/Tz_database/",
12+
"description": "A summary of information of timezone datebases."
13+
},
14+
{
15+
"url": "https://docs.python.org/3/library/calendar.html/",
16+
"description": "The calendar module, for more complex date-only applications."
17+
}
18+
]

0 commit comments

Comments
 (0)