Calendars, leap years and leap seconds
The Gregorian leap rule including the 100 and 400 exceptions, why month lengths vary, how ISO week numbering works, and what leap seconds do to a clock that claims to be exact.
The leap year rule, exactly
A year is a leap year if it is divisible by 4, unless it is divisible by 100, unless it is divisible by 400. Two exceptions, and code that implements only the first one is subtly wrong for centuries.
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]| Month | Days | Month | Days |
|---|---|---|---|
| January | 31 | July | 31 |
| February | 28 or 29 | August | 31 |
| March | 31 | September | 30 |
| April | 30 | October | 31 |
| May | 31 | November | 30 |
| June | 30 | December | 31 |
Never compute a month length by hand, and never assume February has 28 days. Use the library helper — calendar.monthrange, daysInMonth, time.DaysIn — because the correct answer depends on the year.
Week numbers and the year boundary
ISO 8601 defines week 1 as the week containing the first Thursday of the year, so weeks start on Monday and a year has 52 or 53 weeks. This means 30 December can belong to the next ISO year.
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)%G,%Vand%uare the ISO year, week and weekday.%Yand%Ware the calendar year and a week number that has an incomplete first week — mixing them produces off-by-one errors at year boundaries.- The US convention differs: weeks start on Sunday and week 1 contains 1 January, so there is no single correct week number without naming the system.
Leap seconds break the assumption of 86400
Leap seconds are inserted by the IERS to keep civil time within 0.9 seconds of astronomical time. There have been 27 of them since 1972, which means UTC minutes can have 61 seconds and a UTC day can have 86401.
- Unix time ignores leap seconds: it counts 86400 seconds per day, so it drifts about one second from UTC over decades.
- POSIX mandates that a leap second be handled by repeating or smearing the last second, and Linux spreads the correction over milliseconds (leap smearing).
- Google and other operators smear 24 hours around the event so no system ever sees a 23:59:60 timestamp.
- A monotonic clock never jumps, which is why elapsed-time measurement must not use wall clock.
FAQ
Should I worry about a 53-week year?
Is 1900 or 2000 a leap year?
Related
ISO 8601 and RFC 3339 formats The 2038 problem and 32-bit time
Last refreshed 2026-09-18.