Lokalise graphic for dates and times

Date and time localization

Date and time localization is crucial when you’re building apps or software that people all over the world will use. Different countries have their own ways of showing dates and times, and if your app doesn’t handle these differences, it can get confusing for users.

In this article, we’ll explore why date and time localization matters, some of the tricky parts involved, and how you can make it easier across various programming languages and frameworks. We’ll dive into specific techniques and tools for localizing dates and times in Python, PHP, Java, JavaScript, and Ruby on Rails.

Whether you’re developing a multi-language app or just want your software to work better in different places, understanding these concepts will help you create something that everyone can enjoy, no matter where they are in the world, and is integral to effective software internationalization practices.

    Introduction

    It’s no surprise that people in different countries write dates and times in their own unique ways. For example, in the US, the date is usually written as mm/dd/yyyy with slashes separating the month, day, and year. So, 09/01/2021 means September 1, 2021.

    But if you show this same date to someone in Russia, they might think it means January 9, 2021! That’s because in Russia and other CIS countries, the format is dd.mm.yyyy, where the day comes first and dots separate the parts.

    Things get even more interesting in some Asian countries, like Iran, where the date is written as yyyy/mm/dd—starting with the year, then the month, and finally the day.

    Time formats can also be different. Some countries use a 12-hour clock with am/pm, while others stick to the 24-hour format. So, getting the date and time localization right based on the user’s locale is super important to avoid confusion.

    Date and time localization in Python

    Using the datetime module

    When working with date and time localization in Python, the go-to tool is the built-in datetime module. This handy module makes it easy to handle dates and times in your applications.

    You can import it like this:

    from datetime import datetime

    Creating datetime objects

    To get the current local date and time, you can use the following code:

    currentTime = datetime.now()
    print(currentTime)

    The output will look something like this: 2023-10-24 16:11:39.824903.

    If you need to create a datetime object from a specific string, you can do it like this:

    specificTime = datetime.fromisoformat('2011-11-04T00:05:23')
    print(specificTime) # => 2011-11-04 00:05:23

    You can also create a datetime object from a POSIX timestamp and even specify the timezone. A timestamp is just the number of seconds that have passed since the Unix epoch, which started on January 1, 1970.

    from datetime import datetime, timezone
    
    specificTime = datetime.fromtimestamp(1698153639, timezone.utc)
    print(specificTime) # => 2023-10-24 13:20:39+00:00

    In the example above, we use the UTC timezone, but remember that you need to import timezone from the datetime module to make it work.

    Formatting datetime objects

    Now that we know how to create datetime objects in Python, the next step is learning how to format them. After all, you’ll often need to display dates and times in a specific way.

    One of the most common methods for formatting dates and times in Python is strftime. This method takes a formatting pattern, which includes special codes to customize the output. There are lots of formatting codes available, and you can find a full list in the official documentation.

    Let’s say you want to display the current year, the full name of the month, the day, and the time (hours and minutes). Here’s how you can do that:

    currentTime = datetime.now()
    print(currentTime.strftime("%Y %B %d, %H:%M"))

    The output will look like this: 2023 October 24, 16:27.

    So, what’s happening here? Let’s break it down:

    • %Y stands for the full year (e.g., 2023).
    • %B gives you the month’s full name, based on the current locale. This means the output might change depending on the system’s language settings. (We’ll dive into locales more later on.)
    • %d is for the day of the month, with zero-padding. Zero-padding just means that if the day is a single digit, it gets a leading zero (like 01, 09, or 31).
    • %H represents the hour in a 24-hour format, also zero-padded.
    • %M is for the minutes, again zero-padded.

    You can mix and match these formatting codes to display as much or as little detail as you need.

    Parsing dates with dateutil

    When working with dates in Python, sometimes you need to take a date in string form and convert it into a datetime object. This is especially useful when you’re dealing with user input or data from external sources where the date format isn’t fixed. Enter dateutil—a powerful library that makes parsing dates from strings super easy.

    To start using dateutil, you’ll first need to install it if you haven’t already:

    pip install python-dateutil

    Once installed, you can use the parser module to effortlessly convert date strings into datetime objects. Here’s a quick example:

    from dateutil import parser
    
    date_str = "24th October 2023, 17:09"
    parsed_date = parser.parse(date_str)
    print(parsed_date)  # Output: 2023-10-24 17:09:00

    As you can see, the parser module automatically understands the date format, even though it’s a bit informal. It smartly interprets “24th October 2023, 17:09” and converts it into a datetime object with the year, month, day, hour, and minute all properly set.

    Why use dateutil for parsing?

    What makes dateutil so handy is its flexibility. It can handle a wide range of date formats without you needing to specify the exact format each time. This is particularly useful when your application might receive dates in different formats or from various locales.

    Here’s another example with a different date format:

    from dateutil import parser
    
    date_str = "2023/10/24 5:09 PM"
    parsed_date = parser.parse(date_str)
    print(parsed_ate)  # Output: 2023-10-24 17:09:00

    Notice how dateutil automatically figures out that “5:09 PM” means 17:09 in 24-hour time. It’s smart enough to handle these kinds of variations, saving you from having to write complex date parsing logic yourself.

    Handling ambiguous dates

    Sometimes, a date string might be ambiguous. For instance, “03/04/2023” could be interpreted as either March 4th or April 3rd, depending on the locale. By default, dateutil uses the American convention (MM/DD/YYYY), but you can control this by using the dayfirst parameter:

    date_str = "03/04/2023"
    parsed_date = parser.parse(date_str, dayfirst=True)
    print(parsed_date)  # Output: 2023-04-03 00:00:00

    Here, setting dayfirst=True tells dateutil to interpret “03/04/2023” as April 3rd, not March 4th.

    Working with locales in Python

    Now, let’s see how to handle different locales in your Python apps. This is super useful when you want your dates and times to match the local language and formatting rules.

    To get started, you’ll need to import the locale module:

    import locale

    Next, let’s switch the current locale to Russian (ru_RU) and display the current date and time again:

    locale.setlocale(locale.LC_ALL, 'ru_RU.UTF8')
    
    currentTime = datetime.now()
    print(currentTime.strftime("%Y %B %d, %H:%M"))

    With this change, the month name will be automatically localized for us. Here’s what the output will look like: 2023 Октябрь 24, 17:09.

    Awesome, right?

    Working with timezones using zoneinfo

    When handling date and time localization in Python, working with time zones is a crucial part. In the past, the pytz library was the go-to solution for managing time zones.

    However, starting with Python 3.9, there’s a new, built-in solution called zoneinfo. This modern alternative provides timezone support right out of the box and is designed to replace pytz.

    Getting started with zoneinfo

    To start using zoneinfo, you simply need to import it from the standard library. Let’s look at how you can create timezone-aware datetime objects:

    from zoneinfo import ZoneInfo
    from datetime import datetime
    
    # Set current time in UTC
    utc_time = datetime.now(ZoneInfo('UTC'))
    
    # Convert UTC to New York time
    ny_time = utc_time.astimezone(ZoneInfo('America/New_York'))
    print(ny_time.strftime("%Y-%m-%d %H:%M:%S %Z%z"))  # Output: 2023-10-24 12:09:00 EDT-0400

    In this example, we start by getting the current time in UTC and then convert it to New York time. The ZoneInfo class takes care of all the timezone conversions, including handling Daylight Saving Time (DST) automatically.

    Why switch to zoneinfo?

    zoneinfo offers several advantages over pytz:

    1. Simplicity: Since zoneinfo is part of the Python standard library, there’s no need to install an external package like pytz. This makes your code simpler and reduces dependencies.
    2. Automatic DST handling: zoneinfo automatically adjusts for Daylight Saving Time (DST) without requiring extra configuration, making it easier to work with dates across different regions.
    3. Up-to-date timezone data: The zoneinfo module uses the IANA Time Zone Database, which is regularly updated to reflect changes in time zone rules around the world.

    Creating timezone-aware datetime objects

    Let’s look at another example where we create a timezone-aware datetime object for a specific time and location:

    from zoneinfo import ZoneInfo
    from datetime import datetime
    
    # Create a timezone-aware datetime for Tokyo
    tokyo_time = datetime(2023, 10, 24, 15, 30, tzinfo=ZoneInfo('Asia/Tokyo'))
    print(tokyo_time.strftime("%Y-%m-%d %H:%M:%S %Z%z"))  # Output: 2023-10-24 15:30:00 JST+0900

    Here, we create a datetime object for 3:30 PM on October 24, 2023, in Tokyo’s time zone. The output includes the local time, the timezone abbreviation (JST), and the UTC offset (+0900).

    Converting between timezones

    One of the great features of zoneinfo is its ability to easily convert between different timezones:

    from zoneinfo import ZoneInfo
    from datetime import datetime
    
    # Original time in London
    london_time = datetime(2023, 10, 24, 12, 0, tzinfo=ZoneInfo('Europe/London'))
    
    # Convert London time to Sydney time
    sydney_time = london_time.astimezone(ZoneInfo('Australia/Sydney'))
    print(sydney_time.strftime("%Y-%m-%d %H:%M:%S %Z%z"))  # Output: 2023-10-24 22:00:00 AEDT+1100

    In this example, we take a datetime object representing noon in London and convert it to the corresponding time in Sydney. The zoneinfo module handles all the necessary adjustments, including the time difference and any DST changes.

    Date and time localization in Java

    DateFormat class

    When it comes to date and time localization in Java, the DateFormat class is a powerful tool. It allows you to format and parse dates based on the locale you specify.

    Using the DateFormat class

    To get started, you can use the getDateInstance method, which allows you to specify both the format style and the locale.

    Here’s an example of how to use DateFormat to parse a date string:

    import java.text.DateFormat;
    import java.text.ParseException;
    import java.util.Date;
    import java.util.Locale;
    
    public class DateLocalizationExample {
        public static void main(String[] args) {
            DateFormat shortDateFormat = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
    
            String dateString = "01/27/1997";
    
            try {
                Date date = shortDateFormat.parse(dateString);
                System.out.println(date);
            } catch (ParseException e) {
                System.out.println("Cannot parse the string!");
            }
        }
    }

    In this example, we’re using DateFormat.SHORT with the Locale.US setting. The SHORT format is typically used for concise date representations. We then attempt to parse the string “01/27/1997” into a Date object.

    Here’s what the output might look like: Mon Jan 27 00:00:00 IST 1997.

    As you can see, the date string was successfully parsed and converted into a Date object.

    Exploring other date formats

    The DateFormat class offers various styles for formatting dates:

    • DateFormat.SHORT: A short date format, usually numeric (e.g., 1/27/97).
    • DateFormat.LONG: A longer, more detailed date format (e.g., January 27, 1997).
    • DateFormat.FULL: The most detailed format, which may include the day of the week (e.g., Monday, January 27, 1997).

    Here’s an example that demonstrates using a different date format:

    DateFormat longDateFormat = DateFormat.getDateInstance(DateFormat.LONG, Locale.US);
    
    try {
        Date date = longDateFormat.parse(dateString);
        System.out.println(date);
    } catch (ParseException e) {
        System.out.println("Cannot parse the string!");
    }

    This will parse the date string in a longer format, providing more detail compared to the SHORT format.

    SimpleDateFormat class

    Another handy class in Java for date and time localization is SimpleDateFormat.

    This class allows you to define custom date and time patterns, making it easy to parse and format dates exactly the way you need.

    Parsing dates with SimpleDateFormat

    If you want to quickly parse a date string into a Date object, SimpleDateFormat is a great tool. Here’s a basic example:

    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class SimpleDateFormatExample {
        public static void main(String[] args) {
            DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
    
            try {
                Date date = dateFormat.parse("01/02/1997");
                System.out.println(date);
            } catch (ParseException e) {
                System.out.println("Cannot parse the string!");
            }
        }
    }

    In this code, we use the pattern "dd/MM/yyyy" to parse the date string "01/02/1997". This pattern specifies that the date string should be in the format of day (dd), month (MM), and year (yyyy).

    The output of this code will be: Sat Feb 01 00:00:00 IST 1997.

    Formatting dates with SimpleDateFormat

    In addition to parsing, SimpleDateFormat can also format Date objects into strings. This is especially useful when you need to display dates in a specific format. Here’s how you can do it:

    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class DateFormatExample {
        public static void main(String[] args) {
            SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy");
            String formattedDate = formatter.format(new Date());
    
            System.out.println(formattedDate);
        }
    }

    In this example, we create a SimpleDateFormat object with the pattern "MM-dd-yyyy", which specifies that the month comes first, followed by the day and then the year. We then format the current date using this pattern.

    The output will look something like this: 09-04-2024.

    Working with time zones in Java

    Handling date and time localization across different time zones is crucial when developing global applications. In Java, working with time zones became more intuitive with the introduction of the ZonedDateTime class in Java 8. This class allows you to work with date and time, while automatically managing time zone details.

    Using ZonedDateTime

    The ZonedDateTime class represents a date-time with a time zone in the ISO-8601 calendar system. You can easily get the current date and time with the system’s default time zone using the now() method:

    import java.time.ZonedDateTime;
    
    public class ZonedDateTimeExample {
        public static void main(String[] args) {
            System.out.println(ZonedDateTime.now());
        }
    }

    Here’s what the output might look like: 2021-07-20T00:02:03.232139+05:30[Asia/Colombo].

    Creating custom ZonedDateTime instances

    You can also create a ZonedDateTime for a specific date and time, using the of() method. This method allows you to specify the year, month, day, hour, minute, second, nanosecond, and time zone:

    import java.time.ZonedDateTime;
    import java.time.ZoneId;
    
    public class CustomZonedDateTime {
        public static void main(String[] args) {
            ZonedDateTime customTime = ZonedDateTime.of(
                2021, 11, 4, 10, 25, 20, 30000,
                ZoneId.systemDefault()
            );
    
            System.out.println(customTime);
        }
    }

    In this example, we’ve created a ZonedDateTime for November 4, 2021, at 10:25:20 AM, using the system’s default time zone.

    The TimeZone class

    Before Java 8, developers often used the TimeZone class to handle time zones. While ZonedDateTime is now preferred for most tasks, TimeZone is still useful, especially in legacy code. The TimeZone class allows you to set and get time zones in your application.

    Here’s how you can use the TimeZone class to work with different time zones:

    import java.util.Date;
    import java.util.TimeZone;
    
    public class TimeZoneExample {
        public static void main(String[] args) {
            Date now = new Date();
    
            // Set the time zone to Europe/London
            TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
            System.out.println(now);
    
            // Set the time zone to UTC
            TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
            System.out.println(now);
    
            // Set the time zone to GMT
            TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
            System.out.println(now);
        }
    }

    This code shows the current date and time in three different time zones: London, UTC, and GMT. Here’s what the output might look like:

    • Mon Jul 19 21:29:22 BST 2021
    • Mon Jul 19 20:29:22 UTC 2021
    • Mon Jul 19 20:29:22 GMT 2021

    Why use ZonedDateTime over TimeZone?

    While TimeZone is still used, especially in older applications, ZonedDateTime is the modern approach recommended for most new projects. Here’s why:

    • Simplicity: ZonedDateTime integrates both the date-time and time zone into a single object, making it easier to manage and less error-prone.
    • Rich API: ZonedDateTime offers more methods and better support for complex time zone rules, including handling Daylight Saving Time (DST).
    • ISO-8601 Compliance: ZonedDateTime adheres to the ISO-8601 standard, which is widely used and recognized internationally.

    By using ZonedDateTime, you’re better equipped to handle the complexities of date and time localization in a global application.

    Working with the LocalDate class

    The LocalDate class, introduced in Java 8, is an essential part of working with dates in Java. It’s designed to be immutable and thread-safe, which means once you create a LocalDate instance, it can’t be changed, and it’s safe to use in multi-threaded environments.

    LocalDate represents a date in ISO format, like 2021-01-01, making it a straightforward choice for date localization.

    Getting the current date with LocalDate

    To get the current date, you can use the now() method provided by the LocalDate class:

    import java.time.LocalDate;
    
    public class LocalDateExample {
        public static void main(String[] args) {
            LocalDate currentDate = LocalDate.now();
            System.out.println("Current date: " + currentDate);
        }
    }

    This will output something like: Current date: 2024-09-04.

    Parsing dates with LocalDate

    You can also create a LocalDate instance from a string using the parse() method. The string must be in a parsable format, typically yyyy-MM-dd:

    LocalDate parsedDate = LocalDate.parse("2020-01-21");
    System.out.println("Parsed date: " + parsedDate);

    This will output: Parsed date: 2020-01-21.

    The parse() method is handy when you need to convert a date string into a LocalDate object, especially when dealing with input data.

    Date calculations with LocalDate

    One of the standout features of LocalDate is its ability to perform various date-related calculations effortlessly. Whether you need to add or subtract days, months, or years, LocalDate has you covered.

    For example, to get a date two days from today, you can use the plusDays() method:

    LocalDate twoDaysFromToday = LocalDate.now().plusDays(2);
    System.out.println("Two days from today: " + twoDaysFromToday);

    Here’s the output: Two days from today: 2024-09-06.

    Similarly, you can subtract time from a date using the minus() method. Here’s how to subtract two months from today’s date:

    import java.time.LocalDate;
    import java.time.temporal.ChronoUnit;
    
    LocalDate somePreviousDay = LocalDate.now().minus(2, ChronoUnit.MONTHS);
    System.out.println("Two months ago: " + somePreviousDay);

    Output: Two months ago: 2024-07-04.

    More features of LocalDate

    The LocalDate class also makes it easy to get specific information about a date, such as the day of the week, the first day of the month, or whether a given year is a leap year.

    Here’s an example that demonstrates these features:

    import java.time.LocalDate;
    import java.time.DayOfWeek;
    import java.time.temporal.TemporalAdjusters;
    
    public class LocalDateFeatures {
        public static void main(String[] args) {
            // Get the day of the week
            DayOfWeek day = LocalDate.parse("2021-02-11").getDayOfWeek();
            
            // Get the first day of the month
            LocalDate firstDayOfMonth = LocalDate.parse("2021-03-30").with(TemporalAdjusters.firstDayOfMonth());
            
            // Check if the year is a leap year
            boolean leapYearCheck = LocalDate.parse("2021-02-11").isLeapYear();
    
            System.out.println("Day of the week: " + day);
            System.out.println("First day of the month: " + firstDayOfMonth);
            System.out.println("Is leap year: " + leapYearCheck);
        }
    }

    The output will be:

    day : THURSDAY
    firstDayOfMonth : 2023-03-01
    leapYearCheck : false

    Date and time localization in JavaScript

    When developing applications for the browser, handling date and time localization is crucial to ensure that your users see dates and times in a format that makes sense to them. JavaScript provides several ways to work with dates and times, making it possible to create, format, and manipulate datetime objects easily.

    Creating datetime objects

    The simplest way to create a datetime object in JavaScript is by instantiating the Date class:

    const currentDateTime = new Date();
    console.log(current);

    Here’s the output: Tue Oct 24 2023 17:16:51 GMT+0300 (Eastern European Summer Time).

    By default, this creates a datetime object representing the current local date and time according to the user’s system settings. This means that every user will see the date and time in the format and timezone that their browser is set to.

    You can also create a Date object by providing a specific timestamp:

    const dateTime = new Date(1698157119973);
    console.log(dateTime);

    Here’s the output: Tue Oct 24 2023 17:18:39 GMT+0300 (Eastern European Summer Time).

    In JavaScript, the timestamp is the number of milliseconds that have passed since the Unix epoch (January 1, 1970). This is different from some other systems, where the timestamp is often in seconds.

    Creating dates from strings

    Instead of passing a timestamp, you can also create a Date object using a datetime string. This can be particularly useful when you’re working with data input from users or when displaying dates in different formats:

    const dateTime = new Date("2020-02-20T12:00:00Z");
    console.log(dateTime);

    Output: Thu Feb 20 2020 14:00:00 GMT+0200 (Eastern European Standard Time).

    In this example, the "Z" at the end of the string indicates that the time is in UTC. The browser automatically adjusts this to the user’s local time zone when displaying the date.

    It’s also possible to create a Date object using just the date, without specifying the time:

    const dateTime = new Date("2020-02-20");
    console.log(dateTime);

    The output will be: Thu Feb 20 2020 02:00:00 GMT+0200 (Eastern European Standard Time).

    In this case, since the time isn’t provided, the browser assumes the time to be midnight (00:00:00) in the local time zone. This feature is particularly useful when the exact time of day isn’t important and you only need to work with the date itself.

    Using the .toLocaleDateString() method

    The Date.prototype.toLocaleDateString() method in JavaScript is a powerful tool for date and time localization. It returns a string that represents the date portion of a Date object, formatted according to the specified locale and options. This method is particularly useful for displaying dates in a way that is familiar to users, based on their browser’s settings or a specific locale you choose.

    Localizing dates with .toLocaleDateString()

    The .toLocaleDateString() method makes it easy to localize dates for different regions. Let’s look at an example:

    const dateTime = new Date();
    
    const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
    
    console.log(dateTime.toLocaleDateString('de-DE', options));
    console.log(dateTime.toLocaleDateString('ru-RU', options));
    console.log(dateTime.toLocaleDateString(undefined, options));

    In this example, we first create a Date object representing the current date and time. Then, we define an options object to specify how we want the date to be formatted, including the day of the week (weekday), the year, the month, and the day.

    We use the .toLocaleDateString() method to display the date in three different locales:

    1. German (de-DE)
    2. Russian (ru-RU)
    3. Default locale (which depends on the user’s browser settings)

    Here’s what the output might look like:

    Dienstag, 24. Oktober 2023  // German locale
    вторник, 24 октября 2023 г.  // Russian locale
    Tuesday, October 24, 2023    // Default locale (e.g., en-US)

    Using the .toLocaleTimeString() method

    The Date.prototype.toLocaleTimeString() method in JavaScript is a handy tool for localizing the time portion of a Date object. It returns a string that represents the time, formatted according to the locale and options you specify. This method is particularly useful when you need to display time in a way that is familiar to your users, based on their browser’s settings or a specific locale.

    Localizing time with .toLocaleTimeString()

    The .toLocaleTimeString() method allows you to easily format the time according to different regional settings. Let’s look at a few examples:

    const dateTime = new Date();
    
    console.log(dateTime.toLocaleTimeString('en-US'));  //  Example output: "5:35:48 PM"
    console.log(dateTime.toLocaleTimeString('fr-FR'));  //  Example output: "17:35:48"
    console.log(dateTime.toLocaleTimeString('ja-JP'));  //  Example output: "17:35:48"

    In these examples:

    • en-US (English – United States): The time is displayed in a 12-hour format with AM/PM.
    • fr-FR (French – France) and ja-JP (Japanese – Japan): The time is displayed in a 24-hour format, which is more common in these locales.

    Customizing time formats

    The .toLocaleTimeString() method is quite flexible and allows you to customize how the time is displayed using various options. For instance, you can specify whether to use a 12-hour or 24-hour clock, whether to include seconds, and how to display the time zone.

    Here’s an example that demonstrates some of these options:

    const dateTime = new Date();
    
    // Use 12-hour format with AM/PM
    console.log(dateTime.toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric', second: 'numeric', hour12: true }));  
    // Example output: "5:35:48 PM"
    
    // Use 24-hour format without seconds
    console.log(dateTime.toLocaleTimeString('de-DE', { hour: 'numeric', minute: 'numeric', hour12: false }));  
    // Example output: "17:35"
    
    // Display time with time zone
    console.log(dateTime.toLocaleTimeString('en-GB', { hour: 'numeric', minute: 'numeric', timeZoneName: 'short' }));  
    // Example output: "17:35 BST"

    In these examples:

    • 12-hour format with seconds: The time is shown in a 12-hour format with AM/PM, including seconds.
    • 24-hour format without seconds: The time is displayed in a 24-hour format, showing only hours and minutes.
    • Time with time zone: The time is displayed with a short time zone name (like BST for British Summer Time).

    Handling the default locale

    If you don’t specify a locale, the .toLocaleTimeString() method will use the default locale set in the user’s browser:

    console.log(dateTime.toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric' }));

    This will format the time according to the user’s regional settings, which is particularly useful for creating applications that need to adapt to a wide audience without explicitly setting the locale.

    Using Intl.DateTimeFormat() for date and time formatting

    The Intl.DateTimeFormat() object in JavaScript is a versatile tool for formatting and localizing date and time. It offers a wide range of options to customize how dates and times are presented, making it easier to cater to users from different regions or who use different languages.

    Formatting dates with Intl.DateTimeFormat()

    Let’s explore how Intl.DateTimeFormat() works with a few examples:

    const date = new Date(Date.now());
    
    console.log(new Intl.DateTimeFormat('en-US').format(date));

    In the first example, we use the en-US locale to format the current date.

    Output: 10/24/2023.

    Handling fallback languages

    Intl.DateTimeFormat() also allows you to specify multiple locales, providing a fallback option if the primary locale isn’t supported. In the next example, we try to use the Balinese (ban) locale, which may not be widely supported, so we provide Indonesian (id) as a fallback:

    console.log(new Intl.DateTimeFormat(['ban', 'id']).format(date));

    Output: 24/10/2023.

    Since the Balinese locale might not be available, the date is formatted according to the Indonesian locale, resulting in the DD/MM/YYYY format.

    Customizing date and time styles

    One of the powerful features of Intl.DateTimeFormat() is its ability to customize the format using options like dateStyle and timeStyle. In the final example, we format the date and time using the Russian (ru-RU) locale, with the full date style and long time style:

    console.log(new Intl.DateTimeFormat('ru-RU', { dateStyle: 'full', timeStyle: 'long' }).format(date));

    Output: вторник, 24 октября 2023 г. в 17:35:48 GMT+3.

    Here, the date is fully spelled out, including the day of the week, while the time includes seconds and the time zone, providing a detailed and localized representation.

    Introducing the Temporal proposal

    JavaScript has long relied on the Date object for handling dates and times, but developers often encounter its limitations and quirks. To address these issues, the JavaScript community has introduced the Temporal proposal—a modern, robust API that aims to improve and eventually replace the current Date object for more precise and intuitive date and time handling.

    What is the Temporal proposal?

    The Temporal proposal is a new API designed to offer a more comprehensive and reliable way to work with dates, times, time zones, and calendars in JavaScript. Unlike the existing Date object, which can be challenging to use and prone to errors, Temporal is built to be accurate, consistent, and straightforward.

    Here’s what makes Temporal exciting:

    • Precision: Temporal handles dates and times with sub-millisecond accuracy, making it suitable for applications that require high precision.
    • Time Zones and Calendars: It includes native support for time zones and non-Gregorian calendars, which simplifies localization and internationalization efforts.
    • Immutable Objects: Temporal objects are immutable, meaning they can’t be changed once created. This immutability makes Temporal safer to use in complex applications, especially those involving concurrency.
    • Built-in Arithmetic: Temporal offers powerful methods for date-time arithmetic, making it easy to add or subtract dates, times, or durations without needing external libraries.

    Key components of Temporal

    The Temporal API introduces several new objects, each designed to handle different aspects of date and time:

    • Temporal.PlainDate: Represents a calendar date without a time or time zone (e.g., 2024-09-04).
    • Temporal.PlainTime: Represents a time of day without a date or time zone (e.g., 14:30:00).
    • Temporal.PlainDateTime: Combines a date and time, but without a time zone (e.g., 2024-09-04T14:30:00).
    • Temporal.ZonedDateTime: Represents a precise date and time in a specific time zone (e.g., 2024-09-04T14:30:00-07:00[America/Los_Angeles]).
    • Temporal.Duration: Represents a length of time (e.g., 3 hours, 45 minutes).
    • Temporal.Instant: Represents a single point in time, in UTC, without regard to calendar or time zone (e.g., 2024-09-04T21:30:00Z).

    A brief example with Temporal

    Let’s look at a simple example using Temporal.PlainDateTime to create and manipulate a date-time object:

    // Importing Temporal (in environments where it's available)
    const { Temporal } = require('@js-temporal/polyfill');
    
    // Creating a Temporal.PlainDateTime object
    const dateTime = Temporal.PlainDateTime.from('2024-09-04T14:30');
    
    // Adding 5 days to the date-time
    const newDateTime = dateTime.add({ days: 5 });
    console.log(newDateTime.toString());  // Output: "2024-09-09T14:30:00"
    
    // Subtracting 2 hours from the date-time
    const earlierTime = dateTime.subtract({ hours: 2 });
    console.log(earlierTime.toString());  // Output: "2024-09-04T12:30:00"

    This example shows how easy it is to perform date-time arithmetic with Temporal, making it much more intuitive than working with the legacy Date object.

    Date and time localization in PHP

    PHP provides several built-in functions for handling dates and times, making it straightforward to format them according to different locales.

    Date() function for date localization

    The date() function in PHP is a simple yet powerful tool for formatting dates and times. It takes two parameters: a format string and an optional POSIX timestamp. The format string defines how the date and time should be displayed, using a pattern of special formatting codes. If no timestamp is provided, date() uses the current date and time.

    Displaying the current date

    Here’s how you can display the current day, month, and year in a typical DD/MM/YYYY format:

    <?php
    $today_date = date("d/m/Y");
    
    echo $today_date;

    Output: 24/10/2023.

    In this example, d represents the day of the month (with leading zeros), m represents the month, and Y represents the full numeric year.

    Formatting the current time

    You can also format the time along with the date. Here’s an example that displays the date and time in a more detailed format:

    <?php
    echo date("F d, Y h:i:s A");

    The output is: October 24, 2023 04:27:08 PM.

    In this example:

    • A represents AM/PM.
    • F represents the full month name.
    • d is the day of the month (with leading zeros).
    • Y is the four-digit year.
    • h is the hour in 12-hour format.
    • i is the minutes with leading zeros.
    • s is the seconds with leading zeros.

    Working with the DateTime class in PHP

    The DateTime class in PHP is a powerful and flexible tool for managing dates and times. Unlike the date() function, which is primarily used for formatting, the DateTime class provides a comprehensive set of methods for creating, manipulating, and formatting date and time objects.

    Creating DateTime objects

    To start working with DateTime, you can easily create a new instance representing the current date and time, or parse a specific date string:

    <?php
    // Creating a DateTime object for the current date and time
    $currentDateTime = new DateTime();
    echo $currentDateTime->format('Y-m-d H:i:s');
    
    // Creating a DateTime object from a specific date string
    $specifiedDate = new DateTime('2023-10-24 14:30:00');
    echo $specifiedDate->format('Y-m-d H:i:s');

    Output:

    2023-10-24 17:35:48  // Example output for the current date and time
    2023-10-24 14:30:00  // Output for the specified date

    In this example, the format() method is used to display the date and time in the YYYY-MM-DD HH:MM:SS format.

    Modifying dates with DateTime

    One of the strengths of the DateTime class is its ability to modify dates and times easily. You can add or subtract time intervals using methods like modify(), add(), and sub():

    <?php
    $date = new DateTime('2023-10-24 14:30:00');
    
    // Adding 5 days to the date
    $date->modify('+5 days');
    echo $date->format('Y-m-d H:i:s');  // Output: 2023-10-29 14:30:00
    
    // Subtracting 2 hours from the date
    $date->modify('-2 hours');
    echo $date->format('Y-m-d H:i:s');  // Output: 2023-10-29 12:30:00
    
    // Using the add() and sub() methods
    $interval = new DateInterval('P1M');  // Adding 1 month
    $date->add($interval);
    echo $date->format('Y-m-d H:i:s');  // Output: 2023-11-29 12:30:00
    
    $date->sub(new DateInterval('P10D'));  // Subtracting 10 days
    echo $date->format('Y-m-d H:i:s');  // Output: 2023-11-19 12:30:00

    In this example:

    • modify('+5 days'): Adds 5 days to the date.
    • modify('-2 hours'): Subtracts 2 hours from the date.
    • add(new DateInterval('P1M')): Adds 1 month using a DateInterval object.
    • sub(new DateInterval('P10D')): Subtracts 10 days.

    Formatting dates with DateTime

    Just like with the date() function, the DateTime class can format dates and times. However, it offers greater flexibility and is better suited for more complex formatting needs:

    <?php
    $date = new DateTime('2023-10-24 14:30:00');
    
    // Formatting with a custom format
    echo $date->format('l, F j, Y g:i A');  // Output: Tuesday, October 24, 2023 2:30 PM

    In this example, the format() method is used to produce a more human-readable string that includes the day of the week, full month name, and AM/PM notation.

    Handling time zones with DateTime

    The DateTime class also makes it easy to work with different time zones. You can set the time zone when creating the DateTime object or change it later using the setTimezone() method:

    <?php
    // Creating a DateTime object in a specific time zone
    $date = new DateTime('now', new DateTimeZone('America/New_York'));
    echo $date->format('Y-m-d H:i:s T');  // Output: 2023-10-24 11:30:00 EDT
    
    // Changing the time zone
    $date->setTimezone(new DateTimeZone('Europe/London'));
    echo $date->format('Y-m-d H:i:s T');  // Output: 2023-10-24 16:30:00 BST

    In this example:

    • new DateTimeZone('America/New_York'): Sets the initial time zone to New York.
    • setTimezone(new DateTimeZone('Europe/London')): Changes the time zone to London.

    The time is automatically adjusted to reflect the time zone change.

    Converting DateTime to different formats

    In addition to formatting, you might need to convert DateTime objects into different formats, such as Unix timestamps or ISO 8601 strings:

    <?php
    $date = new DateTime('2023-10-24 14:30:00');
    
    // Converting to Unix timestamp
    $timestamp = $date->getTimestamp();
    echo $timestamp;  // Output: 1698156600
    
    // Converting to ISO 8601 format
    $iso8601 = $date->format(DateTime::ATOM);
    echo $iso8601;  // Output: 2023-10-24T14:30:00+00:00

    These conversions are useful when interacting with other systems or APIs that require dates in specific formats.

    Localizing date and time in PHP with IntlDateFormatter

    While the date() function is great for basic formatting, PHP offers more advanced tools for date and time localization when working with different languages and regions. The IntlDateFormatter class from the intl extension is particularly useful for this purpose.

    Here’s how you can use IntlDateFormatter to format dates according to a specific locale:

    <?php
    $formatter = new IntlDateFormatter(
        'fr_FR', // Locale (French - France)
        IntlDateFormatter::FULL, // Date format
        IntlDateFormatter::FULL, // Time format
        'Europe/Paris', // Time zone
        IntlDateFormatter::GREGORIAN // Calendar type
    );
    
    echo $formatter->format(new DateTime());

    Output: mardi 24 octobre 2023 à 16:27:08 heure d’été d’Europe centrale.

    In this example, we use the IntlDateFormatter class to format the date and time in French, including the time zone and using the Gregorian calendar. The FULL format provides a comprehensive representation, including the day of the week and the time.

    Handling different time zones

    PHP also makes it easy to handle time zones. You can set the time zone globally using the date_default_timezone_set() function or specify it on a per-operation basis with DateTimeZone objects.

    Here’s an example of setting the time zone and displaying the current date and time:

    <?php
    date_default_timezone_set('America/New_York');
    
    echo date("F d, Y h:i:s A");

    Output: October 24, 2023 11:27:08 AM.

    This sets the time zone to New York and displays the current date and time accordingly.

    Date and time localization in Ruby on Rails

    Ruby on Rails provides powerful tools for date and time localization, making it easy to display dates and times according to the user’s locale.

    Using the localize (l) method

    One of the key methods for this is the localize method, often aliased as l. The l method is similar to Ruby’s strftime, but it’s tailored for localization. It allows you to format dates and times based on the locale, using either predefined formats or custom ones.

    Using predefined formats

    Here’s how you can use the :long format to display a timestamp:

    l post.created_at, format: :long

    The :long format typically displays the date and time in the following structure: “Full month name – day – year – hours – minutes.” However, the exact output will vary depending on the locale set for the application.

    Here’s the sample output: January 23, 2021 23:05.

    Creating custom formats

    You can also define your own format:

    l post.created_at, format: "%B %Y"

    In this case, the format string uses placeholders similar to those in strftime, where %B is the full month name and %Y is the four-digit year. This would output something like: January 2021.

    Defining custom formats in translation files

    For even more flexibility, you can define custom formats in your translation files. This allows you to reuse these formats across your application and ensures consistency.

    To add a custom format, open a translation file in the config/locales folder (e.g., en.yml) and add your custom format under the time key:

    en:
      time:
        formats:
          very_short: "%H:%M"

    With this setup, you can now use the very_short format:

    l post.created_at, format: :very_short

    This would output just the hours and minutes, like this: 23:05.

    It’s important to define this custom format for all other locales your application supports, ensuring consistent behavior across different languages.

    Adding the rails-i18n gem

    To fully support localization, add the rails-i18n gem to your Gemfile:

    gem 'rails-i18n'

    This gem provides translations for common formats in various languages, making it easier to localize your app without having to manually define every aspect.

    Managing locales in Ruby on Rails

    Effective locale management is crucial for ensuring your Ruby on Rails application properly supports multiple languages and regions. Rails makes it straightforward to manage and switch between locales, allowing you to tailor content, dates, and times to your users’ preferences.

    Setting the default locale

    Rails uses :en (English) as the default locale, but you can change this to any other locale by modifying the application.rb file:

    # config/application.rb
    module YourApp
      class Application < Rails::Application
        config.i18n.default_locale = :fr  # Set default locale to French
      end
    end

    This sets French as the default language for your application, affecting all localized content unless otherwise specified.

    Switching locales dynamically

    To provide users with the ability to switch between languages, you can dynamically change the locale based on user input, such as a selection from a dropdown menu. Typically, you’d do this in a controller:

    class ApplicationController < ActionController::Base
      before_action :set_locale
    
      private
    
      def set_locale
        I18n.locale = params[:locale] || I18n.default_locale
      end
    end

    This code sets the locale based on a locale parameter passed in the URL (e.g., ?locale=fr), falling back to the default locale if none is provided.

    Passing the locale in URLs

    To maintain the selected locale across your application, you can include it in your URL structure:

    # config/routes.rb
    scope "(:locale)", locale: /[a-z]{2}/ do
      resources :posts
    end

    This setup allows URLs like /fr/posts and /en/posts, automatically setting the locale based on the URL segment.

    Storing user preferences

    For a more seamless experience, you might want to store the user’s preferred locale in their profile (if they’re logged in) or in a session:

    def set_locale
      I18n.locale = current_user.try(:locale) || session[:locale] || I18n.default_locale
    end

    This approach remembers the user’s language preference across sessions, improving usability.

    Preserving locale with default_url_options

    In Ruby on Rails, you can use default_url_options to automatically include the locale in your URLs, ensuring that the correct locale is maintained as users navigate through your application.

    By defining default_url_options in your ApplicationController, you can ensure that the locale is always part of the generated URLs:

    class ApplicationController < ActionController::Base
      before_action :set_locale
    
      def default_url_options
        { locale: I18n.locale }
      end
    
      private
    
      def set_locale
        I18n.locale = params[:locale] || I18n.default_locale
      end
    end

    With this setup, the locale parameter (?locale=fr, for example) is automatically added to all generated URLs, helping to keep the user in their preferred language as they navigate through your site. This is particularly useful for maintaining a consistent user experience across different pages and actions.

    For example, a link generated by link_to 'Posts', posts_path would automatically include the current locale, resulting in a URL like /fr/posts if the French locale is active.

    Managing time zones in Ruby on Rails

    Handling time zones correctly is important for applications that serve users across different regions. Ruby on Rails provides built-in support for managing time zones, making it easier to ensure that dates and times are displayed accurately according to the user’s location.

    Setting the application’s time zone

    You can set the default time zone for your entire Rails application in the application.rb file:

    # config/application.rb
    module YourApp
      class Application < Rails::Application
        config.time_zone = 'Eastern Time (US & Canada)'
      end
    end

    This configuration ensures that all date and time operations in your application are performed in the specified time zone, unless otherwise overridden.

    Using Time.zone for time zone-aware operations

    Rails also provides the Time.zone object, which allows you to work with time zone-aware dates and times. This is particularly useful when you need to perform operations or comparisons that are sensitive to time zones:

    # Get the current time in the application's time zone
    current_time = Time.zone.now
    
    # Convert a specific time to the application's time zone
    time_in_zone = some_time.in_time_zone('Pacific Time (US & Canada)')

    Using Time.zone ensures that your application handles time correctly, even when working with users in different time zones.

    Storing time in UTC

    By default, Rails stores all timestamps in the database as UTC. This is a best practice because it standardizes time storage, avoiding potential issues with daylight saving time changes or users in different time zones.

    When you retrieve a timestamp from the database, Rails automatically converts it to the application’s time zone (or the user’s time zone, if specified), ensuring consistent display and calculation of times.

    Expanding localization

    Beyond date and time, Rails offers extensive support for internationalization (i18n) and localization (l10n), including the ability to add a locale switcher to your application. This allows users to select their preferred language and date formats, enhancing the user experience across different regions.

    For a more detailed guide on setting up i18n and l10n in Rails, including how to implement a locale switcher, you can check out our Rails internationalization tutorial.

    Summary

    In this article, we’ve explored how to handle date and time localization across several popular programming languages, including Python, PHP, Java, JavaScript, and Ruby on Rails. We’ve covered essential tools and methods like DateTime, Intl.DateTimeFormat, ZonedDateTime, and Rails’ localize method, among others, to ensure your applications can accurately display dates and times according to the user’s locale and time zone.

    If you’re eager to dive deeper into internationalization and localization processes across various technologies, don’t miss our collection of dev-oriented tutorials. They provide step-by-step guidance on implementing these concepts in many popular languages and frameworks.

    Thank you for joining me on this journey through date and time localization. Happy coding, and may your applications be as global as your ambitions!

    Related articles
    Stop wasting time with manual localization tasks. 

    Launch global products days from now.