Timezone issue counter
Development · Mar 19, 2020 · 5 minutes

A Simple Guide for Testing DateTime

Stephen Fairley, Mudbath Senior Developer
Stephen Fairley Senior Developer

As a developer, it is inevitable that at some point you will have to work on a product that involves datetimes and timezones. It is often considered one of the most frustrating elements of programming - and can be punishing if you aren’t familiar with the common pitfalls that many of us fall into.

There are many different scenarios that might change how you approach working with timezones, but generally speaking a good developer will store the datetime in UTC, and then convert between timezones based on the user’s browser or a selected timezone. 

Since you are a good developer, you have decided that you want to create some unit tests for each of your key functions. Watch out! There are several traps that you can fall into that will cause your DevOps to fail your tests, or co-workers in other timezones to complain that they fail locally. 

How can you write watertight tests for your timezone functions? Here are some top tips for your timezone unit tests! 

For the purposes of this guide, I will be writing my tests in .NET C#, but the principles apply whether you use Javascript, Java or any other language.

 

  1. Make your test timezone agnostic

Do not create datetimes relative to the machine’s timezone, but expect datetimes with a specified timezone. When writing your tests, ensure that they can succeed in any timezone. This basically boils down to not creating datetimes with a hard-coded timezone. When creating the expected outcomes and the parameters, use datetimes that are in the timezone of the machine that is running the test.

 

  1. Be thoughtful about whether to use absolute or relative datetimes

In some instances, such as a “number of days” function, when you run the test could change the output of the function. February 29th is a good example of how, without careful thought, your test could start producing a value that is out by 1, depending on the year. Alternatively, if you are trying to test a mapping class or a timezone conversion, using a datetime relative to now will reduce the risk that your test will be sensitive to where and when your test is run.

 [TestMethod]

        //This test will succeed in 2019

        public void Test1()

        {

            var dtm = new DateTime(2019, 02, 10);

            var dtm2 = new DateTime(2019, 03, 10);

            var days = dtm.NumberOfDaysBetween(dtm2);

            Assert.AreEqual(28, days);

        }

        [TestMethod]

        //But it will fail in 2020

        public void Test2()

        {

            var dtm = new DateTime(2020, 02, 10);

            var dtm2 = new DateTime(2020, 03, 10);

            var days = dtm.NumberOfDaysBetween(dtm2);

            Assert.AreEqual(28, days);

        }

 

  1. Consider Daylight Savings Time

Make sure when you are writing the tests that you account for scenarios where you are converting across a daylight saving time period. For example, choose a timezone where you know that the changeover occurs on the 7th October, and then ensure your tests include parameters between the 6th and the 8th. This will reduce the likelihood that your tests succeed but you wake up one day with your application running everything 1 hour out of sync.

private DateTime CreateInTimezone(DateTime dtm, TimeZoneInfo tz)

        {

            dtm = dtm.ToUniversalTime();

            var unspecDtm = DateTime.SpecifyKind(dtm, DateTimeKind.Unspecified);

            return TimeZoneInfo.ConvertTimeBySystemTimeZoneId(unspecDtm, tz.Id);

        }

--------------------------------------

var dtm = CreateInTimezone(new DateTime(2020, 10, 02), TimeZoneInfo.FindSystemTimeZoneById("E. Australia Standard Time"));

            var dtm2 = CreateInTimezone(new DateTime(2020, 10, 04), TimeZoneInfo.FindSystemTimeZoneById("E. Australia Standard Time"));

 

            //this test will NOT consider the daylight savings changeover

            var days = dtm.NumberOfDaysBetween(dtm2);

 

------------------------------------

var dtm = CreateInTimezone(new DateTime(2020, 10, 02), TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time"));

            var dtm2 = CreateInTimezone(new DateTime(2020, 10, 04), TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time"));

 

            //this test will consider the daylight savings changeover

            var days = dtm.NumberOfDaysBetween(dtm2);

 

  1. Ensure you SpecifyKind correctly

If you want to create a test to verify that you are converting between timezones A and B correctly (for example, timezone X into UTC and back again), specifying a source and target timezone is necessary. However, it is important that you set the parameter and the expected date times to DateTimeKind.Unspecified before attempting to convert them, and then specify DateTimeKind.Local/UTC after the conversion. If you don’t do this, you will find your application will throw exceptions. This is true of tests as well as in your methods. 

 

var dtm = DateTime.Now; //Is DateTimeKind.Local

var dtm2 = DateTime.UtcNow.AddDays(1); //Is DateTimeKind.Utc

var unspecDtm = DateTime.SpecifyKind(dtm2, DateTimeKind.Unspecified);

 

//unspec timezone allows you to target a source and a destination timezone

var newTimezone = TimeZoneInfo.ConvertTime(unspecDtm, TimeZoneInfo.Utc, TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time"));

 

  1. Use IANA timezone codes 

If your application is designed to be run on different platforms, such as in a kubernetes cluster running linux, be aware that storing and using windows uses different timezone IDs to other systems. To ensure this doesn’t happen - I recommend using the IANA timezone codes, as they are universal and can therefore be run across any platform. There are libraries such as TimeZoneConverter which can handle the mapping between the platform timezones and the ones that you are storing.

 

***

Hopefully these tips will help you master the art of testing DateTime in your development pursuits!

Read more articles from our Development team.

***

Mudbath is a 40+ person digital product agency based in Newcastle, NSW. We research, design and develop products for industry leaders.