2.2. Datetime Parsing and Formatting

2.2.2. Date format in USA

Formal date format in USA [StdlibWik19b]:

4/12/61
April 12, 1961

2.2.3. Date format in Japan

Formal date format in Japan [StdlibWik19a]:

20/12/31

2.2.4. Date format in Germany

Formal date format in Germany:

12.04.1961

2.2.5. Date format in Poland

12.4.1961
12.04.1961

12 IV 1961
12.IV.1961

12/4/1961
12/04/1961

12 kwietnia 1961
12 kwiecień 1961

2.2.6. Time formats

7/21/69 2:53
7/21/69 02:53
7/21/69 02:53:00
7/21/69 02:53:0
07/21/69 02:53:0
07/21/69 02:53:00

2.2.7. 24 and 12 hour clock

  • What AM stands for?

  • What PM stands for?

17:00
5:00 PM

2.2.8. Noon and Midnight

  • Which time is a midnight?

  • Which time is a noon?

  • Confusion at noon and midnight

  • Is 12:00 a noon (in 24h format), or someone just simply forgot to put AM/PM?

12:00 am
12:00 pm

12:00
24:00

00:00
0:00

2.2.9. Times after 24:00

23:59:59
23:59:60
24:00
24:01
25:00
27:45
14:00-30:00

2.2.10. Military time

1200J
1200Z

2.2.11. ISO 8601 Standard

2.2.12. Dates

1961-04-12

2.2.13. Date and time

  • "Z" (Zulu) means UTC

  • "T" separates date and time

Date and time:

1961-04-12T06:07:00Z
1961-04-12T06:07:00.123Z
1961-04-12T06:07:00.123456Z

2.2.14. Noon and Midnight

  • "00:00" - midnight, at the beginning of a day

  • "24:00" - midnight, at the end of a day (not recommended)

  • "2007-04-05T24:00" is equal to "2007-04-06T00:00"

2.2.15. Weeks

Note year/month changes during the week:

2009-W01            # First week of 2009
2009-W01-1          # Monday 29 December 2008
2009-W53-7          # Sunday 3 January 2010

2.2.16. Timezone

  • "Z" (Zulu) means UTC

Time zone notation:

<time>UTC
<time>Z
<time>±hh:mm
<time>±hhmm
<time>±hh

2.2.17. Duration

  • Format: P[n]Y[n]M[n]DT[n]H[n]M[n]S

Table 2.1. Duration format

Format

Designator

Description

P

duration (period)

placed at the start of the duration representation

Y

year

number of years

M

month

number of months

W

week

number of weeks

D

day

number of days

T

time

precedes the time components of the representation

H

hour

number of hours

M

minute

number of minutes

S

second

number of seconds

Example:

P8Y3M8DT20H49M15S

Period of:

  • 8 years

  • 3 months

  • 8 days

  • 20 hours

  • 49 minutes

  • 15 seconds

2.2.18. Date and time parsing and formatting parameters

Almost any programming language has very similar date formatting parameters. There are only some minor differences like in JavaScript minutes are i, not M.

Table 2.2. Date and time parsing and formatting parameters

Directive

Example

Meaning

%a

Sun, Mon, …, Sat

Weekday as locale's abbreviated name

%A

Sunday, Monday, …, Saturday (en_US)

Weekday as locale's full name

%w

0, 1, …, 6

Weekday as a decimal number, where 0 is Sunday and 6 is Saturday

%d

01, 02, …, 31

Day of the month as a zero-padded decimal number

%b

Jan, Feb, …, Dec (en_US)

Month as locale's abbreviated name

%B

January, February, …, December (en_US)

Month as locale’s full name

%m

01, 02, …, 12

Month as a zero-padded decimal number

%y

00, 01, …, 99

Year without century as a zero-padded decimal number

%Y

0001, 0002, …, 2013, 2014, …, 9998, 9999

Year with century as a decimal number

%H

00, 01, …, 23

Hour (24-hour clock) as a zero-padded decimal number

%I

01, 02, …, 12

Hour (12-hour clock) as a zero-padded decimal number

%p

AM, PM (en_US)

Locale’s equivalent of either AM or PM

%M

00, 01, …, 59

Minute as a zero-padded decimal number

%S

00, 01, …, 59

Second as a zero-padded decimal number

%f

000000, 000001, …, 999999

Microsecond as a decimal number, zero-padded on the left

%z

(empty), +0000, -0400, +1030

UTC offset in the form +HHMM or -HHMM (empty string if the object is naive)

%Z

(empty), UTC, EST, CST

Time zone name (empty string if the object is naive)

%j

001, 002, …, 366

Day of the year as a zero-padded decimal number

%U

00, 01, …, 53

Week number of the year (Sunday as the first day of the week) as a zero padded decimal number. All days in a new year preceding the first Sunday are considered to be in week 0

%W

00, 01, …, 53

Week number of the year (Monday as the first day of the week) as a decimal number. All days in a new year preceding the first Monday are considered to be in week 0

%c

Tue Aug 16 21:30:00 1988 (en_US)

Locale’s appropriate date and time representation

%x

08/16/1988 (en_US); 16.08.1988 (de_DE)

Locale’s appropriate date representation

%X

21:30:00

Locale’s appropriate time representation

%%

%

A literal % character

%G

0001, 0002, …, 2013, 2014, …, 9998, 9999

ISO 8601 year with century representing the year that contains the greater part of the ISO week (%V)

%u

1, 2, …, 7

ISO 8601 weekday as a decimal number where 1 is Monday

%V

01, 02, …, 53

ISO 8601 week as a decimal number with Monday as the first day of the week. Week 01 is the week containing Jan 4

2.2.19. Date formatting

2.2.20. ISO Format

Datetime formatting to ISO format:

from datetime import datetime

dt = datetime(1969, 7, 21, 2, 56, 15)

dt.isoformat()
# 1969-07-21T02:56:15

Date formatting to ISO format:

from datetime import date

d = date(1969, 7, 21)

d.isoformat()
# 1969-07-21

2.2.21. f-string formatting

Datetime formatting as string with f'...':

from datetime import datetime

gagarin = datetime(1961, 4, 12, 6, 7)

print(f'Gagarin launched on {gagarin:%Y-%m-%d}')
# Gagarin launched on 1961-04-12

Datetime formatting as string with f'...':

from datetime import datetime

gagarin = datetime(1961, 4, 12, 6, 7)

print(f'Gagarin launched on {gagarin:%Y-%m-%d %H:%M}')
# Gagarin launched on 1961-04-12 06:07

Datetime formatting as string with f'...':

from datetime import datetime

gagarin = datetime(1961, 4, 12, 6, 7)
format = '%Y-%m-%d %H:%M'

print(f'Gagarin launched on {gagarin:{format}}')
# Gagarin launched on 1961-04-12 06:07

2.2.22. Format to string

Datetime formatting as string with .strftime():

from datetime import datetime

gagarin = datetime(1961, 4, 12, 6, 7)
formatted = gagarin.strftime('%Y-%m-%d %H:%M')

print(f'Gagarin launched on {formatted}')
# Gagarin launched on 1961-04-12 06:07

2.2.23. Parsing dates

  • Parsing - analyze (a sentence) into its parts and describe their syntactic roles.

Datetime parsing from string:

from datetime import datetime

sputnik = '4 October 1957, 19:28:34 [UTC]'

result = datetime.strptime(sputnik, '%d %B %Y, %H:%M:%S [%Z]')
# datetime.datetime(1957, 10, 4, 19, 28, 34)

print(result)
# 1957-10-04 19:28:34

2.2.24. Examples

from datetime import datetime


log = '1969-07-21T02:56:15.123 [WARNING] First step on the Moon'

date, level, message = log.split(maxsplit=2)
format = '%Y-%m-%dT%H:%M:%S.%f'
date = datetime.strptime(date, format)

print(date)
# 1969-07-21 02:56:15.123000

print(level)
# [WARNING]

print(message)
# First step on the Moon

2.2.25. Assignments

Code 2.27. Solution
"""
* Assignment: Datetime Parse ISO
* Complexity: easy
* Lines of code: 2 lines
* Time: 3 min

English:
    1. Use data from "Given" section (see below)
    2. Given `DATA` is in ISO format
    3. Define `a: datetime` with converted `DATA` to `datetime` object
    4. Define `b: str` with converted `a` to to ISO format
    4. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Dana `DATA` jest w formacie ISO
    3. Zdefiniuj `a: datetime` z przekonwertowaną `DATA` do obiektu `datetime`
    4. Zdefiniuj `b: str` z przekonwertowaną `a` do formatu ISO
    4. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> assert type(a) is datetime, \
    'Variable `a` has invalid type, must be a datetime'

    >>> assert type(b) is str, \
    'Variable `b` has invalid type, must be a str'

    >>> a
    datetime.datetime(1969, 7, 21, 2, 56, 15, 123000)

    >>> b
    '1969-07-21T02:56:15.123000'
"""


# Given
from datetime import datetime


DATA = '1969-07-21T02:56:15.123'

a = ...  # datetime: representing DATA
b = ...  # str: `a` formatted as '1969-07-21T02:56:15.123000'

Code 2.28. Solution
"""
* Assignment: Datetime Parse US
* Complexity: easy
* Lines of code: 3 lines
* Time: 3 min

English:
    1. Use data from "Given" section (see below)
    2. Given `DATA` is in long US date and time format
    3. Define `a: datetime` with converted `DATA` to `datetime` object
    4. Define `b: str` with converted `a` to `1961-04-12 06:07` format
    4. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Dana `DATA` jest w długim formacie amerykańskim
    3. Zdefiniuj `a: datetime` z przekonwertowaną `DATA` do obiektu `datetime`
    4. Zdefiniuj `b: str` z przekonwertowaną `a` do formatu `1961-04-12 06:07`
    4. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Tests:
    >>> assert type(a) is datetime, \
    'Variable `a` has invalid type, must be a datetime'

    >>> assert type(b) is str, \
    'Variable `b` has invalid type, must be a str'

    >>> a
    datetime.datetime(1961, 4, 12, 6, 7)

    >>> b
    '1961-04-12 06:07'
"""


# Given
from datetime import datetime


DATA = 'April 12, 1961 6:07 local time'

a = ...  # datetime: representing DATA
b = ...  # str: `a` formatted as '1961-04-12 06:07'


Code 2.29. Solution
"""
* Assignment: Datetime Parse Local
* Complexity: easy
* Lines of code: 1 lines
* Time: 3 min

English:
    1. Use data from "Given" section (see below)
    3. Create `datetime` object by parsing `DATA`
    4. Using date formatting converts `DATA` to string and assign to `result`
       in american short date format (np. '07/21/69 2:56 AM')
    5. Make sure, that hour is without leading zero
    6. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    3. Stwórz obiekt `datetime` parsując `DATA`
    4. Używając parametrów formatowania daty przekonwertuj `DATA` do stringa
       i zapisz do `result` w formacie amerykańskim krótkim (np. '07/21/69 2:56 AM')
    5. Upewnij się, że godzina jest bez wiodącego zera
    6. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Hints:
    * Add string `local time` to format statement

Hints:
    * Add quote sign `"` like normal text to `fmt` parameter of `.strptime()`
    * `%dst`
    * Use `%-I` or `%_I` on \*nix systems (macOS, BSD, Linux) to remove leading zero
    * Use `%#I` on Windows to remove leading zero

Tests:
    >>> assert type(result) is datetime, \
    'Variable `result` has invalid type, must be a datetime'

    >>> result
    datetime.datetime(1969, 7, 21, 2, 56, 15)
"""


# Given
from datetime import datetime


DATA = 'July 21st, 1969 2:56:15 AM local time'

result = ...  # datetime: representation of DATA


Code 2.30. Solution
"""
* Assignment: Datetime Parse ZeroPadded
* Complexity: easy
* Lines of code: 5 lines
* Time: 3 min

English:
    1. Use data from "Given" section (see below)
    3. Create `datetime` object by parsing `DATA`
    4. Using date formatting converts `DATA` to string and assign to `result`
       in american short date format (np. '07/21/69 2:56 AM')
    5. Make sure, that hour is without leading zero
    6. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    3. Stwórz obiekt `datetime` parsując `DATA`
    4. Używając parametrów formatowania daty przekonwertuj `DATA` do stringa
       i zapisz do `result` w formacie amerykańskim krótkim (np. '07/21/69 2:56 AM')
    5. Upewnij się, że godzina jest bez wiodącego zera
    6. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Hints:
    * Add string `local time` to format statement

Hints:
    * Add quote sign `"` like normal text to `fmt` parameter of `.strptime()`
    * `%dst`
    * Use `%-I` or `%_I` on \*nix systems (macOS, BSD, Linux) to remove leading zero
    * Use `%#I` on Windows to remove leading zero

Tests:
    >>> assert type(result) is str, \
    'Variable `result` has invalid type, must be a str'

    >>> result
    '07/21/69 2:56 AM'
"""


# Given
from datetime import datetime


DATA = 'July 21st, 1969 2:56:15 AM'

result = ...  # str: DATA formatted in short US format: '07/21/69 2:56 AM'


Code 2.31. Solution
"""
* Assignment: Datetime Parse Formats
* Complexity: medium
* Lines of code: 8 lines
* Time: 8 min

English:
    1. Use data from "Given" section (see below)
    2. List `DATA` has dates in multiple formats
    3. Define `result: list` with converted `DATA` elements
       to `datetime` objects
    4. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Lista `DATA` ma dane w różnych formatach
    3. Zdefiniuj `result: list` z przekonwertowanymi elementami `DATA`
       do obiektów `datetime`
    4. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Hints:
    * `try ... except`

Tests:
    >>> assert type(result) is list, \
    'Variable `result` has invalid type, must be a list'

    >>> assert all(type(element) is datetime for element in result), \
    'All elements in `result` must be a datetime'

    >>> result  # doctest: +NORMALIZE_WHITESPACE
    [datetime.datetime(1961, 4, 12, 6, 7),
     datetime.datetime(1961, 4, 12, 6, 7)]
"""


# Given
from datetime import datetime


DATA = ['1961-04-12 06:07',
        '1961-04-12 06:07:00']

result = ...  # list[datetime]: DATA elements in datetime format

Code 2.32. Solution
"""
* Assignment: Datetime Parse List
* Complexity: medium
* Lines of code: 13 lines
* Time: 8 min

English:
    1. Use data from "Given" section (see below)
    2. List `DATA` has dates in multiple formats
    3. Define `result: list` with converted `DATA` elements
       to `datetime` objects
    4. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Lista `DATA` ma dane w różnych formatach
    3. Zdefiniuj `result: list` z przekonwertowanymi elementami `DATA`
       do obiektów `datetime`
    4. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

Hints:
    * `try ... except`

Tests:
    >>> assert type(result) is list, \
    'Variable `result` has invalid type, must be a list'

    >>> assert all(type(element) is datetime for element in result), \
    'All elements in `result` must be a datetime'

    >>> result  # doctest: +NORMALIZE_WHITESPACE
    [datetime.datetime(1957, 10, 4, 19, 28, 34),
     datetime.datetime(1961, 4, 12, 6, 7),
     datetime.datetime(1969, 7, 21, 2, 56, 15)]
"""


# Given
from datetime import datetime


DATA = [
    '1957-10-04 19:28:34',  # Sputnik launch (first satellite in space)
    '1961-04-12T06:07',  # Gagarin launch (first human in space)
    'July 21st, 1969 2:56:15 am',  # Armstrong first step on the Moon
]

result = ...  # list[datetime]: DATA elements in datetime format

Code 2.33. Solution
"""
* Assignment: Datetime Parse Logs
* Complexity: medium
* Lines of code: 13 lines
* Time: 13 min

English:
    1. Use data from "Given" section (see below)
    2. Iterate over `DATA` with Apollo 11 timeline [1]
    3. From each line extract date, time, level and message
    4. Collect data to `result: list[dict]`
    6. Compare result with "Tests" section (see below)

Polish:
    1. Użyj danych z sekcji "Given" (patrz poniżej)
    2. Iteruj po `DATA` z harmonogramem Apollo 11 [1]
    3. Dla każdej linii wyciągnij datę, czas, poziom logowania oraz wiadomość
    4. Zbierz dane do `result: list[dict]`
    6. Porównaj wyniki z sekcją "Tests" (patrz poniżej)

References:
    [1] National Aeronautics and Space Administration.
        Apollo 11 timeline.
        Retrieved: 2021-03-25.
        URL: https://history.nasa.gov/SP-4029/Apollo_11i_Timeline.htm

Hints:
    * `str.splitlines()`
    * `str.split(maxsplit)`
    * ` try ... except ValueError`

Tests:
    >>> assert type(result) is list, \
    'Variable `result` has invalid type, must be a list'

    >>> assert all(type(row) is dict for row in result), \
    'All elements in result must be dict'

    >>> result  # doctest: +NORMALIZE_WHITESPACE
    [{'when': datetime.datetime(1969, 7, 14, 21, 0), 'level': 'INFO', 'message': 'Terminal countdown started'},
     {'when': datetime.datetime(1969, 7, 16, 13, 31, 53), 'level': 'WARNING', 'message': 'S-IC engine ignition (#5)'},
     {'when': datetime.datetime(1969, 7, 16, 13, 33, 23), 'level': 'DEBUG', 'message': 'Maximum dynamic pressure (735.17 lb/ft^2)'},
     {'when': datetime.datetime(1969, 7, 16, 13, 34, 44), 'level': 'WARNING', 'message': 'S-II ignition'},
     {'when': datetime.datetime(1969, 7, 16, 13, 35, 17), 'level': 'DEBUG', 'message': 'Launch escape tower jettisoned'},
     {'when': datetime.datetime(1969, 7, 16, 13, 39, 40), 'level': 'DEBUG', 'message': 'S-II center engine cutoff'},
     {'when': datetime.datetime(1969, 7, 16, 16, 22, 13), 'level': 'INFO', 'message': 'Translunar injection'},
     {'when': datetime.datetime(1969, 7, 16, 16, 56, 3), 'level': 'INFO', 'message': 'CSM docked with LM/S-IVB'},
     {'when': datetime.datetime(1969, 7, 16, 17, 21, 50), 'level': 'INFO', 'message': 'Lunar orbit insertion ignition'},
     {'when': datetime.datetime(1969, 7, 16, 21, 43, 36), 'level': 'INFO', 'message': 'Lunar orbit circularization ignition'},
     {'when': datetime.datetime(1969, 7, 20, 17, 44), 'level': 'INFO', 'message': 'CSM/LM undocked'},
     {'when': datetime.datetime(1969, 7, 20, 20, 5, 5), 'level': 'WARNING', 'message': 'LM powered descent engine ignition'},
     {'when': datetime.datetime(1969, 7, 20, 20, 10, 22), 'level': 'ERROR', 'message': 'LM 1202 alarm'},
     {'when': datetime.datetime(1969, 7, 20, 20, 14, 18), 'level': 'ERROR', 'message': 'LM 1201 alarm'},
     {'when': datetime.datetime(1969, 7, 20, 20, 17, 39), 'level': 'WARNING', 'message': 'LM lunar landing'},
     {'when': datetime.datetime(1969, 7, 21, 2, 39, 33), 'level': 'DEBUG', 'message': 'EVA started (hatch open)'},
     {'when': datetime.datetime(1969, 7, 21, 2, 56, 15), 'level': 'WARNING', 'message': '1st step taken lunar surface (CDR)'},
     {'when': datetime.datetime(1969, 7, 21, 2, 56, 15), 'level': 'WARNING', 'message': "That's one small step for [a] man... one giant leap for mankind"},
     {'when': datetime.datetime(1969, 7, 21, 3, 5, 58), 'level': 'DEBUG', 'message': 'Contingency sample collection started (CDR)'},
     {'when': datetime.datetime(1969, 7, 21, 3, 15, 16), 'level': 'INFO', 'message': 'LMP on lunar surface'},
     {'when': datetime.datetime(1969, 7, 21, 5, 11, 13), 'level': 'DEBUG', 'message': 'EVA ended (hatch closed)'},
     {'when': datetime.datetime(1969, 7, 21, 17, 54), 'level': 'WARNING', 'message': 'LM lunar liftoff ignition (LM APS)'},
     {'when': datetime.datetime(1969, 7, 21, 21, 35), 'level': 'INFO', 'message': 'CSM/LM docked'},
     {'when': datetime.datetime(1969, 7, 22, 4, 55, 42), 'level': 'WARNING', 'message': 'Transearth injection ignition (SPS)'},
     {'when': datetime.datetime(1969, 7, 24, 16, 21, 12), 'level': 'INFO', 'message': 'CM/SM separation'},
     {'when': datetime.datetime(1969, 7, 24, 16, 35, 5), 'level': 'WARNING', 'message': 'Entry'},
     {'when': datetime.datetime(1969, 7, 24, 16, 50, 35), 'level': 'WARNING', 'message': 'Splashdown (went to apex-down)'},
     {'when': datetime.datetime(1969, 7, 24, 17, 29), 'level': 'INFO', 'message': 'Crew egress'}]
"""


# Given
from datetime import datetime


DATA = """1969-07-14, 21:00:00, INFO, Terminal countdown started
1969-07-16, 13:31:53, WARNING, S-IC engine ignition (#5)
1969-07-16, 13:33:23, DEBUG, Maximum dynamic pressure (735.17 lb/ft^2)
1969-07-16, 13:34:44, WARNING, S-II ignition
1969-07-16, 13:35:17, DEBUG, Launch escape tower jettisoned
1969-07-16, 13:39:40, DEBUG, S-II center engine cutoff
1969-07-16, 16:22:13, INFO, Translunar injection
1969-07-16, 16:56:03, INFO, CSM docked with LM/S-IVB
1969-07-16, 17:21:50, INFO, Lunar orbit insertion ignition
1969-07-16, 21:43:36, INFO, Lunar orbit circularization ignition
1969-07-20, 17:44:00, INFO, CSM/LM undocked
1969-07-20, 20:05:05, WARNING, LM powered descent engine ignition
1969-07-20, 20:10:22, ERROR, LM 1202 alarm
1969-07-20, 20:14:18, ERROR, LM 1201 alarm
1969-07-20, 20:17:39, WARNING, LM lunar landing
1969-07-21, 02:39:33, DEBUG, EVA started (hatch open)
1969-07-21, 02:56:15, WARNING, 1st step taken lunar surface (CDR)
1969-07-21, 02:56:15, WARNING, That's one small step for [a] man... one giant leap for mankind
1969-07-21, 03:05:58, DEBUG, Contingency sample collection started (CDR)
1969-07-21, 03:15:16, INFO, LMP on lunar surface
1969-07-21, 05:11:13, DEBUG, EVA ended (hatch closed)
1969-07-21, 17:54:00, WARNING, LM lunar liftoff ignition (LM APS)
1969-07-21, 21:35:00, INFO, CSM/LM docked
1969-07-22, 04:55:42, WARNING, Transearth injection ignition (SPS)
1969-07-24, 16:21:12, INFO, CM/SM separation
1969-07-24, 16:35:05, WARNING, Entry
1969-07-24, 16:50:35, WARNING, Splashdown (went to apex-down)
1969-07-24, 17:29, INFO, Crew egress"""

result = ...  # list[dict]: representation of DATA; dict keys: when, level, message