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, ...).