java.time or Joda-Time
The Date
and SimpleDateFormat
classes have design problems, so consider not using them. The first version of your question was tagged jodatime, and if you are using Joda-Time in your project, that’s already a sizable improvement. Joda-Time gives you all the functionality you need, so your method should probably return an approrpiate type from Joda-Time rather than an old-fashioned Date
.
Joda-Time is also on its way to retirement, though, and its successor is java.time, the modern Java date and time API. So the latter is an option you may consider no matter if you were already using Joda-Time or not. Since your string contains a UTC offset, one option is to return an OffsetDateTime
. To test a method that does this:
assertEquals(OffsetDateTime.of(1980, 3, 26, 0, 0, 0, 0, ZoneOffset.ofHours(2)),
DateUtils.getOffsetDateTimeFromString("1980-03-26T00:00:00.000+0200"));
Your example and your code may give the impression that you are only after the date from the string and don’t care about the time of day nor the offset. If this is correct, your method may return a LocalDate
and be tested in this way:
assertEquals(LocalDate.of(1980, Month.MARCH, 26),
DateUtils.getLocalDateFromString("1980-03-26T00:00:00.000+0200"));
The latter will also free you from all time zone considerations. In both cases please note that I am passing date-time objects to assertEquals
, not strings.
Your unit test is correct: your method is returning the wrong time
The failure you reported in your question was that while the expected Date
from your method was Wed Mar 26 00:00:00 PST 1980
, the actual value was Wed Mar 26 00:00:00 SGT 1980
. It is correct that these differ. Midnight in Baja California, Yukon, Washington, Nevada, California and other places observing Pacific Time is not the same point in time as midnight in China.
Can I use java.time on Android?
Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.
- In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
- In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310; see the links at the bottom).
- On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from
org.threeten.bp
with subpackages.
In case you don’t want your Android code to depend on a third-party library, you can still use java.time in your unit test. To test a method that returns an old-fashioned Date
:
Instant expectedInstant = LocalDate.of(1980, Month.MARCH, 26)
.atStartOfDay(ZoneId.systemDefault())
.toInstant();
Date expectedDate = Date.from(expectedInstant);
Date actualDate = DateUtils.getDateFromString("1980-03-26T00:00:00.000+0200");
assertEquals(expectedDate, actualDate);
If using the backport (ThreeTen Backport and/or ThreeTenABP), the conversion from Instant
ot Date
happens a little differently:
Date expectedDate = DateTimeUtil.toDate(expectedInstant);
Again note that I am comparing Date
objects, not strings.
How to set the timezone for a SimpleDateFormat?
To answer the question in your title: use the setTimeZone
method:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXX");
df.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); // Pacific Time
System.out.println(df.parseObject("1980-03-26T00:00:00.000+0200"));
In this particular case it won’t make any difference, though, because the offset in the string is parsed and takes precedence over the time zone of the formatter.
Is there any way to set the default timezone just for unit testing?
There are a couple of options.
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
Putting this into the beforeClass
or before
method of your unit test will set the time zone of your JVM to Pacific Time. It’s not bullet-proof since other code may set it to something else before the test finishes, but you may be able to control that that doesn’t happen. Normally I would discourage the use of the outdated TimeZone
class. It too has design problems, but is the natural outdated choice if the methods you are testing are using the outdated SimpleDateFormat
. One of the problems is it doesn’t report if the string passed is invalid. To obtain proper validation just go through the modern class:
TimeZone.setDefault(TimeZone.getTimeZone(ZoneId.of("America/Los_Angeles")));
Or using the backport:
TimeZone.setDefault(DateTimeUtils.toTimeZone(ZoneId.of("America/Los_Angeles")));
Links