If you have ever chased a bug where the timestamp is off by one hour despite carrying +09:00, or where the date differs between production and your laptop, the cause is usually the same: treating a time zone and a UTC offset as the same thing. They are different concepts, and they belong in different columns.
+09:00 is the offset from UTC at one instant. Asia/Tokyo is the full set of rules a region has used, including when those rules changed. You cannot recover the second from the first.
An offset only describes one instant
2026-08-03T10:00:00+09:00 says that this particular timestamp is nine hours ahead of UTC. It does not say the region is always nine hours ahead.
Regions with daylight saving time make this obvious. New York is -05:00 in winter and -04:00 in summer. Storing -05:00 alone does not tell you the value came from New York: other regions share that offset, and New York itself changes in summer.
Japan has a case too. Japan observed daylight saving time from 1948 to 1951, when summer time was +10:00. A system that hardcodes +09:00 converts dates in that window incorrectly.
What an IANA time zone name carries
Asia/Tokyo and America/New_York are identifiers from the IANA time zone database. They are not just labels: each one carries the history of when the offset changed and to what.
| UTC offset (+09:00) | IANA name (Asia/Tokyo) | |
|---|---|---|
| Describes | The offset at one instant | A region's rules over time |
| Daylight saving | Cannot express it | Includes the transition points |
| Historical dates | Cannot recover past rules | Converts using the rules then in force |
| Future rule changes | Cannot follow them | Follows via database updates |
| Use for | Recording the instant something happened | Recording which region a time belongs to |
Deciding what to store
Something that already happened
Store UTC. Login times, order timestamps, and updated-at columns all fall here. The instant is fixed, and later rule changes do not move it. Convert to the viewer time zone when rendering.
-- PostgreSQL
created_at timestamptz NOT NULL DEFAULT now()
A future appointment
Do not convert it to UTC up front. A meeting at 9:00 local time on 2027-10-01, stored as UTC, will drift if the region changes its rules before then. Countries do abolish daylight saving time and move transition dates.
Store the local date-time and the IANA name separately.
starts_at_local timestamp -- 2027-10-01 09:00:00 (no zone)
time_zone text -- 'Asia/Tokyo'
Compute the actual instant at display or notification time, using the tz database as it stands then.
ISO 8601 notation
| Notation | Meaning |
|---|---|
2026-08-03T01:00:00Z | 01:00 UTC. Z is equivalent to +00:00 |
2026-08-03T10:00:00+09:00 | The same instant, expressed in a +09:00 region |
2026-08-03T10:00:00 | No zone stated. Meaning depends on the reader |
2026-08-03 | Date only; carries no time information |
When exchanging timestamps over an API, always include the offset. A bare local string is one of the most common sources of disagreement between sender and receiver.
Check how your runtime behaves
// JavaScript: format in an explicit time zone
new Intl.DateTimeFormat('en-US', {
timeZone: 'Asia/Tokyo',
dateStyle: 'full',
timeStyle: 'long',
}).format(new Date('2026-08-03T01:00:00Z'));
// Check the runtime's own time zone
Intl.DateTimeFormat().resolvedOptions().timeZone; // e.g. "Asia/Tokyo"
Code that depends on the server's time zone setting behaves differently in production and on a laptop. Specifying the time zone explicitly, rather than relying on the environment, is the most reliable way to avoid this class of bug.
Frequently Asked Questions
Is Asia/Tokyo the same as +09:00?
Today both describe a time nine hours ahead of UTC, but they mean different things. +09:00 is a fixed offset for one instant; Asia/Tokyo is the set of rules a region has used over time. Japan observed daylight saving time from 1948 to 1951, during which Asia/Tokyo was +10:00. For historical or future dates, the region name carries more information.
What should I store in the database?
For something that already happened, store UTC and convert to the viewer time zone at display time. Future appointments are different: converting them to UTC in advance means the appointment shifts if the region changes its rules. Store the local date-time and the IANA time zone name separately for future events.
What does the Z in ISO 8601 mean?
It denotes UTC and is equivalent to +00:00. 2026-08-03T01:00:00Z is the same instant as 2026-08-03T10:00:00+09:00. A value with no suffix, such as 2026-08-03T10:00:00, does not state which time zone it belongs to, so its meaning depends on whoever reads it.