Monday, January 18, 2021

Storing the Time Zone With a Date

Harshil Shah (tweet):

Going back to how Date works, it doesn’t model the actual clock time but rather a fixed point in time that can be interpreted in any time zone. And so what’s happening here is that the data is being interpreted as if it happened in my current time zone, which is the default time zone that Calendar and DateFormatter use.

And as such, a Date alone isn’t sufficient for modelling historical data, or at least personal historical data: You need time zone information too.

HealthKit acknowledges this too. You do have the ability to specify a time zone when constructing the appropriate HKSample subclass for the health data you’re modelling. It just so happens that while you are required to submit the start and end dates for any sample, the time zone information is entirely optional and buried within a metadata dictionary, that you can even omit entirely.

All of the step data shown in the screenshot was captured by the Health app right on my phone, stored in HealthKit, and displayed by the Health app. Somewhere in this pipeline, the time zone information was ignored or discarded.

Nick Lockwood:

I was just talking about this a few minutes ago, specifically the bad decision Apple made of having a default locale/timezone in most of their date-related APIs, which helps to perpetuate the misconception that a Date object is a self-contained representation of a calendar date.


6 Comments RSS · Twitter

Oh look, someone wants NSCalendarDate back! (No one should want NSCalendarDate back; it made things way more confusing, and we used it heavily in BibDesk). How would this work for the people who live in one time zone and work in another, say in MI/WI? For every edge case like "personal historical [health] data" that you try and fix with a strategy like this, you make ten others worse. I've dealt professionally with parsing time-series data collected in local time and GMT at places around the world, and converting them for model inputs; that convinced me that Apple's NSDate approach was the most sane way to do it, as it forces you to think about what you really want for display vs. storage.

@Adam I don’t think that’s what he’s saying. It’s not that NSDate is wrong (except maybe its name), and NSCalendarDate is not the solution (subclassing causes problems here). The points are that sometimes storing only the time interval (as NSDate does) is not enough and that APIs should allow plumbing through the time zone when necessary.

The timezone (or location, it's a complex topic ...) is actually even more important for modelling _future_ data - a little less so for historical data (depending on your needs, for historical stuff it's more like metadata).
You can't reliably generate a future Date (i.e. UTC offset) because you can't foresee the timezone rules governments will decide on. E.g. if we eventually drop DST, all UTC timestamps stored in the affected DST will be off. (Same for countries switching timezones, which is why you might want to store the location, not the TZ. Again depending on your needs ...)

BTW: Systems can disagree on TZ definitions (imagine writing a CalDAV client for Windows Outlook), which is a reason why iCalendar ships the whole thing together with events (or used to).

In short: We should all just switch to UTC ... less headache eventually.

@Michael, I could be reading it wrong, but my (admittedly somewhat dim) memory of NSCalendarDate is that it was an NSDate subclass that had an optional timezone, so conceptually it's the same as his LocalizedDate composed object. I agree that the "*Date" name isn't perfect, but once you understand that it's a wrapper around CFAbsoluteTime, it at least makes sense. NSCalendarDate is one of the few Foundation deprecations that even I like.

@Adam I think the biggest issue with NSCalendarDate was that it was a subclass of NSDate so people (ahem, including me) used it places that only an NSDate was expected and then silently lost the time zone. Sort of like NSDecimalNumber being a subclass of NSNumber, we ran into this problem a lot when storing things to plists unfortunately, since both NSDate and NSNumber were property list types and it feels like it all works until it doesn't.

I still have code in our app that converts between NSCalendarDates and NSDateComponents/NSDates, though I've mostly moved entirely to using NSDateComponents for new data since we're actually concerned with the actual date of an event, not the time, but calling it NSDate sure made it confusing when I was starting out.

@aaron Exactly. NSCalendarDate being a subclass is an invitation for an API receiving an NSDate to throw away its time zone. It also doesn’t correctly handle equality.

Leave a Comment