Pergunta

I have a date that's stored as a number of days since January 1, 1600 that I need to deal with. This is a legacy date format that I need to read many, many times in my application.

Previously, I'd been creating a calendar, empty date components and root date like this:

self.gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar
                    ] autorelease];
id rootComponents = [[[NSDateComponents alloc] init] autorelease];
[rootComponents setYear: 1600];
[rootComponents setMonth: 1];
[rootComponents setDay: 1];
self.rootDate = [gregorian dateFromComponents: rootComponents];
self.offset = [[[NSDateComponents alloc] init] autorelease];

Then, to convert the integer later to a date, I use this:

[offset setDay: theLegacyDate];
id eventDate = [gregorian dateByAddingComponents: offset
                                          toDate: rootDate
                                         options: 0];

(I never change any values in offset anywhere else.)

The problem is I'm getting a different time for rootDate on iOS vs. Mac OS X. On Mac OS X, I'm getting midnight. On iOS, I'm getting 8:12:28. (So far, it seems to be consistent about this.) When I add my number of days later, the weird time stays.

OS       | legacyDate | rootDate                  | eventDate
======== | ========== | ==========================|==========================
Mac OS X | 143671     | 1600-01-01 00:00:00 -0800 | 1993-05-11 00:00:00 -0700
iOS      | 143671     | 1600-01-01 08:12:28 +0000 | 1993-05-11 07:12:28 +0000

In the previous release of my product, I didn't care about the time; now I do. Why the weird time on iOS, and what should I do about it? (I'm assuming the hour difference is DST.)

I've tried setting the hour, minute and second of rootComponents to 0. This has no impact. If I set them to something other than 0, it adds them to 8:12:28. I've been wondering if this has something to do with leap seconds or other cumulative clock changes.

Or is this entirely the wrong approach to use on iOS?

Foi útil?

Solução 2

It looks like the right answer is to make things simpler. Instead of making a rootDate, I just build the date from components every time. This should be no slower, and is still keeps the code really close to the idea.

Initial setup:

self.gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar
                    ] autorelease];
self.components = [[[NSDateComponents alloc] init] autorelease];
[components setYear: 1600];
[components setMonth: 1];

(Obviously, properties and ivars are adjusted.)

Later, to actually convert a legacy date to a NSDate:

[components setDay: 1 + theLegacyDate];
id eventDate = [gregorian dateFromComponents: components];

This has these advantages for me:

  1. It users fewer ivars.
  2. It's less code.
  3. It always returns midnight on that day, regardless of whether DST is in effect.

Outras dicas

I imagine you're right about the leap seconds/cumulative clock changes accounting for the time issue. Are the dates you're dealing with actually in the past, or is it purely an arbitrary epoch?

In either case, you could try defining a new epoch that's much closer to present day (say, the Cocoa epoch). Calculate a day delta between the new epoch and the old and save it as a constant. When you need to process a date, apply this delta to the date and then use your existing NSCalendar technique, but with your new epoch instead of the old. That will hopefully avoid the clock drift issue you're seeing.

Note that iOS takes into account very obscure rules for various time zones. It is most likely that midnight, Jan 1st. 1600 in your timezone actually was at 7:12:28 UTC. There have been many cases where people complained about bugs in date conversions and then someone figured out that actually they are in a time zone that made some strange calendar change many years ago.

You need to find out first what exact NSDate your data represents. "Number of days since Jan 1st 1600" is nonsense, because you need a time zone! What you should do: Find a "legacy" number where you know what day it is supposed to represent. For example, if you "know" that 143671 is supposed to be 11th May 1993 in your time zone, then start with that date as the root date and add (x - 143671) days to it.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top