Convert Unix timestamps in Python: datetime, timezone-aware code, and Y2038
Python's datetime module converts Unix timestamps to dates with one call. The default is local time, which is the source of more bugs than the rest of the module combined. Timezone-aware code costs three extra characters and saves a production incident.
fromtimestamp returns local time by default
datetime.fromtimestamp(1715342400) converts a Unix timestamp (seconds since 1970-01-01 UTC) to a datetime in the local timezone of the machine running the code. On a developer laptop in Madrid, you get something like 2024-05-10 12:00:00. On a server running UTC, you get 2024-05-10 10:00:00. Same input, two different answers.
The result is also a naive datetime: it has no tzinfo attached, so any code that later asks for the timezone gets None. Comparing it to an aware datetime raises TypeError. Storing it round-trips wrong if the consuming system assumes UTC.
The fix is to pass a tz argument:
from datetime import datetime, timezone
dt = datetime.fromtimestamp(1715342400, tz=timezone.utc)
The result is now an aware datetime in UTC. On any machine, anywhere, you get the same answer.
utcfromtimestamp is deprecated in 3.12
For years the canonical answer to "give me UTC" was datetime.utcfromtimestamp(ts). As of Python 3.12 (October 2023) the function is deprecated, scheduled for removal in 3.14. The replacement is the tz=timezone.utc form above.
The reason for deprecation is exactly the bug it caused. utcfromtimestamp returned a naive datetime that happened to hold UTC values. Code downstream did not know it was UTC and treated it as local time, applying local-time offsets twice. The new API forces you to declare the zone, which propagates through the rest of the program and makes the bug impossible.
If you have an existing codebase calling utcfromtimestamp, schedule a migration. The replacement is mechanical and the deprecation warning will become an error.
.timestamp() converts the other way
To go from a datetime back to a Unix timestamp, call .timestamp() on the datetime. The method returns a float of seconds since the Unix epoch.
If the datetime is aware, the method honors its zone correctly. If the datetime is naive, the method assumes local time, which means the result depends on where the code runs. Always work in aware datetimes when crossing timestamp boundaries:
from datetime import datetime, timezone
ts = datetime(2026, 5, 10, 8, 0, tzinfo=timezone.utc).timestamp()
# 1778760000.0, same on every machine
For arbitrary IANA zones (Europe/Madrid, America/New_York), use the standard-library zoneinfo module, available since Python 3.9. dateutil.tz is the older third-party option and still works, but zoneinfo is the modern choice.
Pandas and the bulk path
When you have a column of timestamps (a CSV, a database extract), pandas.to_datetime vectorizes the conversion:
import pandas as pd
df["created_at"] = pd.to_datetime(df["unix_ts"], unit="s", utc=True)
The unit argument matters. Default is nanoseconds; webhooks and most APIs send seconds; some JS-derived APIs send milliseconds. Picking wrong shifts every timestamp by 1000 or 1e6, which produces dates in the year 56000 or 1970 depending on direction. The utc=True flag returns aware UTC datetimes, the same lesson as the stdlib path.
Y2038 and 32-bit signed time_t
Unix time fits comfortably in a 64-bit integer for billions of years. The problem is 32-bit signed time_t, which overflows on 2038-01-19 03:14:07 UTC. Python itself uses 64-bit integers internally and is not affected. Your concern is the systems Python talks to: legacy C libraries, embedded devices, ext3 filesystems, MySQL TIMESTAMP columns on older schemas.
If you store timestamps in a column declared as a 32-bit signed integer, schedule a migration before 2038. If you read from an embedded device whose firmware uses 32-bit time_t, expect wraparound behavior near the deadline. Python's int is unbounded, so the conversion functions accept any value, but the boundary you care about is on the other side of the wire.
Working example
pythonfrom datetime import datetime, timezone
from zoneinfo import ZoneInfo
# Round-trip with timezone-aware code
ts = 1778760000 # seconds since epoch
# 1. Unix timestamp -> aware UTC datetime
dt_utc = datetime.fromtimestamp(ts, tz=timezone.utc)
print(dt_utc) # 2026-05-10 08:00:00+00:00
# 2. Convert to a specific IANA zone for display
dt_madrid = dt_utc.astimezone(ZoneInfo("Europe/Madrid"))
print(dt_madrid) # 2026-05-10 10:00:00+02:00 (CEST)
# 3. Aware datetime -> Unix timestamp
ts_back = dt_utc.timestamp()
print(ts_back == ts) # True
# 4. Pandas bulk path for a column of seconds
import pandas as pd
df = pd.DataFrame({"unix_ts": [1778760000, 1778763600]})
df["dt"] = pd.to_datetime(df["unix_ts"], unit="s", utc=True)
print(df) Just need the result?
When you have a Unix timestamp from a webhook or a log line and want to see the date in your local zone without writing two lines of code, the timestamp converter at aldeacode.com renders the result instantly, supports seconds and milliseconds, and runs in your browser without sending the value anywhere.
Open Unix Timestamp Converter →Frequently asked questions
Why does fromtimestamp give me a different date on my laptop and on the server?
Without a tz argument, fromtimestamp returns local time. The two machines are in different zones. Pass tz=timezone.utc, or any explicit zone, and both machines return the same datetime.
Is utcfromtimestamp safe to keep using?
On 3.11 and earlier yes, but it is deprecated in 3.12 and slated for removal in 3.14. Replace it with datetime.fromtimestamp(ts, tz=timezone.utc). The replacement is one line per call site and the result is an aware datetime, which downstream code handles correctly.
Should I worry about Y2038 in Python code I write today?
Not in Python itself. Worry about the systems Python integrates with: 32-bit C libraries, embedded firmware, legacy database column types. Audit those before 2038 and widen them to 64-bit storage. Python's int is unbounded.