Quick answer
If you are on GNU/Linux and just need the command, copy one of these:
| Task | GNU/Linux command | Output for fixed example |
|---|---|---|
| Current Unix seconds | date +%s |
current 10-digit timestamp |
| Current Unix milliseconds | date +%s%3N |
current 13-digit timestamp |
| Epoch seconds to UTC date | date -u -d @1700000000 +'%Y-%m-%dT%H:%M:%SZ' |
2023-11-14T22:13:20Z |
| Epoch seconds to local date | date -d @1700000000 |
local timezone output |
| Epoch seconds to New York time | TZ=America/New_York date -d @1700000000 +'%Y-%m-%d %H:%M:%S %Z' |
2023-11-14 17:13:20 EST |
| UTC date to Unix seconds | date -u -d '2023-11-14 22:13:20 UTC' +%s |
1700000000 |
| ISO date to Unix seconds | date -u -d '2023-11-14T22:13:20Z' +%s |
1700000000 |
| 13-digit ms to date | date -u -d @$(awk 'BEGIN { print 1700000000000 / 1000 }') |
Tue Nov 14 22:13:20 UTC 2023 |
Use the browser Epoch to Time Converter when you are checking an unknown timestamp by eye. Use the Date to Epoch Converter when you start with a calendar date and need seconds or milliseconds.
Know which date command you have
The awkward part is not Unix time. It is that date is not the same command everywhere.
| Environment | Common implementation | What to remember |
|---|---|---|
| Ubuntu, Debian, Fedora, RHEL | GNU coreutils date |
supports -d, @SECONDS, %N, --rfc-3339, and --iso-8601 |
| macOS | BSD date |
use -r SECONDS for epoch-to-date and -j -f FORMAT INPUT for parsing |
| FreeBSD | BSD date |
close to macOS for the examples in this guide |
| Alpine or tiny CI images | often BusyBox date |
smaller option set; test the exact image before shipping scripts |
| macOS with coreutils | GNU gdate |
use gdate for Linux-compatible commands |
This is why a command from a Linux answer can fail on a Mac with date: illegal option -- d, and why date -r 1700000000 means different things on Linux and macOS. On GNU date, -r means "show a file's modification time." On BSD date, -r reads epoch seconds.
Convert epoch seconds to a date on Linux
GNU date accepts Unix seconds with an @ prefix. Add -u when you want UTC output that stays the same on laptops, servers, Docker containers, and CI jobs.
# UTC date and time
date -u -d @1700000000
# ISO 8601 style UTC output
date -u -d @1700000000 +'%Y-%m-%dT%H:%M:%SZ'
# 2023-11-14T22:13:20Z
# RFC 3339 output, GNU date only
date -u -d @1700000000 --rfc-3339=seconds
# 2023-11-14 22:13:20+00:00
Omit -u only when you intentionally want the machine's local timezone:
date -d @1700000000
Use TZ=... when the script needs a named display timezone without changing the host clock:
TZ=America/New_York date -d @1700000000 +'%Y-%m-%d %H:%M:%S %Z'
# 2023-11-14 17:13:20 EST
TZ=Asia/Tokyo date -d @1700000000 +'%Y-%m-%d %H:%M:%S %Z'
# 2023-11-15 07:13:20 JST
That TZ= prefix affects this one command. It does not rewrite the stored Unix timestamp.
Convert a date to Unix timestamp on Linux
The reverse command is date -d INPUT +%s. The output format +%s means seconds since 1970-01-01 00:00:00 UTC.
# UTC datetime to Unix seconds
date -u -d '2023-11-14 22:13:20 UTC' +%s
# 1700000000
# ISO 8601 input to Unix seconds
date -u -d '2023-11-14T22:13:20Z' +%s
# 1700000000
For a local business timezone, set TZ explicitly:
TZ=America/Los_Angeles date -d '2023-11-14 14:13:20' +%s
# 1700000000
Avoid ambiguous date strings in scripts:
# Avoid: depends on locale, habit, and future readers
date -d '11/14/23' +%s
# Better: explicit order and timezone
date -u -d '2023-11-14 22:13:20 UTC' +%s
If the date is a boundary for logs, analytics, or SQL, prefer half-open ranges:
start=$(date -u -d '2026-07-01 00:00:00 UTC' +%s)
end=$(date -u -d '2026-08-01 00:00:00 UTC' +%s)
printf 'created_at >= %s AND created_at < %s\n' "$start" "$end"
This avoids double-counting events exactly at midnight.
Get current Linux time in seconds, milliseconds, or ISO UTC
Use seconds when you are talking to Unix tools, shell scripts, PHP time(), Python int(time.time()), many database filters, and APIs that document Unix seconds.
date +%s
Use milliseconds only when the receiving system expects them, such as JavaScript Date, Java currentTimeMillis, frontend event logs, or an API that shows 13-digit examples.
# GNU date only
date +%s%3N
For a readable timestamp in logs, print ISO UTC:
date -u +'%Y-%m-%dT%H:%M:%SZ'
A useful production log line includes both:
now_s=$(date +%s)
now_iso=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
printf 'time=%s epoch=%s event=%s\n' "$now_iso" "$now_s" "job_started"
The ISO value helps humans during an incident. The epoch value is easy for machines to sort, compare, and filter.
Convert epoch milliseconds in shell
Most command-line timestamp converters read seconds. A 13-digit value is usually milliseconds, so passing it directly to date -d @... produces a far-future date.
# Wrong: 1700000000000 is treated as seconds
date -u -d @1700000000000
# Right: divide milliseconds by 1000 first
date -u -d @$(awk 'BEGIN { print 1700000000000 / 1000 }')
If the millisecond precision matters, split the value into seconds plus a fractional part:
ms=1700000000123
seconds=${ms%???}
millis=${ms#$seconds}
date -u -d "@$seconds.$millis" +'%Y-%m-%dT%H:%M:%S.%3NZ'
# 2023-11-14T22:13:20.123Z
For integer math where you only need whole seconds:
ms=1700000000000
seconds=$((ms / 1000))
date -u -d "@$seconds" +'%Y-%m-%dT%H:%M:%SZ'
Name variables with units: created_at_s, created_at_ms, expires_at_epoch_s, or client_sent_at_ms. That one habit catches more bugs than another clever parser.
macOS and BSD date equivalents
macOS does not use GNU date by default. These are the BSD equivalents for the same fixed timestamp:
| Task | macOS / BSD command |
|---|---|
| Current Unix seconds | date +%s |
| Epoch seconds to UTC date | date -u -r 1700000000 |
| Epoch seconds to ISO UTC | date -u -r 1700000000 +'%Y-%m-%dT%H:%M:%SZ' |
| UTC date to epoch seconds | date -j -u -f '%Y-%m-%d %H:%M:%S %Z' '2023-11-14 22:13:20 UTC' +%s |
| Current epoch milliseconds | python3 -c 'import time; print(int(time.time()*1000))' |
The -j flag matters on BSD date. It tells date to parse and print without trying to set the system clock.
If you want Linux-compatible commands on macOS, install GNU coreutils and use gdate:
gdate -u -d @1700000000 +'%Y-%m-%dT%H:%M:%SZ'
A quick sanity check:
date -u -r 1700000000 +'%Y-%m-%dT%H:%M:%SZ'
# 2023-11-14T22:13:20Z
Timezone rules that keep scripts sane
A Unix timestamp is timezone-neutral. The number 1700000000 is the same instant everywhere. Timezone changes only the displayed wall-clock text.
Use these rules in scripts:
- Store Unix seconds, Unix milliseconds, or ISO UTC.
- Use
-ufor repeatable conversion in logs, tests, and CI. - Use
TZ=Region/Cityonly when the output is meant for a specific audience. - Do not hard-code offsets like
-0500for dates that may cross daylight saving time. - Do not parse date strings without a timezone unless "local machine time" is truly what you mean.
Daylight saving time is the reason local date math gets weird. One local hour can repeat in autumn or disappear in spring. For storage and filtering, UTC keeps the boundary stable; local display can happen later.
Troubleshooting common date command bugs
| Symptom | Likely cause | Fix |
|---|---|---|
date: illegal option -- d |
macOS/BSD date | use date -u -r SECONDS, or install GNU gdate |
| Output year is 1970 | milliseconds were divided too early, or seconds were treated as milliseconds in app code | check the unit at the boundary |
| Output year is far in the future | 13-digit milliseconds passed as seconds | divide by 1000, or split seconds and milliseconds |
date +%s%3N ends with 3N |
BSD date does not support GNU %N |
use Python, Perl, Node.js, or gdate |
| CI script works on Ubuntu but fails on Alpine | BusyBox date has a smaller option set | test the exact container image or install coreutils |
| Timestamp differs between laptop and server | input date was parsed in local timezone | add -u, UTC, an offset, or TZ=Region/City |
| Parsed date changes around DST | local wall-clock time is repeated or missing | convert boundaries in UTC, then format local time for display |
| GNU and BSD commands disagree | different option meanings | branch by platform or standardize on GNU date |
For a quick platform branch:
if date --version >/dev/null 2>&1; then
date -u -d @1700000000 +'%Y-%m-%dT%H:%M:%SZ'
else
date -u -r 1700000000 +'%Y-%m-%dT%H:%M:%SZ'
fi
Script patterns worth keeping
Convert a timestamp from a log line
ts=1700000000
date -u -d @"$ts" +'%Y-%m-%dT%H:%M:%SZ'
Quoting the variable is not needed for this exact integer, but it keeps the habit consistent when scripts later handle optional values or user input.
Fail early on non-numeric input
ts=${1:-}
case "$ts" in
''|*[!0-9]*)
printf 'usage: %s UNIX_SECONDS\n' "$0" >&2
exit 2
;;
esac
date -u -d @"$ts" +'%Y-%m-%dT%H:%M:%SZ'
That deliberately accepts only positive integer seconds. If your script needs negative timestamps or millisecond values, make that a separate flag instead of silently guessing.
Use SOURCE_DATE_EPOCH for reproducible builds
Many build tools recognize SOURCE_DATE_EPOCH as epoch seconds. It is useful when generated files need stable timestamps.
export SOURCE_DATE_EPOCH=1700000000
date -u -d "@$SOURCE_DATE_EPOCH" +'%Y-%m-%dT%H:%M:%SZ'
Keep it in seconds unless the tool's documentation explicitly asks for another unit.
Official references
Check the manual for the implementation you are actually running:
- GNU Coreutils manual: date invocation
- Linux man-pages: date(1)
- Linux man-pages: time(2)
- FreeBSD date(1)
- The Open Group: seconds since the Epoch
Related timestamp tools
- Use the Epoch to Time Converter when you already have a Unix timestamp and want a readable date.
- Use the Date to Epoch Converter when you have a wall-clock date and need Unix seconds or milliseconds.
- Use the Unix Timestamp Reference for current day, week, month, and year boundary timestamps.
- Use the Unix Timestamp Formats guide when you need to identify seconds, milliseconds, microseconds, or nanoseconds.
- Use Unix Timestamp in Databases when the shell value is going into SQL.
FAQ
- How do I convert a Unix timestamp to a date in Linux?
- On GNU/Linux, use date -u -d @1700000000 +'%Y-%m-%dT%H:%M:%SZ'. The @ prefix tells GNU date to read the value as Unix seconds, and -u makes the output UTC.
- How do I get the current Unix timestamp in Linux?
- Run date +%s for Unix seconds. On GNU date, run date +%s%3N for current epoch milliseconds. On macOS BSD date, use python3 -c 'import time; print(int(time.time()*1000))' for milliseconds.
- How do I convert a date to a Unix timestamp in Linux?
- Use date -u -d '2023-11-14 22:13:20 UTC' +%s on GNU/Linux. Include UTC or an explicit timezone so the same command returns the same timestamp on every machine.
- Why does date -d fail on macOS?
- macOS ships BSD date, not GNU date. Use date -u -r 1700000000 for epoch seconds to date, or install GNU coreutils and run gdate -u -d @1700000000.
- Why does date +%s%3N print 3N on macOS?
- %N is a GNU date nanosecond format. BSD date on macOS does not support it the same way, so date +%s%3N can print a literal 3N suffix. Use Python, Perl, Node.js, or gdate for portable milliseconds.
- Should shell scripts store UTC or local time?
- Store Unix timestamps or ISO 8601 UTC strings. Render a local timezone only at the display edge with TZ=Region/City or a timezone-aware application layer.