保持NSDates时区独立

时间:2014-07-10 15:19:54

标签: ios nsdate nsdateformatter nsdatecomponents nstimezone

我正在使用一个包含日历的应用程序,我在特定日期添加了几个注释,并将日期和注释保存到plist,所以当我启动我的应用程序时,我从plist中检索日期并在日历日期显示带有颜色标记,以识别日期是否有注释。

当用户选择添加备注的日期时,我会以这种方式创建日期

 NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    NSDateComponents *weekdayComponents = [gregorian components:(NSDayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit  | NSMinuteCalendarUnit)fromDate:**date selected**];
    NSInteger day    = [weekdayComponents day];
    NSInteger month  = [weekdayComponents month]; 
    NSInteger year   = [weekdayComponents year];

    NSDateComponents *timeZoneComps=[[NSDateComponents alloc] init];
    [timeZoneComps setDay:day];
    [timeZoneComps setMonth:month];
    [timeZoneComps setYear:year];
    [timeZoneComps setHour:00];
    [timeZoneComps setMinute:00];
    [timeZoneComps setSecond:01];    


    NSDate *date = [gregorian dateFromComponents:timeZoneComps];

并将此日期保存到plist

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"file.plist"];

    NSMutableDictionary *d = [NSMutableDictionary new];
    [d setObject:date forKey:@"my-date"];

    [d writeToFile:filePath atomically:YES];

现在,当我打开应用程序时,我会遍历我的日历日期并进行比较以标记不在日历上

这是

if([[**dateFromPlist** isEqualToDate:**dateFromCalendar**])
{            
        do something
}

当我在一个时区内写下和阅读日期时,这是有效的,但如果我写日期,假设我在10月-20日在印度时区(设备)创建了一个音符,然后我将时区更改为美国(设备)并打开我的应用程序,if条件永远不会执行,因为它会有不同的时间。

请帮我找出错误的地方。

此致 兰吉特。

1 个答案:

答案 0 :(得分:4)

NSDate没有时区。它是unix时间的包装器,是自参考日期以来的浮点数秒。 (固定时间点)NSTimeInterval是double的typedef,是用于表示此浮点值的类型。

您应该做的是存储NSTimeInterval值。然后在从文件中读取NSDate对象时从该值创建NSDate对象。

时区严格地是向用户呈现的一部分。

假设someDate是NSDate

    NSDate *someDate = [NSDate date];
    NSTimeInterval someDateAsInterval = someDate.timeIntervalSinceReferenceDate;

好的,现在你有了你的双,a.k.a。NSTimeInterval。让我们将它保存到plist然后再读回来。我们将使用NSDictionary和NSNumber文字语法。 (我在这里不需要一个可变字典。)NSNumber是一种在标记中存储标量的好方法。

    NSString *aFilePath = [@"~/Documents/my_fancy_file.plist" stringByExpandingTildeInPath];

    NSDictionary *dateDict = @{@"dateAsInterval": @(someDateAsInterval)};
    [dateDict writeToFile:aFilePath atomically:YES];

    NSDictionary *dateDictFromFile = [NSDictionary dictionaryWithContentsOfFile:aFilePath];
    NSTimeInterval dateFromFileAsTimeInterval = [dateDictFromFile[@"dateAsInterval"] doubleValue];
    NSDate *dateFromFile = [NSDate dateWithTimeIntervalSinceReferenceDate:dateFromFileAsTimeInterval];
    NSLog(@"\n someDate\n%@\n dateFromFile\n%@", someDate, dateFromFile);

下面的最后一点只是为了说明它有多方便。哦,我提到NSTimeInterval可能比完整的NSDate对象便宜很多吗?这是一个很好的加分。

    if ([someDate isEqual:dateFromFile]) {
        NSLog(@"SAME");
    }

    if (someDateAsInterval == dateFromFileAsTimeInterval) {
        NSLog(@" == ");
    }

现在还有一件事你完全搞砸了,但你并不孤单,而且不明显或不容易。 您可以从日期和时间中学到很多以下内容。文档中的时间编程指南。 其余内容在WWDC视频中有详细介绍。 事实上,人们已经搞砸了这么多,几乎每年都会在WWDC上报道。今年也没有例外。当你回想起NSDate实际上是一个包装typedef& d的对象,NSTimeInterval时,这将更容易理解。这提供了

让我们引用文档:

  

NSDate提供了创建日期,比较日期和方法的方法   计算间隔。日期对象是不可变的。标准单位   日期对象的时间是浮点值,键入为NSTimeInterval   并以秒表示。这种类型使广泛和   细粒度的日期和时间值范围,提供精确的内部   相隔10000年的日期的毫秒数。

这意味着该对象为您提供方法而不是C样式函数。一个可爱的Objective-C世界。支持数据结构NSTimeInterval为您提供了令人难以置信的精确度。

在最近的WWDC视频中还有一件事你需要知道。

比较日期和日历计算很难找到可能让您感到惊讶的原因。最值得考虑的是,并非所有日期都有午夜,有些日期不止一个! 因此,比较午夜的两个日期可能是一团糟。这很复杂但很真实。不要这样做。 最佳做法:白天使用一段时间,接近中午。所有日期都有这个时间,只有一次。 等等,我为什么要谈论时间?回头仔细思考。 NSDate的名字误导了很多人,我们插入了很多关于我们认为很简单的常见事物的假设,但实际上看起来很复杂。为方便起见,我们倾向于将日期视为日历上的日期,但事实上,日期和NSDate是特定的时间点。

事实证明,使用NSCalendar方法dateFromComponents是一个常见的错误:不包括更精细的组件。可悲的是,如果你没有提供时间,它会给你一个00:00:00的默认值,这会导致比较和计算的日期不可靠。

但我离题了。 这里的关键是使用您的NSTimeInterval值。这很容易比较。易于存放。很难出错。 您可以根据需要从中创建NSDate。 您可以根据需要使用当前区域设置显示它。

这是plist内容的样子。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>dateAsInterval</key>
    <real>426751774.87438202</real>
</dict>
</plist>