Building a Time Zone Enabled Date Picker

Tom Chen
Sight Machine
Published in
5 min readOct 2, 2018

TLDR:

Date and time handling is often filled with edge cases and has always been a technical challenge in software development. From time zone to localization to server/client time latency, numerous problems exist in representing time. Even deciding how to represent “today” can have many interpretations.

Here at Sight Machine, as a global data company, we work with manufacturers from all over the world and presenting accurate data in a user-friendly manner is important to us. In our date picker, we allow users to select a variety of options such as start date, end date, time zone etc. Here is the date picker implemented in React, utilizing moment.js and some key lessons learned from building it.

Keeping State

In its essence, the date picker is a collection of input components that select a date range. As highlighted below, there are roughly 8 different components that can update the date range and also each other. For example, changing the time zone should update the last selectable date on the calendar.

Keeping the state central here among the components is crucial to reduce complexity and bugs of components being out of sync. In our picker we keep the following states:

All components in the date picker should derive display values based on these states and at the same time, each component’s handler should update these states. Here’s an abridged example of selecting a time zone:

We can see that the handler updates the startDate, endDate and internalTimeZone. This guarantees that the other components will update correctly to this newTimeZone, as long as those components display correctly based on these states.

A benefit of keeping the states simple is that we can have clear inputs and outputs from external connections with this date picker. Here’s what we pass out of the date picker:

The date picker can handle repopulating the state based on these output key/values, therefore keeping the picker consistent.

Handling Time Zone

Handling time zone can be tricky especially because time zone rules are not consistent and time can be represented in many ways. A common way is through Coordinated Universal Time (UTC) and offset. This format divides the world into different zones in which each one is plus or minus certain time off UTC. For example, right now in San Francisco the offset is UTC-7 hours. Asking the user to select UTC offset can be problematic because of daylight saving. Say I want data between March 1 and April 30 in 2018 from a factory in San Francisco and I select UTC-7, the data would be represented incorrectly because from March 1 to March 11, the offset in San Francisco is UTC-8. Also, note that the UTC offset isn’t always by hours. There are also places that observe time in UTC+9:30 or UTC+12:45 for instances.

Another common representation of timezone is an abbreviation. For example, San Francisco can have either PST (Pacific Standard Time) or PDT (Pacific Daylight Time) time zones. Selecting an abbreviation will cause the same daylight saving issue as UTC offset. On top of that, the same abbreviation can represent multiple zones. For example, CST can mean any of the following: Central Standard Time, China Standard Time, Cuba Standard Time, making it even more confusing.

To solve these problems we turned to IANA time zone database. The tz database is a collection of time zones by locations with daylight saving information included. To represent San Francisco we would use America/Los_Angeles in the database which contains both -7 and -8 offsets. IANA is the standard among numerous software systems such macOS, Android and GNU. In our date picker we use moment-timezone to convert UTC time into the selected tz.

Another benefit of using IANA is our Python backend can easily use pytz to handle the format.

Using IANA databases and libraries that accept them also means you would not need to update these timezone manually yourself when areas decide to change the timezone they observe. The databases get updated and thus reduce a lot of future headache for developers.

In our application, we store and pass any date state in UTC and only convert to the selected time zone when displaying. This improves consistency and avoids unnecessary conversions between multiple time zone.

Displaying date in UTC offset and abbreviations is perfectly fine, but internally using the IANA database format will make your life a lot easier.

Live / Relative Time Selection

Relative time range can be useful for users to select a common period of time. For example, a user may want data for the last day, last week or last month. While these are common phrases we use, translating the phrase to a date range can be arbitrary. For example, it is currently 3pm Friday and we would like data for the “last 7 days”. Does that mean we should query data from last Friday at 3 pm to now? How about “last 2 days”? Is it 120 hours?

To simplify, in our date picker we decided to use the following formula to calculate the start of the date range:

now.startOf(relativeUnit).subtract(relativeAmount - 1, relativeUnit)

To handle the last 2 days, the formula would be:

now.startOf(day).subtract((2 - 1), day)

In this case, the “last 2 days” would be time up to now + yesterday and thus could be different than the last 48 hours. This formula may not always be what the user would initially expect, but keeping the standard can reduce further confusions, while still providing flexibility for the user to query a different time range.

One last note on live data is to make sure to store the user preference of relative time (e.g. relativeAmount: 2, relativeUnit: ‘day’) and only translate to absolute time range when querying the data. This way the data being queried is as close to live as possible.

Conclusion

Handling date, time and timezone in software can often cause many bugs. By keeping state central, utilizing libraries and using common standards can truly reduce these errors.

Interested in working with some smart people and solving technical challenges like the one above? You’re in luck, because we’re hiring!

--

--