If you’ve been coding on any platform for any length of time, you will have learnt that when manipulating dates it’s almost always best to use UTC. We’re the same here: spotting a rogue DateTime.Now is unusual, and it’s normally reflex to replace that with DateTime.UtcNow.
There is however one unusual location in the Windows Phone 7 SDK where DateTime.Now is important.
We submitted an application to the Marketplace and were failed, several times, with the testers complaining of an error immediately on login. This error was blocking the application, and causing the app to repeatedly fire our global error handler.
We tested and re-tested. We put in extra guard clauses. We changed the time on the phone to see if it was an oAuth problem. We tried behind firewalls and proxies. We even tried unplugging ethernet cables mid-login to see if we could cause the failure. No dice.
Finally, in a fit of desperation, I changed my timezone from New Zealand to Seattle, in a bid to replicate the testing conditions. Bingo!
Turns out the bug had nothing to do with login.
You might be aware that on Windows Phone, Scheduled Tasks expire if the app is not used. Your background task is allowed to run for up to 14 days after the app was last launched. Therefore it’s often our first step after login to refresh the app’s background task, asking it to expire after the maximum allowable time:
task.ExpirationTime = DateTime.UtcNow.AddDays(14);
This code worked perfectly. Unless your phone was set to a timezone anywhere west of Greenwich.
For timezones west of Greenwich, that line of code threw an ArgumentOutOfRange exception.
From what we understand, when scheduling a task, the Windows Phone is checking the expiry time against DateTime.Now, which means that UtcNow + 14 days is over 14 days from “Now” only if you’re west of Greenwich, which is longer than permitted. Hence the exception.
task.ExpirationTime = DateTime.Now.AddDays(14);
To be fair, it makes some sense to be saying the task must expire 14 days from right now. However, the better approach would be to take a TimeSpan as an expiration parameter, not a DateTime.
So there you go. The rule becomes “Always use UTC, unless scheduling a background task on Windows Phone”.