Breaking Junit: Timezones with Daylight Savings Time
It is quite well known to make your unit tests resilient to environmental variables to avoid weird or hard to reproduce failures. Environment variables such as system variables, timezones, data from sensors etc etc.
Here is a similar case which I had found and fixed recently.
The Symptoms
Unit test started to fail suddenly - no code changes done in recent times.
Tests didn’t fail on build servers - only failing in local runs.
Tests failed on time assertions. Meaning, tests were able to compile, initialize and run.
Diagnosis
At the first glance, the difference between the local runs and the build servers was the Operating System. Build servers were Linux based, while the developers used Macs.
A difference in the OS shouldn’t cause errors on time assertions.
On a detailed look, I realized the build servers were running in UTC, while local setups were running, with local timezones (PST).
To validate the theory, I changed the timezone of my mac to UTC, and re-run the tests. Voila. Went smooth.
So, we have a case where unit test is dependent on the system timezone.
What did the test do
Take currentTime
Randomly take a duration range : e.g., 170 days - mocked as external input
Add it to currentTime to get an endTime
Assert that the endTime matches with the duration range that we got from external source.
Pretty simple. So, why this ‘addition’ in step 3 resulted in a wrong value in PST?
I found that test cases used DateUtils
from JakartaCommons
package. The add
method of DateUtils
uses Calendar
fields to add days, without taking timezone into consideration. Normally this works, but it fails when there is a Daylight Saving Time.
Which they also mention in the JavaDocs, which was missed whenever that code was added:
It is important to note these methods use a
Calendar
internally (with default time zone and locale) and may be affected by changes to daylight saving time (DST).
For example, assuming daylight saving time changes on 01/01/2023 (mm/dd//yyyy), advancing the clock by 1 hour
We would expect this behavior :
startDate = 01/01/2023 00:00:00
endDate = startDate + 1 Day = 01/02/2023 01:00:00
Days added: 1 + delta (delta = 1 hour)
Actual hours added: 24
That's because, normally, the expectation is we add 86400 seconds to the starting timestamp. So, with Daylight saving time, even when we add 24 hours, the clock is advanced by 25 hours.
However, Jakarta commons library's behavior is different. Instead of adding timestamps, it adds 1 Day
to Calendar
field. That means, for the same example above, it does this:
startDate = 01/01/2023 00:00:00
endDate = startDate + 1 Day = 01/02/2023 00:00:00
Days added : 1
Actual hours added: 23
With that in the test case, it became timeZone
dependent and it failed in PST for a certain date range. If no fix is being done, it will start working again on its own, whenever the date range moves past the daylight switching date.
The fix
The fix was simple. Just don’t depend on the DateUtils library for this trivial task!