We shed light on the intricacies of global dates and times, covering formatting, time zones, regional calendars, and more.
We often think of internationalization (i18n) and localization (l10n) as translating text, ignoring all-too-important i18n aspects like date and time localization. Yet dates and times are represented in very different ways across geographies, and localizing them correctly is crucial to making users feel at home in our apps. This guide aims to shed some light on the intricacies of global dates and times. We’ll cover formatting, working with time zones, understanding regional calendars, and taking a look at date and time picker UI.
🗒️ While the examples in this guide target the browser and are written in JavaScript, the guide is intentionally tech-agnostic — there should be an equivalent to each concept in your platform and programming language of choice.
🗒️ Internationalization (i18n) and localization (l10n) allow us to make our apps available in different languages and to different regions, often for more profit. If you’re new to i18n and l10n, check out our guide to internationalization.
- Formatting dates and times
- Time zones
- Calendars
- Date and time pickers
- Wrapping up: Key takeaways in date and time localization
Formatting dates and times
How do we present dates and times to someone depending on where they hail from the world? We have to answer the second question first and determine the user’s locale. This will often be their system locale: If a user sets her operating system language to French and region to Canada, the default locale of an app presented to this user is likely fr-CA
(French Canada).
🗒️ A locale defines a language, a region, and sometimes more. Locales typically use IETF BCP 47 language tags, like en
for English, fr
for French, and es
for Spanish. Adding a region with the ISO Alpha-2 code (e.g., BH
for Bahrain, CN
for China, US
for the United States) is recommended for accurate date and number localization. So a complete locale might look like en-US
for American English or zh-CN
for Chinese as used in China.
🔗 Explore more language tags on Wikipedia and find country codes through the ISO’s search tool.
🗒️ Date and time formats are region-specific, so it’s important to use qualified locales when formatting dates e.g. use en-US
, not en
.
With our essential background in place, the straightforward approach to formatting localized dates and times involves creating a date object and then utilizing a native date formatting library. Let’s take a look at an example in the browser using the native JavaScript Intl.DateTimeFormat formatter.const date = new Date("2024-01-27T14:00:00Z"); const formatter = new Intl.DateTimeFormat(); formatter.format(date); // => 1/27/2024
Code language: JavaScript (javascript)
Our date
represents 2024-01-27 2:00 PM in UTC (more on time zones a bit later). We create an Intl.DateTimeFormat
without specifying any options, which means we want default date formatting for the default locale.
The format()
call spits out “1/27/2024” when en-US
(English America) is determined to be the default locale, respecting the American default date format of Month/Day/Year.
On my machine, however, the same code outputs “2024-01-27”. This is because my operating system is set to en-CA
(English Canada), and Year-Month-Day is the default Canadian date format.
🗒️ Intl.DateTimeFormat is available natively in all modern browsers and Node.js.
Default formats
When we don’t provide specific formatting options to a date formatting library, we often get the default format for a given locale. Here are some default date formats for various locales:const date = new Date("2024-01-27T14:00:00Z"); // We use `date.toLocaleDateString("en-US")` for // the examples below. This is a shortcut for // `new Intl.DateTimeFormat("en-US").format(date)` // See 🗒️ below for more info. // English-America date.toLocaleDateString("en-US"); // => "1/27/2024" // Arabic-Egypt date.toLocaleDateString("ar-EG"); // => "٢٧/١/٢٠٢٤" // Hindi-India date.toLocaleDateString("hi-IN"); // => "27/1/2024" // Russian-Russia date.toLocaleDateString("ru-RU"); // => "27.01.2024" // Chinese-China date.toLocaleDateString("zh-CN"); // => "2024/1/27" // Japanese-Japan date.toLocaleDateString("jp-JP"); // => "2024-01-27"
Code language: JavaScript (javascript)
🗒️ Date.toLocaleDateString()
uses Intl.DateTimeFormat
under the hood when it’s available. Read more on the Date MDN entry.
“What about times?” you might be asking. Of course, we can often use native libraries to format localized times as well. Here are some default time formats:// Date object with time of 2 PM UTC const date = new Date("2024-01-27T14:00:00Z"); // The below calls to // `date.ToLocaleTimeString(...)` are equivalent to // `new Intl.DateTimeFormat("en-US", { // timeZone: "UTC", // timeStyle: "medium", // }).format(date)` // See 🗒️ below for more info. // English-America date.toLocaleTimeString("en-US", { timeZone: "UTC" }); // => "2:00:00 PM" // Arabic-Egypt date.toLocaleTimeString("ar-EG", { timeZone: "UTC" }); // => "٢:٠٠:٠٠ م" // Hindi-India date.toLocaleTimeString("hi-IN", { timeZone: "UTC" }); // => "2:00:00 pm" // Russian-Russia date.toLocaleTimeString("ru-RU", { timeZone: "UTC" }); // => "14:00:00" // Chinese-China date.toLocaleTimeString("zh-CN", { timeZone: "UTC" }); // => "14:00:00" // Japanese-Japan date.toLocaleTimeString("jp-JP", { timeZone: "UTC" }); // => "2:00:00 p.m."
Code language: JavaScript (javascript)
🗒️ Just like Date.toLocaleDateString()
, Date.toLocaleTimeString()
uses Intl.DateTimeFormat
under the hood when it’s available. Read more on the Date MDN entry.
Note that in the above examples, we set the time zone to UTC using the timeZone
option. This is to maintain a consistent output for demonstration purposes. If we omit the option, the system time zone will be used. I’m currently in the UTC +1 time zone, so the en-US
example would output the following on my machine: “3:00:00 PM”.
🗒️ Again, we’ll tackle time zones in a bit more detail a bit later.
Preset formats
Beyond default formats, we often want to control the length of the date and time output. We can achieve this with preset formats that many native formatting libraries offer. In the JavaScript Intl.DateTimeFormat options, these are dateStyle
and timeStyle
. If you’re working on a different platform, there are likely equivalents in that platform’s formatting libraries.
dateStyle
— can be"full"
,"long"
,"medium"
, or"short”
.timeStyle
— can be"full"
,"long"
,"medium"
, or"short”
.
Let’s see what these look like in action:const date = new Date("2024-01-27T14:00:00Z"); const shortOptions = { timeZone: "UTC", dateStyle: "short", timeStyle: "short", }; // English-America new Intl.DateTimeFormat("en-US", shortOptions) .format(date); // => "1/27/24, 2:00 PM" // Arabic-Egypt new Intl.DateTimeFormat("ar-EG", shortOptions) .format(date); // => "٢٧/١/٢٠٢٤، ٢:٠٠ م" // Chinese-China new Intl.DateTimeFormat("zh-CN", shortOptions) .format(date); // => "2024/1/27 14:00" const fullOptions = { timeZone: "UTC", dateStyle: "full", timeStyle: "full", }; // English-America new Intl.DateTimeFormat("en-US", fullOptions) .format(date); // => "Saturday, January 27, 2024 at 2:00:00 PM Coordinated Universal Time" // Arabic-Egypt new Intl.DateTimeFormat("ar-EG", fullOptions) .format(date); // => "السبت، ٢٧ يناير ٢٠٢٤ في ٢:٠٠:٠٠ م التوقيت العالمي المنسق" // Chinese-China new Intl.DateTimeFormat("zh-CN", fullOptions).format(date); // => "2024年1月27日星期六 协调世界时 14:00:00"
Code language: JavaScript (javascript)
Preset options often conform to the conventions of the given locale. We can just set the length of the format we want and rely on the formatting library to handle the per-locale details.
Custom formats
Complete control over our date and time formatting is achieved via granular format specifiers. For example, we might want to show only the year of a given date or force 24-hour time representation. Any date formatting library worth its salt will allow us this level of customization. Here are examples using JavaScript’s Intl.DateTimeFormat
:const date = new Date("2024-01-27T14:00:00Z"); const options = { timeZone: "UTC", timeZoneName: "short", year: "2-digit", month: "narrow", weekday: "short", day: "numeric", hourCycle: "h24", hour: "numeric", minute: "numeric", }; // English-America date.toLocaleDateString("en-US", options); // => "Sat, J 27, 24, 14:00 UTC" // Arabic-Egypt date.toLocaleDateString("ar-EG", options); // => "السبت، ٢٧ ي ٢٤، ١٤:٠٠ UTC" // Hindi-India date.toLocaleDateString("hi-IN", options); // => "शनि, 27 ज 24, 14:00 UTC" // Russian-Russia date.toLocaleDateString("ru-RU", options); // => "сб, 27 Я 24 г., 14:00 UTC" // Chinese-China date.toLocaleDateString("zh-CN", options); // => "24年1月27日周六 UTC 14:00" // Japanese-Japan date.toLocaleDateString("jp-JP", options); // => "Sat, J 27, 24, 14:00 UTC"
Code language: JavaScript (javascript)
🔗 Check out all the formatting options provided by Intl.DateTimeFormat on the MDN documentation.
🗒️ You may have noticed that localizing dates and times often relies on local number systems. The previous Arabic example uses both Eastern Arabic numerals and Arabic script, “السبت، ٢٧ ي ٢٤، ١٤:٠٠ UTC”. Modern systems and their formatting libraries often take care of all of this for us, but it’s good to bear in mind.
🔗 Our Concise Guide to Number Localization covers numeral systems, currency, and more.
🗒️ To represent written days of the week, months, etc. we of course have to use the language and script of the target locale. Again, something to be mindful of.
Time zones
We usually stick to UTC or the server’s time zone for keeping track of times on the back end. We can then convert the date during formatting to match the user’s local time zone on the front end. The key here is knowing the source and destination time zones so we can nail the conversion.
🗒️ Heads up if you’re working with full-stack frameworks like Next.js or SvelteKit — mismatched time zones between server-side rendering and client-side can lead to those pesky hydration errors. One fix is to keep your time zone consistent on both the server and client side by setting it once in a shared config.
When working with time zones, we often use a set of standards, primarily UTC, IANA, and ISO 8601. Let’s go over these briefly.
UTC
UTC (Coordinated Universal Time) is the modern standard for global civil time, refining GMT (Greenwich Mean Time) with atomic clock precision alongside Earth’s rotation. Time zones are represented as offsets from UTC. For example, UTC-05:00
is equivalent to Eastern Standard Time (EST).
🔗 Dive deeper into UTC’s specifics on its Wikipedia page.
IANA
The Internet Assigned Numbers Authority (IANA) handles crucial Internet roles like IP addressing, DNS management, and protocol IDs to keep the net running smoothly. Its Time Zone Database tracks global time zones, including daylight saving changes and UTC offsets, updated for political shifts. Time zones use an Area/Location
format, like America/New_York
, and have abbreviations such as EST
(Eastern Standard Time).
🔗 Dive into the details on the tz database’s Wikipedia page.
🔗 The Wikipedia List of tz database time zones is an excellent lookup.
ISO 8601
We’ve been using the ISO 8601 format for dates, like 2024-02-23T15:45:00Z
, which includes the date followed by T
and the time in 24-hour format. Z
indicates UTC, but you can specify other time zones with an offset, like +03:00
e.g. 2024-02-23T15:45:00+03:00
.
🗒️ ISO 8601 is a common standard, but your source date can be formatted in a variety of ways. Make sure you know the format when parsing.
🔗 Learn more on Wikipedia’s ISO 8601 page.
Code examples
Let’s see how these standards work together when localizing dates and times. Here are examples in JavaScript using Intl.DateTimeFormat
:// Note how the `Date` object knows how to parse // the ISO 8601 format. The following datetime // includes 3:45 PM in the UTC +01:00 time // zone. const date = new Date("2024-02-27T15:45:00+01:00"); date.toLocaleString("en-US", { // Convert to target IANA `timeZone` when // formatting. timeZone: "America/New_York", dateStyle: "medium", timeStyle: "full", }); // => "Feb 27, 2024, 9:45:00 AM Eastern Standard Time" date.toLocaleString("en-US", { // Use the abbreviated time zone; this // affects conversion as well as formatting. timeZone: "EST", dateStyle: "medium", timeStyle: "full", }); // => "Feb 27, 2024, 9:45:00 AM GMT-05:00" // Format in Hindi-India and convert to // the Indian time zone. date.toLocaleString("hi-IN", { timeZone: "Asia/Kolkata", dateStyle: "medium", timeStyle: "full", }); // => "27 फ़र॰ 2024, 8:15:00 pm भारतीय मानक समय" // Chinese-China in the Shanghai time zone. date.toLocaleString("zh-CH", { timeZone: "Asia/Shanghai", dateStyle: "medium", timeStyle: "full", }) // => "2024年2月27日 中国标准时间 22:45:00"
Code language: JavaScript (javascript)
Calendars
While the Gregorian calendar — Jan, Feb, etc. — is ubiquitous, there are many regional and cultural calendars used all over the world. This is important when localizing, since some calendars, like the Islamic, Persian, and Buddhist calendars are used on a day-to-day basis. Indeed, certain regions use a non-Gregorian calendar as the default.
Some of these calendars serve cultural purposes. Some are solar, others are lunar, and each has its starting year.