Software localization

How to Get the Current UTC Date and Time in Java?

Learn all the tips and tricks to get the current date and time in UTC or GMT for your multilingual Java application.
Software localization blog category featured image | Phrase

One of the key questions that many developers have while working on software for international markets is how best to deal with time and time zones. To be able to get the current date and time in UTC or GMT in Java, for example, there are a few concepts we need to make ourselves familiar with upfront.

First things first, GMT (Greenwich Mean Time) is not the same as UTC (Coordinated Universal Time):

  • GMT is a time zone used in some but not all parts of the world (mainly Europe and Africa). It uses either a 24-hour format or a 12-hour format for display, and it's based on astronomical observations.
  • UTC isn't a time zone. It is a standard that we can use to display time zones. It is more stable as it takes time from an atomic clock.

If you are using the java.util.date package to get the current date and time in Java (as it looks more intuitive), you'll soon find out it's very limited. There are several reasons for that:

  • It doesn’t have a time zone.
  • It doesn't represent a date but an instance in time in milliseconds since the Unix epoch (1970).

Therefore, if you want to get swiftly the current time in milliseconds only, you can use the following code:

package com.thdespou;

import java.util.Date;

public class Main {

    public static void main(String[] args) {

        // Date

        Date now = new Date();

        System.out.println("Current Date in milliseconds is :" + now.getTime());

    }

}

Other than that, you can also change the current system, TimeZone, and if you wanted to use a different time zone, you could do it as follows:

package com.thdespou;

import java.util.Date;

import java.util.TimeZone;

public class Main {

    public static void main(String[] args) {

        // Date

        Date now = new Date();

        System.out.println("Current Date in milliseconds is :" + now.getTime());

        // Display the instant in three different time zones

        TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));

        System.out.println(now);

        TimeZone.setDefault( TimeZone.getTimeZone("GMT"));

        System.out.println(now);

        TimeZone.setDefault( TimeZone.getTimeZone("UTC"));

        System.out.println(now);

    }

}

However, if you run this code, you'll see the following output:

Current Date in milliseconds is :1583954404789

Wed Mar 11 19:20:04 GMT 2020

Wed Mar 11 19:20:04 GMT 2020

Wed Mar 11 19:20:04 UTC 2020

In general, this isn't ideal as it displays the current time based on the time zone of the specified region, which may be different than GMT; therefore, you should ideally avoid it.

A better and more modern option bundled within the core Java library (and not using any third-party offerings) is to use the java.time package.

If you look at the documentation page, the package is based on the ISO calendar system and offers a simplified API for displaying and handling date and time instances. Below is an overview of the capabilities exposed in this package:

Time table java.time package | Phrase

Based on this, we can infer that this package represents the time (with the location unspecified), as UTC, or as an offset from UTC.

Indeed, if we check the Instant class to get the current time, we can see the date time printed as UTC:

package com.thdespou;

import java.time.Instant;

public class Main {

    public static void main(String[] args) {

        Instant now = Instant.now();

        System.out.println("Date is " + now);

    }

}

This will display:

Date is 2020-03-11T20:39:20.480918Z

Which is the current date-time in UTC. The ending Z character denotes the Zone Offset which is Zero.

A more robust option is to convert this Instance to a ZonedDateTime as you'd like to configure the current TimeZone. You can do that by using the ofInstant static factory method passing a ZoneId:

package com.thdespou;

import java.time.*;

public class Main {

    public static void main(String[] args) {

        Instant now = Instant.now();

        ZonedDateTime zdt = ZonedDateTime.ofInstant(now,

                ZoneId.systemDefault());

        System.out.println( "Date is: " + zdt );

    }

}

In most cases, you should be using the UTC format as it's best supported in Java.

How do I set the default locale for my JVM?

Locales identify a specific language and geographic region; they're represented as strings in the following format:

[language[_territory][.codeset][@modifier]]

Here's an example of some locale strings:

eo_KE

no_NO

ca_AD

zh_MO_#Hant

en_SH

If you run a UNIX platform (Mac, Linux), there's a convention to set some environmental variables using the LC_* prefix. For example, by running this command in the console, we can see en_IE as the default system locale:

➜ locale

LANG="en_IE.UTF-8"

LC_COLLATE="en_IE.UTF-8"

LC_CTYPE="en_IE.UTF-8"

LC_MESSAGES="en_IE.UTF-8"

LC_MONETARY="en_IE.UTF-8"

LC_NUMERIC="en_IE.UTF-8"

LC_TIME="en_IE.UTF-8"

LC_ALL=

In Java, if we call the Locale.getDefault() in a method and run the java command without any flags, it won't pick up the system locale as specified in the LC_* variables. For example, given the following main class, located in com/thdespou/Main.java:

package com.thdespou;

import java.util.Locale;

public class Main {

    public static void main(String[] args) {

        System.out.println(Locale.getDefault());

    }

}
$ javac com/thdespou/Main.java

$ java -classpath $(pwd) com/thdespou/Main

en_IE

Now, if we change the LC_* variables to en_UK, it will still print en_IE.

In fact, looking at the Locale documentation page, there is no information on how to change that. However, by inspecting the source code of the Locale.class and particularly the static initializer block ...

static {

...

defaultLocale = initDefault();

}

... and looking at the initDefault() method, we get a hint of what we exactly need to do in order to set the default locale in the JVM:

private static Locale initDefault() {

    Properties props = GetPropertyAction.privilegedGetProperties();

    String language = props.getProperty("user.language", "en");

    String region = props.getProperty("user.region");

    String script;

    String country;

    String variant;

    if (region != null) {

        int i = region.indexOf(95);

        if (i >= 0) {

            country = region.substring(0, i);

            variant = region.substring(i + 1);

        } else {

            country = region;

            variant = "";

        }

        script = "";

    } else {

        script = props.getProperty("user.script", "");

        country = props.getProperty("user.country", "");

        variant = props.getProperty("user.variant", "");

    }

So using the following system properties:

  • user.language: For setting the current language
  • user.region: For setting the language region

Given that information, we can check in the command line for setting the default locale and using this guide. For example:

$ java -Duser.language=el -Duser.region=CY com.thdespou.Main

el_CY

$ java -Duser.language=zh -Duser.region=hans_chinese com.thdespou.Main

zh_HANS_chinese

Note that this is based on this implementation of the JVM, and we should not be if this would change in the future, as it is not clearly documented in the JavaDocs.