Pattern format

Custom format and parse patterns allow you to format datetime values into strings and parse strings into datetime values, using a pattern string that describes the expected format.

Quick example

>>> from whenever import Date, Time, OffsetDateTime, hours
>>> Date(2024, 3, 15).format("YYYY/MM/DD")
'2024/03/15'
>>> Date.parse("2024/03/15", format="YYYY/MM/DD")
Date("2024-03-15")
>>> OffsetDateTime(2024, 3, 15, 14, 30, offset=+2).format(
...     "EEE, DD MMM YYYY hh:mm:ssxxx"
... )
'Fri, 15 Mar 2024 14:30:00+02:00'

Specifiers

Each pattern is a string containing specifiers and literal text. Specifiers are sequences of the same letter that are replaced by the corresponding value.

Date specifiers

Symbol

Meaning

Pattern

Example output

Y

year

YY [1]
YYYY

24
2024

M

month

M
MM
MMM
MMMM

3
03
Mar
March

D

day of month

D
DD

5
05

E

day of week [2]

EEE
EEEE

Fri
Friday

Time specifiers

Symbol

Meaning

Pattern

Example output

h

hour

h
hh

4
04

i

hour (12-hour)

i
ii

4
04

m

minute

m
mm

5
05

s

second

s
ss

5
05

S

second, optional [3]

SS

05, (omitted)

f

fractional seconds, exact digits

f
ff
fff

fffffffff

1
12, 00
123, 400

123456789, 374930000

F

fractional seconds, trimmed [4]

F
FF
FFF

FFFFFFFFF

1
12, (omitted)
123, 4

123456789, 37493

a

AM/PM [5]

a
aa

P
PM

Optional seconds

SS omits the seconds component entirely when both seconds and nanoseconds are zero, allowing compact times like 14:30 alongside full times like 14:30:05 in the same format string.

  • When seconds or nanoseconds are non-zero, SS writes two zero-padded digits

  • When both are zero, nothing is written. Any preceeding colon disappears as well.

>>> Time(14, 30, 0).format("hh:mm:SS")
'14:30'
>>> Time(14, 30, 5).format("hh:mm:SS")
'14:30:05'
>>> Time(14, 30, 0, nanosecond=500_000_000).format("hh:mm:SS")
'14:30:00'
>>> Time(14, 30, 0).format("hh:mm:SS.FFF")
'14:30'
>>> Time(14, 30, 0, nanosecond=500_000_000).format("hh:mm:SS.FFF")
'14:30:00.5'

Offset and timezone specifiers

See Timezones for background on timezones, offsets, and abbreviations.

Symbol

Meaning

Pattern

Example output

x

Offset hours and minutes

x
xx
xxx
xxxx
xxxxx

+02
+0230
+02:30
+023045
+02:30:45

X

Offset hours and minutes, with Z for zero offset

X
XX
XXX
XXXX
XXXXX

+02
+0230
+02:30
+023045
+02:30:45 or Z when zero

V

IANA timezone ID

VV

Europe/Paris

z

Timezone abbreviation [6]

zz

CET, CEST

Choosing between x and X

Use uppercase X when you want Z for zero offset (e.g. Instant formatting). Use lowercase x when you always want a numeric offset (e.g. OffsetDateTime formatting).

>>> ZonedDateTime(2024, 7, 15, 14, 30, tz="Europe/Paris").format(
...     "YYYY-MM-DD hh:mm zz"
... )
'2024-07-15 14:30 CEST'
>>> ZonedDateTime.parse(
...     "2024-07-15 14:30+02:00[Europe/Paris]",
...     format="YYYY-MM-DD hh:mmxxx'['VV']'",
... )
ZonedDateTime("2024-07-15 14:30:00+02:00[Europe/Paris]")

Supported specifiers per type

Type

Date

Time

x/X

VV/zz

Date

Time

PlainDateTime

OffsetDateTime

ZonedDateTime

Instant

Literal text

Common non-letter characters (:, -, /, ., ,, ;, _, (, ), digits, spaces, and other ASCII punctuation) are treated as literals by default:

>>> Date(2024, 3, 15).format("YYYY/MM/DD")
'2024/03/15'

Letters must be quoted with single quotes to be used as literals. This prevents accidental use of reserved characters and keeps options open for future specifiers:

>>> Date(2024, 3, 15).format("YYYY'xx'MM")
'2024xx03'

To include a literal single quote, use '':

>>> Date(2024, 3, 15).format("YYYY''MM")
"2024'03"

Restrictions

  • ASCII-only: Pattern strings must contain only ASCII characters. Non-ASCII characters raise ValueError.

  • Reserved characters: <, >, [, ], {, }, and # are reserved for future use and cannot appear unquoted.

  • No duplicate fields: A pattern cannot contain two specifiers that set the same value. For example, MM and MMM both set the month, so "DD MM MMM YYYY" is invalid.

Parsing requirements

Some types require specific fields in the parse pattern:

All types that include date fields require YYYY, MM, and DD.

A second value of 60 (leap second) is accepted and normalized to 59. See Are leap seconds supported? for details.

Comparison with strftime

The parse_strptime() methods on OffsetDateTime and PlainDateTime are deprecated in favor of parse(). Here’s a migration guide:

strftime

Pattern

Notes

%Y

YYYY

%y

YY

Format only

%m

MM

%b

MMM

%B

MMMM

%d

DD

%a

EEE

%A

EEEE

%H

hh

Note: hh = 24-hour

%I

ii

Note: ii = 12-hour

%M

mm

%S

ss

%f

ffffff

microseconds (6 digits)

%p

aa

%z

xxxx

XXXX for Z-style

%:z

xxxxx

XXXXX for Z-style

%Z

Abbreviations are not supported for parsing. See Timezones.