1

I'm using Android, JUnit and SimpleDateFormat.

I like to do some conversions between String and Date in rfc3339 format with timezone.

Eg: 2017-12-31T23:59:59+02:00 but not 2017-12-31T23:59:59+0200.

Android SimpleDateFormat uses Z pattern but not X pattern (see RFC3339_REGEX_1 below).

But in my JUnit test (jdk1.8), RFC3339_REGEX_1 does not give me the right format (+02:00), but +0200. JUnit test runs well with RFC3339_REGEX_2.

So how can I do something clean which runs well both on Android device and JUnit test?

public static final String RFC3339_REGEX_1 = "yyyy-MM-dd'T'HH:mm:ssZ"; //android
public static final String RFC3339_REGEX_2 = "yyyy-MM-dd'T'HH:mm:ssXXX"; //java
Anh-Tuan Mai
  • 1,129
  • 19
  • 36

1 Answers1

2

The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs. And if I'm not wrong, the X pattern doesn't work in all versions (I know that in JDK 6 it wasn't supported, it was introduced only in JDK 7; not sure how this affects different Android versions).

As an alternative, in Android you can use ThreeTen Backport. To make it work, you'll also need ThreeTenABP (see how to use it here).

If you still need to work with java.util.Date, though, this API can make things easier to you. If you want a format like 2017-12-31T23:59:59+02:00, you can convert your java.util.Date to org.threeten.bp.OffsetDateTime (using the org.threeten.bp.DateTimeUtils class to convert the values, and a org.threeten.bp.ZoneOffset to set the offset to +02:00):

// assuming that java.util.Date has value equivalent to 2017-12-31T23:59:59+02:00
Date date = ...
// convert to OffsetDateTime (use +02:00 offset)
OffsetDateTime odt = DateTimeUtils.toInstant(date).atOffset(ZoneOffset.of("+02:00"));
System.out.println(odt); // 2017-12-31T23:59:59+02:00

The output will be:

2017-12-31T23:59:59+02:00

Note that this is the result of odt.toString() (which is called implicity by System.out.println), and by default it matches the format you want. But you can also enforce a specific format, by using a org.threeten.bp.format.DateTimeFormatter:

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
System.out.println(fmt.format(odt));

The output is the same. The only difference is that the first version (using toString()) will print the fractions of second if they're not zero and will omit the seconds if they are zero, and the second version (with a formatter) will always print in the desired format (in this case, print the seconds but not the fraction of seconds), no matter if the values are zero or not.

I believe that, in your case, you're going to need a formatter as you seem to always need a format with seconds and without fraction of seconds.


To convert OffsetDateTime back to Date, you can use DateTimeUtils as well:

// convert back to java.util.Date
date = DateTimeUtils.toDate(odt.toInstant());

If you don't need to work with java.util.Date, you can use the OffsetDateTime directly:

// create OffsetDateTime with value = 2017-12-31T23:59:59+02:00
OffsetDateTime odt = OffsetDateTime.of(2017, 12, 31, 23, 59, 59, 0, ZoneOffset.ofHours(2));

Also note that I used ZoneOffset.ofHours(2), which is equivalent to ZoneOffset.of("+02:00").

You can also get the current date using OffsetDateTime.now(ZoneOffset.ofHours(2)).


ThreeTen Backport is a backport for new Java 8 date/time classes, and it has many different date and time types (like the OffsetDateTime above) to be used in different situations. Check this tutorial for more information about what type fits best your case - the tutorial covers Java 8, but the ThreeTen Backport has the same classes and methods names, just the package is different: org.threeten.bp instead of java.time.

  • In Joda time we have AbstractDateTime class who provides the common behaviour for datetime classes, I did not find the correspondent in threeten (providing LocalDateTime, ZonedDateTime and OffsetDateTime but no abstract class)? – Anh-Tuan Mai Jul 25 '17 at 09:52
  • @Anh-TuanMai What common behavior do you need? Depending on what you need, you can use the `Temporal` or `TemporalAccessor` interfaces –  Jul 25 '17 at 09:56
  • I like to have something like BaseDateTime.getMillis() in Joda time and a constructor by milliseconds :) – Anh-Tuan Mai Jul 25 '17 at 10:07
  • @Anh-TuanMai I suggest you to ask another question (it seems to be the case) providing more details, specific examples, context, what have you tried, and so on. The new API differs from Jodatime in some aspects and there may be many different ways to do it, depending on your needs, and by asking another question you'll have a more precise answer. –  Jul 25 '17 at 10:39
  • 1
    @Anh-TuanMai But to work with milliseconds, start taking a look at the `Instant` class –  Jul 25 '17 at 10:41
  • 1
    Thanks you, `Instant` class is the resolution. – Anh-Tuan Mai Jul 25 '17 at 11:30