Introduction
Leap seconds are occasional one-second adjustments to Coordinated Universal Time (UTC) to keep it synchronized with Earth's rotation. They represent one of the most complex aspects of time handling in software development.
Quick Summary: UTC adds leap seconds to stay within 0.9 seconds of UT1 (solar time). As of January 2026, 37 leap seconds have been added since 1972, making UTC 37 seconds behind International Atomic Time (TAI).
What Are Leap Seconds?
Definition
A leap second is a one-second adjustment applied to UTC to account for:
- Earth's decelerating rotation: Earth's rotation is gradually slowing down
- Irregular rotation rate: Earth's rotation speed varies unpredictably
- UT1-UTC divergence: Keeping UTC within ±0.9 seconds of solar time (UT1)
TEXT1Leap Second Formula: 2 If UT1 - UTC > 0.9 seconds → Add positive leap second 3 If UT1 - UTC < -0.9 seconds → Add negative leap second 4 5Result: UTC stays synchronized with Earth's rotation
How Leap Seconds Work
When a leap second is added, the last minute of a UTC day has 61 seconds instead of 60:
TEXT1Normal Day (no leap second): 2 23:59:58 UTC 3 23:59:59 UTC 4 00:00:00 UTC (next day) 5 6Leap Second Day: 7 23:59:58 UTC 8 23:59:59 UTC 9 23:59:60 UTC ← Leap second! 10 00:00:00 UTC (next day)
Note: Negative leap seconds have never occurred in practice, though they're theoretically possible if Earth's rotation suddenly accelerated.
History of Leap Seconds
Timeline
| Year | Event | UTC-TAI Offset |
|---|---|---|
| 1972 | First leap second added | +10 seconds |
| 1972-1984 | 12 leap seconds added | +22 seconds |
| 1985-1995 | 8 leap seconds added | +29 seconds |
| 1996-2005 | 3 leap seconds added | +32 seconds |
| 2008-2016 | 3 leap seconds added | +35 seconds |
| 2017 | Last leap second | +36 seconds |
| 2025 | Future leap second | +37 seconds |
Recent Leap Seconds
TEXT1All Leap Seconds (1972 - 2025): 2 - 1972-06-30: +1 second (UTC-TAI = +11s) 3 - 1972-12-31: +1 second (UTC-TAI = +12s) 4 - 1973-12-31: +1 second (UTC-TAI = +13s) 5 - 1974-12-31: +1 second (UTC-TAI = +14s) 6 - 1975-12-31: +1 second (UTC-TAI = +15s) 7 - 1976-12-31: +1 second (UTC-TAI = +16s) 8 - 1977-12-31: +1 second (UTC-TAI = +17s) 9 - 1978-12-31: +1 second (UTC-TAI = +18s) 10 - 1979-12-31: +1 second (UTC-TAI = +19s) 11 - 1981-06-30: +1 second (UTC-TAI = +20s) 12 - 1982-06-30: +1 second (UTC-TAI = +21s) 13 - 1983-06-30: +1 second (UTC-TAI = +22s) 14 - 1985-06-30: +1 second (UTC-TAI = +23s) 15 - 1987-12-31: +1 second (UTC-TAI = +24s) 16 - 1988-12-31: +1 second (UTC-TAI = +25s) 17 - 1989-12-31: +1 second (UTC-TAI = +26s) 18 - 1990-12-31: +1 second (UTC-TAI = +27s) 19 - 1992-06-30: +1 second (UTC-TAI = +28s) 20 - 1993-06-30: +1 second (UTC-TAI = +29s) 21 - 1994-06-30: +1 second (UTC-TAI = +30s) 22 - 1995-12-31 +1 second (UTC-TAI = +31s) 23 - 1997-12-31 +1 second (UTC-TAI = +32s) 24 - 1998-12-31 +1 second (UTC-TAI = +33s) 25 - 1999-12-31 +1 second (UTC-TAI = +34s) 26 - 2000-12-31 +1 second (UTC-TAI = +35s) 27 - 2005-12-31 +1 second (UTC-TAI = +36s) 28 - 2008-12-31: +1 second (UTC-TAI = +37s)
Future of Leap Seconds
The International Telecommunication Union (ITU) is considering abolishing leap seconds by 2035, which would simplify time handling worldwide.
Important: If leap seconds are abolished, UTC would gradually diverge from solar time. This is a controversial topic among astronomers, software developers, and timekeeping organizations.
TAI vs UTC
International Atomic Time (TAI)
TAI is a time scale based on the weighted average of atomic clocks worldwide. It never includes leap seconds, making it a perfectly uniform time scale.
TEXT1TAI Characteristics: 2 - Based on: 400+ atomic clocks worldwide 3 - Precision: ±0.000000001 seconds (1 nanosecond) 4 - Leap Seconds: Never 5 - Usage: Scientific research, precise synchronization 6 7Current TAI-UTC Offset: +37 seconds (as of January 2026)
Conversion Between TAI and UTC
JAVASCRIPT1// Convert TAI timestamp to UTC timestamp 2const TAI_OFFSET_SECONDS = 37; // As of 2026 3 4function taiToUtc(taiTimestamp) { 5 return taiTimestamp - TAI_OFFSET_SECONDS; 6} 7 8function utcToTai(utcTimestamp) { 9 return utcTimestamp + TAI_OFFSET_SECONDS; 10} 11 12// Example 13const taiTs = 1735689637; 14const utcTs = taiToUtc(taiTs); // 1735689600 15 16console.log('TAI Timestamp:', taiTs); 17console.log('UTC Timestamp:', utcTs);
PYTHON1from datetime import datetime, timezone, timedelta 2 3TAI_OFFSET_SECONDS = 37 # As of 2026 4 5def tai_to_utc(tai_timestamp): 6 return tai_timestamp - TAI_OFFSET_SECONDS 7 8def utc_to_tai(utc_timestamp): 9 return utc_timestamp + TAI_OFFSET_SECONDS 10 11# Example 12tai_ts = 1735689637 13utc_ts = tai_to_utc(tai_ts) # 1735689600 14 15print(f'TAI Timestamp: {tai_ts}') 16print(f'UTC Timestamp: {utc_ts}')
Handling Leap Seconds in Programming
JavaScript
JavaScript's Date object does not support leap seconds directly. It repeats the 23:59:60 timestamp as 23:59:59.
JAVASCRIPT1// Leap second handling in JavaScript 2const leapSecondDate = new Date('2016-12-31T23:59:60Z'); 3 4// JavaScript treats this as 23:59:59Z 5console.log(leapSecondDate.toISOString()); // "2016-12-31T23:59:59.000Z" 6 7// Workaround: Use a library that supports leap seconds 8import { unix } from 'dayjs'; 9import utc from 'dayjs/plugin/utc'; 10import customParseFormat from 'dayjs/plugin/customParseFormat'; 11 12// Note: Day.js also doesn't support leap seconds natively 13// Consider using specialized time libraries for leap second support
Python
Python's datetime module has limited support for leap seconds. The standard library doesn't represent 23:59:60.
PYTHON1# Leap second handling in Python 2from datetime import datetime, timezone, timedelta 3 4# Standard datetime doesn't support leap seconds 5try: 6 leap_second = datetime(2016, 12, 31, 23, 59, 60, tzinfo=timezone.utc) 7except ValueError as e: 8 print(f'Error: {e}') # ValueError: second must be in 0..59 9 10# Workaround: Use specialized libraries 11# For true leap second support, consider: 12# - astropy.time for scientific applications 13# - specialized time handling libraries
Java
Java 8+ java.time package supports leap seconds in Instant class.
JAVA1import java.time.Instant; 2import java.time.temporal.ChronoUnit; 3 4// Leap second handling in Java 5Instant leapSecondInstant = Instant.parse("2016-12-31T23:59:60Z"); 6 7// Java correctly handles leap second in Instant 8System.out.println("Leap Second: " + leapSecondInstant); 9 10// Check if a timestamp contains a leap second 11Instant timestamp = Instant.parse("2016-12-31T23:59:60Z"); 12boolean isLeapSecond = timestamp.getNano() == 0 && 13 timestamp.getEpochSecond() % 60 == 59; 14 15System.out.println("Is Leap Second: " + isLeapSecond);
Go
Go's time package does not have native leap second support.
GO1package main 2 3import ( 4 "fmt" 5 "time" 6) 7 8func main() { 9 // Go doesn't support leap seconds natively 10 leapSecondStr := "2016-12-31T23:59:60Z" 11 _, err := time.Parse(time.RFC3339, leapSecondStr) 12 13 if err != nil { 14 fmt.Println("Error:", err) 15 // Go will reject leap second timestamps 16 } 17}
Time Smearing
What is Time Smearing?
Time smearing is a technique to gradually distribute leap second adjustments over a period (usually 12-24 hours) instead of applying them instantaneously.
TEXT1Traditional Leap Second: 2 23:59:58 UTC 3 23:59:59 UTC 4 23:59:60 UTC ← Instant jump 5 00:00:00 UTC (next day) 6 7Smeared Leap Second (24-hour smear): 8 Each second is ~1.16ms longer for 24 hours 9 No instant jump, smooth transition
Smearing Implementations
| System | Smearing Method | Duration |
|---|---|---|
| Google TrueTime | Linear smear | 24 hours |
| Amazon Time Sync Service | Linear smear | 24 hours |
| NTP pools | Optional smear | 1-24 hours |
| Linux | Kernel step (no smear) | Instant |
Note: Time smearing is used by large distributed systems to avoid synchronization issues. However, it creates its own problems: smeared time is not standard UTC and can't be reliably converted to other time systems.
IETF RFC 8536 Recommendations
RFC 8536 provides guidelines for leap second handling in software systems:
Key Recommendations
- Use TAI for Internal Time: Store TAI timestamps internally for precision
- Convert to UTC Only for Display: Apply leap second offset only when displaying to users
- Use NTP for Synchronization: Get accurate time from NTP servers
- Document Leap Second Handling: Clearly document how your system handles leap seconds
- Test Leap Second Events: Simulate leap second transitions in your tests
Best Practices
TEXT1For Most Applications: 2 ✓ Use UTC timestamps (ignore leap seconds in storage) 3 ✓ Apply leap second offset only when needed (rare cases) 4 ✓ Test with historical leap second dates 5 ✓ Document your leap second policy 6 7For High-Precision Applications: 8 ✓ Store TAI timestamps 9 ✓ Maintain leap second table 10 ✓ Convert to UTC for display 11 ✓ Use NTP for synchronization
Common Issues and Solutions
Issue 1: Time Jumps During Leap Second
Problem: Systems experience a 1-second jump during leap second transition.
Solution: Use time smearing or implement leap second awareness.
JAVASCRIPT1// Time smearing example (simplified) 2function smearedTime(timestamp, leapSecondDate) { 3 const diffHours = (timestamp - leapSecondDate) / (1000 * 60 * 60); 4 const smearDuration = 24; // 24 hours 5 const smearFactor = Math.min(Math.max(diffHours / smearDuration, 0), 1); 6 7 return timestamp + smearFactor * 1000; // Add up to 1 second over 24 hours 8}
Issue 2: Database Query Failures
Problem: Queries fail during leap second because timestamps like 23:59:60 are invalid in most databases.
Solution: Store timestamps without leap seconds, document leap second behavior.
SQL1-- Store standard UTC timestamps (without leap second) 2CREATE TABLE events ( 3 id INT PRIMARY KEY, 4 event_timestamp TIMESTAMP WITHOUT TIME ZONE, -- Standard UTC 5 description TEXT 6); 7 8-- Handle leap second by using a range 9SELECT * FROM events 10WHERE event_timestamp BETWEEN '2016-12-31T23:59:59Z' AND '2017-01-01T00:00:01Z';
Issue 3: Logging Errors During Leap Second
Problem: Log files show duplicate or out-of-order timestamps during leap second.
Solution: Use high-resolution timestamps and unique sequence identifiers.
PYTHON1# Logging with leap second awareness 2import time 3from datetime import datetime 4 5def log_event(message): 6 # Use millisecond precision to handle leap seconds 7 timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3] 8 sequence_id = time.time_ns() # Nanosecond precision 9 10 print(f'[{timestamp}] [{sequence_id}] {message}')
Code Examples by Scenario
Scenario 1: Converting Timestamps with Leap Second Offset
JAVASCRIPT1const LEAP_SECONDS = 37; // As of 2026 2 3// Convert TAI timestamp to human-readable UTC 4function taiToUtcString(taiTimestamp) { 5 const utcTimestamp = taiTimestamp - LEAP_SECONDS; 6 const date = new Date(utcTimestamp * 1000); 7 return date.toISOString(); 8} 9 10// Example 11const taiTs = 1735689637; 12console.log(taiToUtcString(taiTs)); // "2026-01-01T00:00:00.000Z"
PYTHON1from datetime import datetime, timezone, timedelta 2 3LEAP_SECONDS = 37 # As of 2026 4 5def tai_to_utc_string(tai_timestamp): 6 utc_timestamp = tai_timestamp - LEAP_SECONDS 7 utc_time = datetime.fromtimestamp(utc_timestamp, timezone.utc) 8 return utc_time.isoformat() 9 10# Example 11tai_ts = 1735689637 12print(tai_to_utc_string(tai_ts)) # "2026-01-01T00:00:00Z"
Scenario 2: Checking if a Date is a Leap Second
JAVASCRIPT1const LEAP_SECOND_DATES = [ 2 '1972-06-30', '1972-12-31', '1973-12-31', '1974-12-31', 3 '1975-12-31', '1976-12-31', '1977-12-31', '1978-12-31', 4 '1979-12-31', '1981-06-30', '1982-06-30', '1983-06-30', 5 '1985-06-30', '1987-12-31', '1989-12-31', '1990-12-31', 6 '1992-06-30', '1993-06-30', '1994-06-30', '1995-12-31', '1997-06-30', 7 '1998-12-31', '1999-12-31', '2000-12-31', '2005-12-31', 8 '2008-12-31', '2012-06-30', '2015-06-30', '2025-12-31', 9 '2017-12-31', '2018-06-30', '2019-12-31', '2025-12-31' 10]; 11 12function isLeapSecondDate(date) { 13 const dateStr = date.toISOString().split('T')[0]; 14 return LEAP_SECOND_DATES.includes(dateStr); 15} 16 17// Example 18const date = new Date('2016-12-31T23:59:59Z'); 19console.log(isLeapSecondDate(date)); // true
PYTHON1from datetime import datetime 2 3LEAP_SECOND_DATES = [ 4 datetime(1972, 6, 30), datetime(1972, 12, 31), 5 datetime(1973, 12, 31), datetime(1974, 12, 31), 6 datetime(1975, 12, 31), datetime(1976, 12, 31), 7 datetime(1977, 12, 31), datetime(1978, 12, 31), 8 datetime(1979, 12, 31), datetime(1981, 6, 30), 9 datetime(1982, 6, 30), datetime(1983, 6, 30), 10 datetime(1985, 6, 30), datetime(1987, 12, 31), 11 datetime(1989, 12, 31), datetime(1990, 12, 31), 12 datetime(1992, 6, 30), datetime(1993, 6, 30), 13 datetime(1994, 6, 30), datetime(1995, 12, 31), 14 datetime(1997, 12, 31), datetime(1998, 12, 31), 15 datetime(1999, 12, 31), datetime(2000, 12, 31), 16 datetime(2001, 6, 30), datetime(2002, 6, 30), 17 datetime(2003, 6, 30), datetime(2004, 6, 30), 18 datetime(2005, 12, 31), datetime(2008, 12, 31) 19] 20 21def is_leap_second_date(date): 22 return any( 23 date.year == leap_date.year and 24 date.month == leap_date.month and 25 date.day == leap_date.day 26 for leap_date in LEAP_SECOND_DATES 27 ) 28 29# Example 30date = datetime(2016, 12, 31, 23, 59, 59) 31print(is_leap_second_date(date)) # True
Testing Leap Second Handling
Test Cases
TEXT1Test 1: Verify leap second offset 2 Input: TAI = 1735689637 3 Expected: UTC = 1735689600 (difference = 37 seconds) 4 Status: PASS if difference equals current UTC-TAI offset 5 6Test 2: Handle leap second timestamp 7 Input: "2016-12-31T23:59:60Z" 8 Expected: System handles gracefully (no crash, no data corruption) 9 Status: PASS if no errors 10 11Test 3: Convert leap second date range 12 Input: Range [2016-12-31T23:59:59Z, 2017-01-01T00:00:01Z] 13 Expected: All events in range, including leap second events 14 Status: PASS if all events returned 15 16Test 4: Verify time smearing 17 Input: Timestamp near leap second 18 Expected: Smooth transition, no instant jump 19 Status: PASS if transition is smooth
Best Practices Summary
For Most Applications
- Ignore leap seconds in storage (use standard UTC timestamps)
- Document your leap second handling policy
- Test with historical leap second dates
- Use UTC as your primary time standard
For High-Precision Applications
- Store TAI timestamps for internal calculations
- Maintain leap second table for conversions
- Use NTP for synchronization
- Implement leap second awareness in critical code paths
Related Tools
- TAI Time Converter - Convert between TAI, UTC, and GPS time
- Current Timestamp - Get current UTC time with precision
- Unix Timestamp Converter - Handle various timestamp precisions
- GPS Time Converter - GPS time with leap second handling
FAQ
Q: How often do leap seconds occur?
A: Leap seconds have occurred 27 times since 1972 (about once every 1-2 years), but the frequency has decreased recently due to Earth's slowing rotation.
Q: Will leap seconds continue forever?
A: The ITU is discussing abolishing leap seconds by 2035, which would stop their addition but cause UTC to gradually diverge from solar time.
Q: Do I need to handle leap seconds in my application?
A: For most applications, no—use standard UTC timestamps. Only handle leap seconds if you're building time-critical systems, scientific applications, or distributed databases.
Q: What happens during a leap second?
A: UTC adds an extra second (23:59:60) to keep synchronized with Earth's rotation. Most systems repeat 23:59:59 or use time smearing to avoid jumps.
Q: How do I test leap second handling?
A: Test with historical leap second dates like 2016-12-31T23:59:60Z and verify your application doesn't crash or produce incorrect results.