Skip to main content

4. Local Time

4.1. Coordinated Universal Time (UTC)

Because the daylight saving rules for local time zones are so convoluted and can change based on local law at unpredictable times, true interoperability is best achieved by using Coordinated Universal Time (UTC). This specification does not cater to local time zone rules.

Why use UTC?

  • ✅ Globally unified time standard
  • ✅ Not affected by daylight saving time
  • ✅ Not affected by political decisions
  • ✅ Ensures interoperability

4.2. Local Offsets

The offset between local time and UTC is often useful information. For example, in electronic mail (RFC2822, [IMAIL-UPDATE]) the local offset provides a useful heuristic to determine the probability of a prompt response. Attempts to label local offsets with alphabetic strings have resulted in poor interoperability in the past [IMAIL], [HOST-REQ]. As a result, RFC2822 [IMAIL-UPDATE] has made numeric offsets mandatory.

Numeric Offset Calculation

Numeric offsets are calculated as "local time minus UTC". So the equivalent time in UTC can be determined by subtracting the offset from the local time.

Example 1:

Local time: 18:50:00-04:00
UTC time: 18:50:00 - (-04:00) = 18:50:00 + 04:00 = 22:50:00Z

Verification: Eastern Daylight Time (EDT) is UTC-4

Example 2:

Local time: 15:30:00+08:00
UTC time: 15:30:00 - (+08:00) = 15:30:00 - 08:00 = 07:30:00Z

Verification: China Standard Time (CST) is UTC+8

Important Notes

Note: Following ISO 8601, numeric offsets represent only time zones that differ from UTC by an integral number of minutes. However, many historical time zones differ from UTC by a non-integral number of minutes. To represent such historical timestamps exactly, applications MUST convert them to a representable time zone.

Historical Time Zone Examples:

Some local times in the late 19th century:
- Amsterdam: UTC+00:19:32
- Paris: UTC+00:09:21

These cannot be represented precisely in RFC 3339 and must be rounded to the nearest minute

4.3. Unknown Local Offset Convention

If the time in UTC is known, but the offset to local time is unknown, this can be represented with an offset of "-00:00". This differs semantically from an offset of "Z" or "+00:00", which implies that UTC is the preferred reference point for the specified time. RFC2822 [IMAIL-UPDATE] describes a similar convention for email.

Distinguishing Three Representations

Z or +00:00:

2002-07-15T10:30:00Z
2002-07-15T10:30:00+00:00

Meaning: This time IS UTC time, UTC is the preferred reference point

-00:00:

2002-07-15T10:30:00-00:00

Meaning: The UTC time is 10:30:00, but the local time zone offset is unknown
(possibly from a system that doesn't know its time zone setting)

Usage Scenarios:

Scenario 1: Server logs, known to be UTC → use Z
Scenario 2: Device-generated timestamp, device is in UTC but doesn't know local zone → use -00:00
Scenario 3: Explicitly in London (GMT) → use +00:00

4.4. Unqualified Local Time

A number of devices currently connected to the Internet run their internal clocks in local time and are unaware of UTC. While the Internet does have a tradition of accepting reality in designing specifications, this should not be done at the expense of interoperability. Since interpretation of an unqualified local time zone will fail in approximately 23/24 of the globe,

Mandatory Requirements

Internet protocols MUST generate fully qualified timestamps.

This means Internet protocols MUST NOT use local time without time zone information.

Incorrect Examples:

❌ 2002-07-15T10:30:00 (no time zone information)

Correct Examples:

✅ 2002-07-15T10:30:00Z (UTC)
✅ 2002-07-15T10:30:00+08:00 (explicit time zone)
✅ 2002-07-15T10:30:00-00:00 (UTC but zone unknown)

Interoperability Issues

If unqualified local time is used:

Sender: 2002-07-15T10:30:00 (New York local time, actually UTC-4)
Receiver in Tokyo: Misinterprets as Tokyo time (UTC+9)
Time difference error: 13 hours!

Implementation Recommendations

System Design

# Recommended: Always store time in UTC
def store_timestamp():
utc_time = datetime.now(timezone.utc)
return utc_time.isoformat() # 2024-12-21T10:30:00+00:00

# When displaying: Convert to user's local time zone
def display_timestamp(utc_time, user_timezone):
local_time = utc_time.astimezone(user_timezone)
return local_time.isoformat()

Database Storage

-- Recommended: Use TIMESTAMP WITH TIME ZONE
CREATE TABLE events (
id SERIAL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Avoid: TIMESTAMP WITHOUT TIME ZONE (causes ambiguity)

Key Principle: Store internally in UTC, convert to local time zone when displaying. Never use timestamps without time zone information for data exchange.