ios - 保持 NSDates 时区独立

标签 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 - jul -2014 上创建了一个关于印度时区(设备)的注释,然后我将时区更改为美国(设备)并打开我的应用程序,if 条件永远不会执行,因为它的时间不同。

请帮助我找出问题所在。

问候
兰 git 。

最佳答案

NSDate 没有时区。它是一个围绕 unix 时间的包装器,它是自引用日期以来的浮点秒数。 (一个固定的时间点) NSTimeInterval 是一个 double 类型定义,是用来表示这个浮点值的类型。

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

时区严格来说是向用户展示的一部分。

假设 someDate 是 NSDate

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

好的,现在你有了你的替身,也就是 NSTimeInterval。让我们把它保存到一个 plist 中,然后再读回来。我们将使用 NSDictionary 和 NSNumber 文字语法。 (我在这里不需要可变字典。) NSNumber 是将标量存储在 plist 中的好方法。
    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 上被报道。今年也不异常(exception)。当您回想起 NSDate 实际上是一个包装了 typedef'd double NSTimeInterval 的对象时,这将更容易理解。这提供了

让我们引用文档:

NSDate provides methods for creating dates, comparing dates, and computing intervals. Date objects are immutable. The standard unit of time for date objects is floating point value typed as NSTimeInterval and is expressed in seconds. This type makes possible a wide and fine-grained range of date and time values, giving precision within milliseconds for dates 10,000 years apart.



这意味着该对象为您提供方法而不是 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>

关于ios - 保持 NSDates 时区独立,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24680358/

相关文章:

ios - 来自 iPhone 设置和 GMT 偏移的日期时间格式

ios - 如何从 NSMutableArray 中的循环添加 NSString

iphone - iOS 中的离线通知

ios - 在 AVPlayer 上添加 ActivityIndi​​cator 并删除 addObserver

ios - 无效 token VoIP Apple 推送通知

ios - 将自1970年以来的时间间隔转换为自1970年以来的时间间隔

iphone - 在 iOS 中将 NSdate 舍入到最近的小时

iphone - 无法使用 NSDateFormatter 从字符串中解析日期

ios - 从字符串到 NSDate

Swift:从日期选择器获取正确的时区?