I'm using JodaTime 2.1 and I'm looking for a pattern to unit test code which performs date/time operations to make sure it behaves well for all time zones and independent of DST.
Specifically:
new DateTime()
to get the current time)Test transition of DST, i.e. when you are currently in summer time, select a time value from winter. Test boundary cases, such as a timezone that is UTC+12, with DST, making the local time UTC+13 in summer and even places that are UTC+13 in winter.
First, when consistently working with people in different time zones, it's helpful to keep a clock of that time zone handy. A lot of phones have apps that allow you to show different time zones on your home screen or you can just simply buy a cheap clock, set it up to another time zone, and put it on your desk.
What is the best way to handle multiple time zones in the application ? The user can select the timezone that they are in. The user can be anywhere in the world. When displaying data the time has to be adjusted to the timezone that the user has selected.
You can use a @Rule
for this. Here is the code for the rule:
import org.joda.time.DateTimeZone; import org.junit.rules.TestWatcher; import org.junit.runner.Description; public class UTCRule extends TestWatcher { private DateTimeZone origDefault = DateTimeZone.getDefault(); @Override protected void starting( Description description ) { DateTimeZone.setDefault( DateTimeZone.UTC ); } @Override protected void finished( Description description ) { DateTimeZone.setDefault( origDefault ); } }
You can use the rule like this:
public class SomeTest { @Rule public UTCRule utcRule = new UTCRule(); .... }
This will change the current time zone to UTC before each test in SomeTest
will be executed and it will restore the default time zone after each test.
If you want to check several time zones, use a rule like this one:
import org.joda.time.DateTimeZone; import org.junit.rules.TestWatcher; import org.junit.runner.Description; public class TZRule extends TestWatcher { private DateTimeZone origDefault = DateTimeZone.getDefault(); private DateTimeZone tz; public TZRule( DateTimeZone tz ) { this.tz = tz; } @Override protected void starting( Description description ) { DateTimeZone.setDefault( tz ); } @Override protected void finished( Description description ) { DateTimeZone.setDefault( origDefault ); } }
Put all the affected tests in an abstract base class AbstractTZTest
and extend it:
public class UTCTest extends AbstractTZTest { @Rule public TZRule tzRule = new TZRule( DateTimeZone.UTC ); }
That will execute all tests in AbstractTZTest
with UTC. For each time zone that you want to test, you'll need another class:
public class UTCTest extends AbstractTZTest { @Rule public TZRule tzRule = new TZRule( DateTimeZone.forID( "..." ); }
Since test cases are inherited, that's all - you just need to define the rule.
In a similar way, you can shift the system clock. Use a rule that calls DateTimeUtils.setCurrentMillisProvider(...)
to simulate that the test runs at a certain time and DateTimeUtils.setCurrentMillisSystem()
to restore the defaults.
Note: Your provider will need a way to make the clock tick or all new DateTime
instances will have the same value. I often advance the value by a millisecond each time getMillis()
is called.
Note 2: That only works with joda-time. It doesn't affect new java.util.Date()
.
Note 3: You can't run these tests in parallel anymore. They must run in sequence or one of them will most likely restore the default timezone while another test is running.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With