使用“ localTimeZone”设置NSCalendar时区与使用“ timeZoneWithAbbreviation”设置不同的值之间的区别

时间:2018-12-14 22:08:03

标签: ios nsdate nsdateformatter nscalendar nsdatecomponents

我已将手机(实际设备,而不是模拟器)配置为位于英国伦敦的格林尼治标准时间。

当我使用NSCalendarlocalTimeZone上设置时区时,与我使用[NSTimeZone timeZoneWithAbbreviation:@"GMT"]设置时区相比,它使我节省了1分15秒的时间。如果我的时区已经设置为格林尼治标准时间,两个人都不应该给我相同的结果吗?

我注意到的另一件事是,如果设置了断点,则使用NSDate通过断点检查的localTimeZone会关闭1分15秒,但是NSLog会打印正确的时间(断点显示:0000-12-30 09:01:15 UTC,但NSLog打印12月30日星期六09:00:00 0000):

enter image description here

但是当我使用[NSTimeZone timeZoneWithAbbreviation:@"GMT"]时,断点会显示正确的时间,但是NSLog会在1分钟15秒的时间内显示错误的时间(断点显示:0000-12-30 09:00 :00 UTC,但NSLog打印12月30日星期六08:58:45 0000):

enter image description here

这是我的代码以及断点和NSLog输出(在其旁边的注释中):

NSLog(@"Timezone: %@",[NSTimeZone localTimeZone]);//Timezone: Local Time Zone (Europe/London (GMT) offset 0)

    NSLocale *locale = [NSLocale currentLocale];
    NSCalendar *myCalendar = [NSCalendar currentCalendar];
    [myCalendar setLocale:locale];
    [myCalendar setTimeZone:[NSTimeZone localTimeZone]];
    NSLog(@"TZ: %@",myCalendar.timeZone);//TZ: Local Time Zone (Europe/London (GMT) offset 0)

    NSDateFormatter *timeFormatter = [NSDateFormatter new];
    [timeFormatter setDateFormat:@"h:mm a"];
    [timeFormatter setLocale:locale];
    [timeFormatter setTimeZone:[NSTimeZone localTimeZone]];

    NSDateComponents* dateComps = [myCalendar components:NSCalendarUnitHour|NSCalendarUnitMinute fromDate:[timeFormatter dateFromString:@"9:00 AM"]];
    NSDate *myDate1 = [myCalendar dateFromComponents:dateComps];//Breakpoint shows: 0000-12-30 09:01:15 UTC


    NSLog(@"myDate1: %@",myDate1); // myDate1: Sat Dec 30 09:00:00 0000

    //----------------Explicitly specified timeZoneWithAbbreviation GMT:


    NSCalendar *myCalendarExplicitGMT = [NSCalendar currentCalendar];
    [myCalendarExplicitGMT setLocale:locale];
    [myCalendarExplicitGMT setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"GMT"]];
    NSLog(@"TZ: %@",myCalendarExplicitGMT.timeZone);//TZ: GMT (GMT) offset 0

    NSDateComponents* dateCompsUsingExplicitGMT = [myCalendarExplicitGMT components:NSCalendarUnitHour|NSCalendarUnitMinute fromDate:[timeFormatter dateFromString:@"9:00 AM"]];
    NSDate *myDate2 = [myCalendarExplicitGMT dateFromComponents:dateCompsUsingExplicitGMT];//Breakpoint shows: 0000-12-30 09:00:00 UTC


    NSLog(@"myDate2: %@",myDate2); // myDate2: Sat Dec 30 08:58:45 0000

1 个答案:

答案 0 :(得分:3)

在计算过程中,您将获得仅带有小时和分钟值的NSDateComponents。到目前为止,您还可以。但是,要与它一起使用NSDateFormatter,则需要转换为NSDate(因为这正是NSDateFormatter的要求)。 NSDate是时间上的绝对瞬间,因此它需要一个完全指定的日期才能进行转换。 NSDateComponents中缺少的任何字段都使用默认值(零),而不是返回nil。因此,它使用的是1年(实际上是向后调整为1 BC)。当您这样做时,您会看到年份打印为“ 0000”。

当您使用显式GMT时区时,您将显式指定偏移量,并且它基本上按预期工作。但是,当使用本地时区(即您的欧洲/伦敦)格式化NSDate时,它将应用该时区中发生的任何历史变化,因为这是“欧洲/伦敦”所说的。 NSTimeZone使用的IANA time zone system具有相当完整的所有时区更改历史,可以追溯到时区系统本身的创建。他们忽略了大约1970年之前和之后的时区,但是他们试图追踪他们确实回到其起源的时区的历史。对于伦敦来说,他们似乎早在1847年就将GMT用作标准时间(在创建全时区系统之前)。不过,在此之前,他们(以及其他所有人)使用的是“本地平均时间”,这是该镇所在位置的自然时间-因此,对于相距一定度经度的城镇,他们的时间会相差四分钟(每分钟1440分钟)天除以地球的经度360度,每度得到4分钟)。因此,在1847年之前,他们使用的“伦敦平均时间”与“格林威治标准时间”略有不同。通过查看tzdata“欧洲”源文件,他们给出了在1994年 Independent 文章中找到的描述:

'An old stone obelisk marking a forgotten terrestrial meridian stands
beside the river at Kew. In the 18th century, before time and longitude
was standardised by the Royal Observatory in Greenwich, scholars observed
this stone and the movement of stars from Kew Observatory nearby. They
made their calculations and set the time for the Horse Guards and Parliament,
but now the stone is obscured by scrubwood and can only be seen by walking
along the towpath within a few yards of it.'

I have a one inch to one mile map of London and my estimate of the stone's
position is 51° 28' 30" N, 0° 18' 45" W. The longitude should
be within about ±2". The Ordnance Survey grid reference is TQ172761.

[This yields GMTOFF = -0:01:15 for London LMT in the 18th century.]

因此,他们计算得出1847年之前,伦敦(和“欧洲/伦敦”时区)距格林尼治标准时间有1分15秒,因此它将相应地调整您的时间。由于您的NSDate适用于公元前1年,因此绝对可以肯定是在1847年之前,因此您可以进行调整。当我以同样的方式尝试America / New_York时,它与纯GMT-5的区别是3:58。

因此,简短的答案是-当您使用现代时区进行日期转换时,最好使其余的日期组件也保持现代。例如,如果让一年回到第二次世界大战,您将开始应用伦敦在战争期间的双重夏令时,从1800年代及更早的时候开始,您进入了使铁路公司疯狂的时间体系;-)。如您所见,您的另一种选择是使用非历史性但具有可预测偏移量的NSTimeZones。但是,NSLog()使用NSDate -description方法,该方法将依次使用当前(历史)时区,并且在古代一直会得到那些相当任意的偏移量。