Dates & Time cheat sheet

A scannable Dates & Time reference: 33 short snippets across 14 topics, each linking back to the lesson it came from.

At a glance

TopicWhat it covers
Unix timestamps & UTCA Unix timestamp is the number of seconds (or milliseconds) since the epoch: 1970-01-01 00:00:00 UTC. It islesson
Time zones & UTC offsetsAn offset like UTC+8 only says '8 hours ahead of UTC'. It does not tell you which region's rules apply — when daylightlesson
Calendars, leap years and leap secondsA year is a leap year if it is divisible by 4, unless it is divisible by 100, unless it is divisible by 400. Twolesson
ISO 8601 and RFC 3339 formatsThe T is required when a duration has a time part, because P1M is ambiguous otherwise: M before the T is months, afterlesson
Parsing and formatting dates safely03/04/2026 is 3 April in most of the world and 4 March in the United States. A parser that just works on this input islesson
Durations, periods and intervalsOne month after 31 January is not a well-defined instant. February has no 31st, so the result must be clamped to thelesson
Daylight saving and its edge casesWhen a zone enters daylight saving, the local clock jumps forward and a range of local times never happens. When itlesson
Monotonic clocks and elapsed timeThe system wall clock is continuously corrected by NTP, by virtualisation hosts and by manual changes. It can jumplesson
Date libraries comparedLibraries split into two camps: mutable date objects with implicit conversion, and separate types per concept. Thelesson
Storing dates in databasesThe name TIMESTAMP WITH TIME ZONE is misleading: PostgreSQL does not store a zone. It stores a UTC instant and convertslesson
Scheduling, cron and time-based triggersAn in-process scheduler is not durable. If the process is down at the scheduled moment, the run never happens andlesson
The 2038 problem and 32-bit timeA signed 32-bit integer counting seconds from 1970 reaches its maximum on 2038-01-19T03:14:07Z. One second later itlesson
Testing time-dependent codeCode that calls datetime.now() directly cannot be tested without patching a global. Accepting a clock as a parameter orlesson
Internationalisation: locales, calendars and relative timeDo not hand-write month names or date order. Every platform ships locale data — the CLDR database — behind an APIlesson

Quick snippets

Unix timestamps & UTC

What a Unix timestamp is

2026-09-17 12:00:00 UTC  →  1787112000
2026-09-17 08:00:00 EDT    →  1787112000   (same instant!)
2026-09-17 20:00:00 +08:00  →  1787112000

Store UTC, display local

const now = Math.floor(Date.now() / 1000); // seconds since epoch
const d = new Date();
console.log(d.toISOString()); // always UTC: 2026-09-17T12:00:00.000Z

Store UTC, display local

from datetime import datetime, timezone
now = datetime.now(timezone.utc)
print(int(now.timestamp()))      # epoch seconds
print(now.isoformat())           # 2026-09-17T12:00:00+00:00

Full lesson: Unix timestamps & UTC →

Time zones & UTC offsets

Use ISO 8601 with the zone

2026-09-17T12:00:00Z          (Z = UTC)
2026-09-17T20:00:00+08:00     (with explicit offset)
2026-09-17T08:00:00-04:00     (same instant, different zone)

Full lesson: Time zones & UTC offsets →

Calendars, leap years and leap seconds

The leap year rule, exactly

def is_leap(year):
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)

# 2024 leap, 2023 not, 1900 not (divisible by 100), 2000 leap (divisible by 400)
assert [y for y in (2023, 2024, 1900, 2000) if is_leap(y)] == [2024, 2000]

Week numbers and the year boundary

from datetime import date

d = date(2025, 12, 29)
iso_year, iso_week, iso_day = d.isocalendar()
print(iso_year, iso_week, iso_day)     # 2026 1 1 — belongs to ISO year 2026

# the trap: strftime year is NOT the ISO year
print(d.strftime("%G-W%V-%u"))         # 2026-W01-1  (ISO year, ISO week)
print(d.strftime("%Y-W%W"))            # 2025-W52     (calendar year, week 0-based)

Full lesson: Calendars, leap years and leap seconds →

ISO 8601 and RFC 3339 formats

The pieces of a timestamp

2026-09-18                          date only
10:30:00                            time, no zone
2026-09-18T10:30:00Z                UTC (Z means the same as +00:00)
2026-09-18T10:30:00+02:00           offset from UTC
2026-09-18T10:30:00.123Z            fractional seconds
2026-09-18T10:30:00.123456789Z      nanoseconds
2026-09-18T10:30:00+02:00[Europe/Paris]   offset plus IANA zone (RFC 9557)
2026-W38-5                          ISO week date
2026-261                            ordinal date: day 261 of 2026

Durations, intervals and recurrence

// JavaScript has no duration parser; Temporal does (shipping in modern runtimes)
const d = Temporal.Duration.from("P1Y2M3DT4H5M6S");
const later = Temporal.PlainDate.from("2026-01-31").add({ months: 1 });
later.toString();  // "2026-02-28" — clamped, not overflowing into March

Why this and not something else

# RFC 3339 requires an uppercase T and Z, and a fixed-width offset
date -u +%Y-%m-%dT%H:%M:%SZ
# 2026-09-18T10:30:00Z

# GNU date can round-trip an offset into a zone
TZ=Europe/Paris date -d '2026-09-18T10:30:00+02:00' +%Y-%m-%dT%H:%M:%S%:z

Full lesson: ISO 8601 and RFC 3339 formats →

Parsing and formatting dates safely

Ambiguous input and lenient parsers

new Date("03/04/2026")       // parsed as March 4 (US, month first)
new Date("2026-03-04")       // parsed as March 4 (ISO, but see the trap below)
new Date("2026-03-04T00:00") // interpreted as LOCAL time, not UTC
new Date("2026-03-04")       // interpreted as UTC midnight

// two forms that look alike produce instants a day apart

Parse strictly, format explicitly

from datetime import datetime, timezone

# explicit format, no guessing
dt = datetime.strptime("2026-09-18 10:30", "%Y-%m-%d %H:%M")
try:
    datetime.strptime("18/09/2026", "%Y-%m-%d")
except ValueError as e:
    print("rejected:", e)      # time data '18/09/2026' does not match format

# machine output: always ISO 8601 with an offset
print(dt.isoformat())                      # 2026-09-18T10:30:00
print(dt.replace(tzinfo=timezone.utc).isoformat())  # ...+00:00

Parse strictly, format explicitly

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

# a naive datetime is a bug waiting to happen: no zone, no instant
naive = datetime(2026, 9, 18, 10, 30)

# attach a zone explicitly before any conversion
aware = naive.replace(tzinfo=ZoneInfo("Europe/Paris"))
utc = aware.astimezone(timezone.utc)

# subtract aware datetimes; subtracting naive ones across zones is meaningless
delta = datetime.now(timezone.utc) - utc

Full lesson: Parsing and formatting dates safely →

Durations, periods and intervals

Three different concepts that share a name

from datetime import datetime, timedelta, timezone
from dateutil.relativedelta import relativedelta

start = datetime(2026, 1, 31, tzinfo=timezone.utc)

# duration: exact, unit-independent
print(start + timedelta(days=30))          # 2026-03-02T00:00:00+00:00

# period: calendar-aware, clamped
print(start + relativedelta(months=1))     # 2026-02-28T00:00:00+00:00

Addition is not associative

// the order of operations can change the answer
const d = new Date("2026-01-31T00:00:00Z");
const a = new Date(d); a.setUTCMonth(a.getUTCMonth() + 1); a.setUTCDate(a.getUTCDate() + 1);
const b = new Date(d); b.setUTCDate(b.getUTCDate() + 1); b.setUTCMonth(b.getUTCMonth() + 1);
a.toISOString();
b.toISOString();
// both land on 2026-03-01 here, but not for every date — pick and document an order

Business days and rounding

import numpy as np
from datetime import date

start = np.datetime64("2026-09-18")           # a Friday
print(np.busday_offset(start, 5, roll="forward"))
# 2026-09-25 — five business days later, skipping the weekend

# holidays must be supplied; there is no universal list
holidays = ["2026-12-25", "2026-12-28"]
print(np.busday_offset("2026-12-24", 1, roll="forward",
                       holidays=holidays))      # 2026-12-29

Full lesson: Durations, periods and intervals →

Daylight saving and its edge cases

Two clocks move in opposite directions

Europe/Paris, 2026

spring forward, 29 March 02:00 -> 03:00
  01:58  01:59  03:00  03:01    (02:30 does not exist)

fall back, 25 October 03:00 -> 02:00
  02:58  02:59  02:00  02:01    (02:30 happens twice)

Scheduling rules that survive a transition

# cron in local time is zone-dependent
30 2 * * *     # in Europe/Paris this is skipped on 29 March

# cron in UTC has no gaps
30 1 * * *     # 01:30 UTC is a real instant on every day

Full lesson: Daylight saving and its edge cases →

Monotonic clocks and elapsed time

The wall clock can move backwards

const start = Date.now();
await work();
const elapsed = Date.now() - start;   // can be negative after an NTP step

// the correct tool
const t0 = performance.now();
await work();
const ms = performance.now() - t0;    // monotonic, sub-millisecond

The API in each language

start := time.Now()
doWork()
// Sub uses the monotonic reading when both values have one
fmt.Println(time.Since(start))

// mixing a wall-clock-only time into the subtraction loses the monotonic part
wall := time.Unix(0, start.UnixNano())
fmt.Println(start.Sub(wall))   // wall-clock semantics, not monotonic

Measuring things correctly

# a timeout that cannot be fooled by a clock step
def run_with_timeout(call, timeout_s):
    deadline = time.monotonic() + timeout_s
    while True:
        remaining = deadline - time.monotonic()
        if remaining <= 0:
            raise TimeoutError
        if call(min(remaining, 0.25)):    # never block longer than the deadline
            return

Full lesson: Monotonic clocks and elapsed time →

Date libraries compared

The same task in each

now := time.Now()                            // has a monotonic reading
loc, _ := time.LoadLocation("Europe/Paris")
local := now.In(loc)

t, err := time.Parse(time.RFC3339, "2026-09-18T10:30:00Z")   // strict
if err != nil { log.Fatal(err) }
_ = t.AddDate(0, 1, 0)          // calendar arithmetic (year, month, day)
_ = t.Add(30 * 24 * time.Hour)  // exact duration

Choosing and migrating

# pytz (legacy) vs zoneinfo (modern)
import pytz
from zoneinfo import ZoneInfo

dt = datetime(2026, 9, 18, 10, 30)

legacy = pytz.timezone("Europe/Paris").localize(dt)     # explicit localize step
modern = dt.replace(tzinfo=ZoneInfo("Europe/Paris"))    # plain constructor

# both are aware, but only zoneinfo behaves like the standard library expects

Full lesson: Date libraries compared →

Storing dates in databases

Why naive timestamps cause tickets

-- DANGER: a naive column silently accepts any local time
CREATE TABLE shipments (shipped_at TIMESTAMP);        -- no zone
INSERT INTO shipments VALUES ('2026-09-18 10:30:00'); -- whose 10:30?

-- an application in Tokyo and one in Berlin now write different instants
-- into the same column, and nothing detects the difference.

Ranges, indexes and per-dialect traps

-- half-open range: includes the whole day, excludes the next day's midnight
SELECT count(*) FROM events
WHERE occurred_at >= '2026-09-18T00:00:00Z'
  AND occurred_at <  '2026-09-19T00:00:00Z';

-- a function on the column defeats the index
WHERE date(occurred_at) = DATE '2026-09-18'    -- no index use

-- time-bucket aggregation
SELECT date_trunc('hour', occurred_at AT TIME ZONE 'UTC') AS bucket, count(*)
FROM events GROUP BY 1 ORDER BY 1;

Full lesson: Storing dates in databases →

Scheduling, cron and time-based triggers

Misfires, catch-up and clusters

# systemd timer: OnCalendar with an explicit zone, Persistent handles downtime
# /etc/systemd/system/report.timer
# [Timer]
# OnCalendar=Mon..Fri 04:00
# Persistent=true
# RandomizedDelaySec=300

systemctl list-timers --all
systemctl status report.timer

Delivery semantics and durable delays

# idempotent daily job keyed on the intended date, not on when it ran
def run_daily(run_date: date):
    with db.transaction():
        if already_done(run_date):        # a unique index on (job, run_date)
            return
        process(run_date)
        mark_done(run_date)

Full lesson: Scheduling, cron and time-based triggers →

The 2038 problem and 32-bit time

What actually overflows

import datetime

max32 = 2**31 - 1                      # 2147483647

print(datetime.datetime.fromtimestamp(max32, datetime.timezone.utc))
# 2038-01-19 03:14:07+00:00

# what the wrap produces on a system that stores a signed 32-bit value
print(datetime.datetime.fromtimestamp(-2**31, datetime.timezone.utc))
# 1901-12-13 20:45:52+00:00

Where it hides

#include <stdio.h>
#include <time.h>

int main(void) {
    printf("sizeof(time_t) = %zu bytes\n", sizeof(time_t));
    // 4 bytes on a 32-bit build: affected
    // 8 bytes on a 64-bit build: safe
    return 0;
}

Planning the migration

# check a running system for 32-bit time
getconf LONG_BIT
date -d @2147483647 -u            # the last representable second
# in a container, run with an offset clock to simulate:
# faketime '2038-01-19 03:14:08' ./your-program

Full lesson: The 2038 problem and 32-bit time →

Testing time-dependent code

Freezing and travelling

# freezegun patches the clock; useful for code you cannot refactor
from freezegun import freeze_time

@freeze_time("2026-09-18T10:30:00Z")
def test_daily_report_uses_today():
    assert report_date() == date(2026, 9, 18)

# travel forward to test a scheduled transition
with freeze_time("2026-09-18T10:30:00Z") as frozen:
    frozen.tick(delta=timedelta(hours=2))   # explicit advance, not real waiting

Testing across zones and boundaries

# run the whole suite under several zones in CI
for tz in UTC Europe/Paris America/New_York Australia/Lord_Howe Asia/Kathmandu; do
  TZ=$tz pytest -q || exit 1
done

Full lesson: Testing time-dependent code →

Internationalisation: locales, calendars and relative time

Non-Gregorian calendars

const dt = new Date("2026-09-18T10:30:00Z");

new Intl.DateTimeFormat("th-TH-u-ca-buddhist", { dateStyle: "long" }).format(dt);
// Buddhist era year 2569 (Gregorian year plus 543), rendered in Thai script

new Intl.DateTimeFormat("ar-SA-u-ca-islamic-umalqura", { dateStyle: "long" }).format(dt);
// the corresponding Hijri date, rendered in Arabic script

new Intl.DateTimeFormat("en-US-u-ca-hebrew", { dateStyle: "long" }).format(dt);
// the corresponding Hebrew calendar date, in English

The size and correctness cost

// Node: full ICU is the default in modern releases
process.config.variables.icu_small;   // false means full data

// Browsers ship the data; in Node you may need to build with full-icu
// or rely on the small-icu default, which supports only English formats

Full lesson: Internationalisation: locales, calendars and relative time →

FAQ

Is this Dates & Time cheat sheet free to use?
Yes. No sign-up and no tracking: the page is static, every example is on the page itself, and you can print it or save it as a one-page reference.
Where do the examples come from?
Every snippet is taken from the 14 lessons of the Dates & Time course on this site, and each section links back to the lesson it was pulled from.
How do I go deeper than a cheat sheet?
Open the full Dates & Time course — it carries the worked explanations, the edge cases and the exercises behind every line here.

Algorithms Data Structures Computer Networks Operating Systems Character Encodings Hashing & Checksums

Last refreshed 2026-09-27.