Someone kindly reported a bug entitled “Times Rounding Off” on FoodCandy.
“I just created a new event, scheduled to start at 6:30 PM on December 14th. When it displays, it shows a start time of 6:00 PM. If I edit it, it shows 6:30 PM.”
I tried to reproduce at no avail. After-all, it’s not that complicated to display a time correctly, especially when it’s the same one as the user enters. How could I have possibly introduced a bug in something this simple? Could this be a user error? So to nail this down, I got a recording of what the user sees.
Looks like a MAC. That’s the problem! It’s a conspiracy of Apple against Microsoft. There must be code in Safari that changes 6:30 to 6:00 every time it hits a website written in .NET.
The screenshots are convincing. The problem exists. And the cause is right there on the screen. The browser’ time zone is Adelaide (GMT+09:30). Adelaide, turns out, is the capital of South Australia and has a population of just over a million people. It has a superb climate and cheap housing. It also has a time zone that is nine and a half hours away from UTC. Nine and a half? That’s weird, I’ve never paid attention to half-hour time zones. Turns out there’re others, including Kathmandu, GMT+5:45. Debugging the code it was clear that my entire time-zone system was rounding hours. And now I remember why.
When I started dealing with time zones, I added code that let the user choose which time zone he’s in and defaulted the value to the browser time zone. The latter comes from a browser cookie, x-VisitorTimeZoneOffset. I wrongly assumed that the offset is an int, so it did int.Parse. That was the first mistake because in Adelaide the browser time zone would be +9.5. I wanted this value to become a TimeSpan, but this is not serializable, hence all methods that needed the user’s time zone offset took an int. Adelaide time zone offset would be 9 and all the times, stored in UTC and converted to the user’s time zone were wrong by 30 minutes.
In order to fix this I had to make a few changes.
- Fix TimeZoneInformation.cs to return TimeSpan instead of an int for UTC bias and to parse the browser time zone offset as a float. TimeZoneInformation is, btw, a very helpful class that originated here and is capable of enumerating system time zones and do the time zone math taking care of daylight savings and stuff like that.
- Change all native methods that took an int UTC bias to take a TimeSpan.
- Change all SOAP methods that took an int UTC bias to take a long UTC bias in ticks. This makes it serializable and I can new TimeSpan(ticks) on the server or client side.
- Change the code that adjusts UTC times to Add a TimeSpan and not AddHours a UTC bias.
- Write some unit tests for TimeZoneInformation.
And finally, ask someone in Adelaide to test this for me.