12-Hour vs 24-Hour - Regional Conventions
The most basic format choice is 12-hour (AM/PM) versus 24-hour. The 12-hour clock dominates everyday conversation in the U.S., Canada, Australia, and the Philippines, while the 24-hour clock is standard for official notation and timetables in continental Europe, Japan, Korea, and China. The split is rarely absolute: countries on the 12-hour clock still print 24-hour departure times in some transport schedules, airlines in particular, and countries on the 24-hour clock still say the equivalent of "morning" and "afternoon" in speech. The U.K. shows the dual structure especially plainly, with 12-hour in spoken English and 24-hour on train timetables and official documents.
The right software design defers to the user's locale rather than picking a global default. A server hardcoding "14:30" will look wrong to American users; "2:30 PM" will look pedantic to those used to the 24-hour clock. Honor the OS or browser locale and let the formatting layer choose, rather than baking a choice into business logic.
ISO 8601 - Machine-Readable, Not Human-Readable
ISO 8601 (e.g., 2026-05-15T16:30:00+09:00) is excellent for machine-to-machine data exchange but should not be presented directly to end users. Use it for API payloads, database storage, and log output, then convert to a localized human-readable form at the display layer. Mixing roles between machine and human formats is one of the most common causes of user complaints about timestamps.
There is one exception: technical dashboards and debug screens for engineers. The explicit time zone offset in ISO 8601 removes ambiguity when comparing events across regions. Whether your audience is technical or general determines whether ISO 8601 is acceptable in the user-facing path.
JavaScript's Intl.DateTimeFormat API provides locale-aware formatting using the OS's internationalization data. new Intl.DateTimeFormat('ja-JP', { hour: '2-digit', minute: '2-digit' }).format(date) returns "16:30," while changing the locale to 'en-US' yields "04:30 PM." The '2-digit' setting asks for zero padding, so if you want the unpadded "4:30 PM" instead, set hour to 'numeric'. No third-party library required, and the data stays current with OS updates.
Watch for two pitfalls. First, dateStyle/timeStyle and individual options (year/month/day/hour/minute) cannot be mixed in the same call. Second, output details vary slightly with the internationalization data (ICU) version bundled in the browser or runtime - most notoriously which kind of space character sits before AM/PM, since a plain space and a narrow no-break space look identical on screen but differ byte for byte. That makes snapshot tests flaky. Use string-includes assertions or normalize the output if you need stable test fixtures.
Relative Time - The "3 Minutes Ago" Pattern
Social and chat apps commonly display "3 minutes ago" or "yesterday" rather than absolute times. The intuition is simple, but several design choices follow. How granular should sub-minute display be (most services collapse to "just now" under a minute)? How far back before switching to absolute dates (some services switch after about a day, others keep relative labels for roughly a week)? How is "yesterday" computed when the user crosses a time zone?
Intl.RelativeTimeFormat handles the localization of the resulting string but does not pick the appropriate unit (second, minute, hour, day, week, month, year). You still need a small piece of logic to select the right unit based on the magnitude of the difference. Several libraries (date-fns, Day.js relativeTime plugin) wrap this for you, but the underlying decisions are still yours to make.
Edge Cases - 12:00 AM and 12:00 PM
The 12-hour clock has a famous ambiguity at noon and midnight. By convention, 12:00 PM is noon and 12:00 AM is midnight, but this is logically inconsistent (PM stands for post meridiem, after noon, yet noon itself is labeled PM). The aviation industry sidesteps the issue by using 24-hour notation (00:00 for midnight, 12:00 for noon), and U.S. legal documents typically write "12:00 noon" or "12:00 midnight" explicitly.
The defensive approach in software is to store times internally as 24-hour integers (0-23) and convert only at display. When accepting user input in 12-hour form, validate the AM/PM interpretation explicitly and include 12:00 AM and 12:00 PM in test cases. Bugs at the noon/midnight boundary are easy to introduce and easy to miss in casual testing.