Python epoch/timestamp converter

Convert between Unix epoch seconds and Python datetime objects, with copyable code for both directions.

Converting timestamps in Python

Python represents a Unix timestamp — the number of seconds elapsed since 1970-01-01T00:00:00Z — as a plain float or int, and gives you two different toolkits for working with one. The low-level time module talks almost exclusively in epoch seconds, while the higher-level datetime module represents an instant as a datetime object with year, month, day, hour, minute and second fields. Converting between the two is one of the most common small tasks in backend code: an API returns created_at as an epoch integer, a database column stores updated_at as a datetime, and a log line wants both a human-readable stamp and a sortable numeric one. The conversion itself is simple — time.time() or datetime.now().timestamp() to go forward, datetime.fromtimestamp() to go back — but it hides two traps that catch even experienced Python developers: naive versus timezone-aware datetime objects, and the difference between a value's local interpretation and its UTC interpretation. Get either wrong and a timestamp that looks correct in a REPL will render several hours off once it reaches a server in a different timezone. This page works through the calls you actually need, with runnable code for each direction and for the two gotchas that most often turn "just convert this number" into a debugging session before it does.

Get the current Unix timestamp

time.time() returns the current epoch value directly, as a float. There is no timezone to get wrong here — it is just a count of seconds since 1970 UTC.

import time

epoch = time.time()        # 1786795200.123456 (float, seconds)
epoch_int = int(time.time())  # 1786795200 (whole seconds)

Convert a datetime to a Unix timestamp

Call .timestamp() on a datetime object. This is where the naive-vs-aware distinction first bites: a naive datetime (no tzinfo) is assumed to be in your machine's local timezone when .timestamp() runs.

from datetime import datetime, timezone

# Aware datetime — unambiguous, converts correctly anywhere
dt = datetime(2026, 8, 18, 12, 0, 0, tzinfo=timezone.utc)
epoch = dt.timestamp()     # 1786795200.0

# Naive datetime — Python assumes LOCAL time, not UTC
naive = datetime(2026, 8, 18, 12, 0, 0)
epoch = naive.timestamp()  # differs by your UTC offset

Convert a Unix timestamp to a datetime

Call datetime.fromtimestamp(), and pass tz=timezone.utc explicitly unless you actually want the result in your machine's local timezone.

from datetime import datetime, timezone

epoch = 1786795200

# Timezone-aware, in UTC — reproducible on any machine
dt_utc = datetime.fromtimestamp(epoch, tz=timezone.utc)
print(dt_utc)  # 2026-08-15 04:00:00+00:00

# Naive, in the machine's LOCAL timezone — NOT portable
dt_local = datetime.fromtimestamp(epoch)

For a whole column rather than one value, pandas.to_datetime() is the call — and its unit argument is where the same seconds-versus-milliseconds bug lives. It defaults to unit='ns', so a column of ten-digit epoch seconds handed over unmarked is read as nanoseconds and lands in 1970 with no error raised:

import pandas as pd

s = pd.Series([1786795200, 1786795260])

pd.to_datetime(s, unit='s')            # 2026-08-15 04:00:00 — correct
pd.to_datetime(s, unit='ms')           # 1970-01-21 — a millisecond column's setting
pd.to_datetime(s)                      # 1970-01-01 00:00:01.786795200 — the default, in ns

# Aware, and the reverse direction
dt = pd.to_datetime(s, unit='s', utc=True)
dt.astype('int64') // 10**9            # the column back to epoch seconds

Format a datetime with strftime

datetime.fromtimestamp() hands you an object; .strftime() turns that object into whatever string a log line, a filename or a CSV column actually needs. It is a method on the datetime rather than a module-level function — SQL's strftime('%s', …) is a different call that happens to share the name, and it goes towards an epoch value rather than away from one. Format an aware datetime and the %z and %Z codes fill in; format a naive one and both come out empty, which is the quickest way to spot a datetime that lost its timezone somewhere upstream.

from datetime import datetime, timezone

dt = datetime.fromtimestamp(1786795200, tz=timezone.utc)

dt.strftime('%Y-%m-%d %H:%M:%S')      # 2026-08-15 04:00:00
dt.strftime('%Y-%m-%dT%H:%M:%S%z')    # 2026-08-15T04:00:00+0000
dt.strftime('%d %b %Y, %I:%M %p %Z')  # 15 Aug 2026, 04:00 AM UTC

# The other direction: a formatted string back to epoch seconds
parsed = datetime.strptime('2026-08-15 04:00:00', '%Y-%m-%d %H:%M:%S')
epoch = parsed.replace(tzinfo=timezone.utc).timestamp()  # 1786795200.0

Handling timezones

For a specific named timezone rather than UTC or the system default, use zoneinfo (standard library since Python 3.9). Avoid datetime.utcfromtimestamp() and datetime.utcnow() — both are deprecated as of Python 3.12 because they return a naive datetime that merely contains UTC values, which silently reintroduces the local-time bug the moment it is compared against an aware datetime. If you just want to see what one epoch value looks like in a given zone before writing the zoneinfo call, the UTC-to-local timezone converter renders it with the DST rules for that exact date already applied.

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

epoch = 1786795200

utc_dt = datetime.fromtimestamp(epoch, tz=timezone.utc)
ny_dt = utc_dt.astimezone(ZoneInfo("America/New_York"))
print(ny_dt)  # same instant, New York wall-clock time

Common gotchas

Frequently asked questions

How do I get the current Unix timestamp in Python?

Call time.time() from the standard library time module. It returns the current Unix timestamp as a float, in seconds since 1970-01-01T00:00:00Z. If you need it as a whole number, wrap it in int(time.time()).

How do I convert a Python datetime to a Unix timestamp?

Call .timestamp() on the datetime object: epoch = dt.timestamp(). If the datetime is naive (no tzinfo), Python assumes it is in your system's local timezone when doing this conversion, which is rarely what you want for data that crosses machines — attach tzinfo=timezone.utc first if the datetime represents UTC.

How do I convert a Unix timestamp to a Python datetime?

Call datetime.fromtimestamp(epoch, tz=timezone.utc) to get a timezone-aware datetime in UTC. Omitting the tz argument returns a naive datetime in your system's local timezone instead, which is the single most common source of off-by-several-hours bugs in Python timestamp code.

Why does datetime.fromtimestamp() give the wrong time?

Almost always because tz was omitted. Without tz=timezone.utc, fromtimestamp() converts the epoch value into your machine's local timezone, not UTC. The same epoch number will print a different wall-clock time on a server in a different timezone unless you pin tz explicitly.

What is the difference between time.time() and datetime.now()?

time.time() always returns a float epoch value in UTC seconds — there is no timezone ambiguity because it is just a number. datetime.now() returns a naive datetime in local time by default; pass datetime.now(timezone.utc) to get an aware datetime in UTC instead.

Where to go next

The same round trip in another language reads a little differently: converting epoch values in JavaScript works in milliseconds rather than seconds, and the SQL epoch functionsFROM_UNIXTIME(), to_timestamp(), strftime('%s', …) — do it in the query rather than in Python. If the number you are about to pass to fromtimestamp() came from somewhere you do not control, check whether it is seconds or milliseconds first.