How to get the current date and time in Python
Getting the current time is one line. Getting a value that still means something tomorrow, on another machine, in another timezone, takes a little more thought. This article covers both.
The answer
from datetime import datetime, UTC
now = datetime.now(UTC)
datetime.now(UTC) : 2026-08-29 06:23:39.621099+00:00
aware.tzinfo : UTC
Note the argument. Without it you get something different:
datetime.now() : 2026-08-29 08:23:39.621096
naive.tzinfo : None
Two hours apart, and the second one carries no record of which two hours.
naive and aware
Python has two kinds of datetime. An aware one knows its offset from UTC.
A naive one is a wall clock reading with no note of which wall.
datetime.now() gives you a naive one. That is fine for a log line a human
will read on the same machine, and a problem for anything you store, transmit,
or compare later — because nothing in the value says whether 08:23 was
Vienna, London or UTC.
Python will not let you mix them:
naive - aware -> TypeError: can't subtract offset-naive and offset-aware datetimes
Which is the right call, and is usually where people discover the distinction.
utcnow() is deprecated
You will find this in most older answers:
datetime.utcnow()
Do not use it. Current Python says so itself:
DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for
removal in a future version. Use timezone-aware objects to represent datetimes
in UTC: datetime.datetime.now(datetime.UTC).
The reason it was a bad idea:
the value : 2026-08-29 06:23:39.624877
its tzinfo : None <- UTC time, but labelled as naive
It gave you the UTC time and then failed to say so. So the object claims to be
local time while holding UTC — and any later .timestamp() or comparison
against local time is wrong by your offset. Replace it with
datetime.now(UTC).
Real timezones come with Python
Since 3.9 you do not need a third-party package for this. Use
zoneinfo, added by
PEP 615:
from zoneinfo import ZoneInfo
vienna = datetime.now(ZoneInfo("Europe/Vienna"))
datetime.now(ZoneInfo('Europe/Vienna')) : 2026-08-29 08:23:39.625011+02:00
.tzname() : CEST
.utcoffset() : 2:00:00
The pytz you see in older answers still works but has a different and
error-prone API. New code should use zoneinfo.
A fixed offset is not a timezone
Tempting, and wrong:
Vienna on 2026-07-01 : offset 2:00:00 (CEST)
Vienna on 2026-01-01 : offset 1:00:00 (CET)
A hardcoded +02:00 is correct for roughly half the year. Europe/Vienna
carries the rules; +02:00 carries one of the answers those rules produce.
The hour that happens twice
Here is the concrete reason a naive local datetime cannot be trusted. On the night the clocks go back, one wall clock reading occurs twice:
2026-10-25 02:30 in Vienna happens twice:
fold=0 : offset 2:00:00 epoch 1792888200
fold=1 : offset 1:00:00 epoch 1792891800
difference : 3600 seconds apart
Same date, same time, two moments an hour apart. The fold attribute, from
PEP 495, is what distinguishes them.
If you store local wall clock time, this row of your database is genuinely ambiguous and no amount of later processing can recover which one it was. Store UTC and convert for display.
Formatting
isoformat() : 2026-08-29T14:05:09+00:00
strftime('%d.%m.%Y %H:%M') : 29.08.2026 14:05
f-string with format spec : 2026-08-29 14:05:09 UTC
equal after round-trip : True
For anything machine-readable use
isoformat(),
because
fromisoformat()
reads it straight back, offset included. For anything a person reads, use
strftime or a format spec in an f-string — they take the same codes.
Just the date, or just the time
datetime.now().date() : 2026-08-29
datetime.now().time() : 08:23:39.625103
today() is naive too : None
Note the last line: datetime.today() is naive as well, despite sounding
more decisive than now().
Do not measure durations with a clock
time.perf_counter() delta : 0.0501 s
datetime.now() reads the wall clock, and a wall clock can move backwards —
NTP corrections, a manual change, a DST transition. Subtracting two readings
can therefore give you a negative duration.
For measuring how long something took, use
time.perf_counter()
or time.monotonic(). Neither can go backwards. Neither tells you the date,
which is the point — they measure intervals, not moments.
Unix timestamps
time.time() : 1787984619.6251163
datetime.fromtimestamp(ts, UTC) : 2026-08-29 06:23:39.625116+00:00
aware.timestamp() : 1787984619.621099
A timestamp is always UTC by definition, so it is a perfectly good storage
format. Two things to watch: pass the timezone to fromtimestamp() or you get
a naive local result, and naive.timestamp() assumes the value was local time —
which, if it actually held UTC, is wrong by your offset.
About Netcup (advertisement)
The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).