Format dates including offset

Asked

Viewed 87 times

1

I’m not able to format in this pattern: 2020-10-01T11:34:00.270-03:00.

SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
System.out.println(inputFormat.format(new Date()));

1 answer

1

That value -03:00 is the offset (the difference with respect to UTC - then in this case it would be "3 hours unless UTC"), and according to the documentation, just use the Pattern X to display it. In case, to have hours and minutes separated by two-points, just use 3 letters X:

SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
System.out.println(inputFormat.format(new Date())); // 2020-10-06T17:02:42.755-03:00

Remembering that the value of offset depends on Timezone default which is configured in the JVM. In my case, the default (which may be consulted with TimeZone.getDefault()) is America/Sao_Paulo, which corresponds to the Time of Brasilia.

But if the Timezone default is another, will be used the offset correspondent. Ex:

// setando o timezone default para usar o fuso horário da Alemanha
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
System.out.println(inputFormat.format(new Date())); // 2020-10-06T22:04:44.057+02:00

Changing the Timezone default for the German zone, SimpleDateFormat uses the offset of this spindle. In this case, it is +02:00 (for currently Europe is in daylight time, usually Germany uses +01:00).

If you want the SimpleDateFormat always use a specific Timezone, regardless of what is configured in the JVM, just set in it:

SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
// usar o Horário de Brasília, independente do timezone default da JVM
inputFormat.setTimeZone(TimeZone.getTimeZone("America/Sao_Paulo"));
System.out.println(inputFormat.format(new Date())); // 2020-10-06T17:07:06.891-03:00

It may not seem like it, but that’s an important detail, because Timezone default is something that may not be in your control (for example, those responsible for the server can change this setting - unintentionally or on purpose - without telling you, or another application in the same JVM can call TimeZone.setDefault(), etc). If you depend on default, suddenly the system may start showing wrong dates "out of nowhere", but if you set Timezone on SimpleDateFormat, these configuration changes will not affect your code.

These timezone names in the format Continente/Regiao (as America/Sao_Paulo and Europe/Berlin) are defined by IANA, and it is possible to consult all available using TimeZone.getAvailableIDs().


API java.time

If you are using Java >= 8, another option is to use the API java.time (more modern and solving several problems and failures of design of Date and SimpleDateFormat).

In this case, an option is to use OffsetDateTime, which is a class that has the date, time and offset (that is, all the fields you need):

OffsetDateTime now = OffsetDateTime.now();
System.out.println(now.toString());

By default, the method toString() already returns the date in the format you need (this format is known as ISO 8601).

It is worth remembering that the method now uses the offset based on Timezone default of the JVM (i.e., if Timezone default be that of Germany, he would wear +02:00, as in the previous example). If you want it to use a specific Timezone, regardless of what is configured in the JVM, just pass a ZoneId:

// usa o offset do Horário de Brasília, independente do timezone default configurado na JVM
OffsetDateTime now = OffsetDateTime.now(ZoneId.of("America/Sao_Paulo"));
System.out.println(now.toString());

To view all available timezones, use ZoneId.getAvailableZoneIds().


One detail is that toString can omit some fields if they are zero:

OffsetDateTime now = OffsetDateTime.of(2020, 10, 1, 9, 0, 0, 0, ZoneOffset.ofHours(-3));
System.out.println(now.toString()); // 2020-10-01T09:00-03:00

Notice that the seconds and fractions of a second do not appear, because toString omit these fields when their value is zero. If you want at all times show these fields, the way is to use a DateTimeFormatter:

OffsetDateTime now = OffsetDateTime.of(2020, 10, 1, 9, 0, 0, 0, ZoneOffset.ofHours(-3));
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSXXX");
System.out.println(now.format(formatter)); // 2020-10-01T09:00:00.000-03:00

Java < 8

If you are using JDK 6 or 7 and want a better alternative than Date and SimpleDateFormat, can use the Threeten Backport, an excellent backport of java.time. The code is identical to the above example (the same classes and methods), the only difference is that the classes are in the package org.threeten.bp instead of java.time (this even facilitates a future migration to Java 8, as it would be enough to exchange the import's - plus some other details, explained at the end of this answer).


And for JDK 5, an alternative is Joda-Time. In the case of ISO 8601 format, a ready-made method already exists:

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

// data/hora atual em um timezone específico (para não depender do timezone default da JVM)
DateTime now = DateTime.now(DateTimeZone.forID("America/Sao_Paulo"));
DateTimeFormatter formatter = ISODateTimeFormat.dateTime();
System.out.println(formatter.print(now)); // 2020-10-07T09:31:52.822-03:00

It is worth remembering that Joda-Time is an old project and closed, so I suggest you read the considerations of this question before deciding to use it.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.