为什么NSDateFormatter返回这4个时区的空日期?

43

尝试在iOS6中运行此代码(未经iOS6测试):

NSDateFormatter *julianDayDateFormatter = nil;
julianDayDateFormatter = [[NSDateFormatter alloc] init];
[julianDayDateFormatter setDateFormat:@"g"];

for (NSString *timeZone in [NSTimeZone knownTimeZoneNames]) {
    julianDayDateFormatter.timeZone = [NSTimeZone timeZoneWithName: timeZone];
    NSDate *date = [julianDayDateFormatter dateFromString:[NSString stringWithFormat:@"%d", 2475213]];
    if (date == nil)
        NSLog(@"timeZone = %@", timeZone);
}

然后您会得到以下输出:

America/Bahia
America/Campo_Grande
America/Cuiaba
America/Sao_Paulo

有人能解释一下,为什么当NSDateFormatter设置为儒略日数字时,这四个时区会出现这种情况?而所有其他时区都能使NSDateFormatter返回实际的NSDates。


3
这是一个很好的问题,但显然Jon Skeet的影响在这里是非常讨人厌的。 - jscs
6
@JoshCaswell:这很棘手。我总是对发这种推文感到紧张。它肯定会扭曲投票结果,但我想引起人们的注意,让他们觉得有趣。至少,就声望而言,对于我的回答来说,投票是无关紧要的。 - Jon Skeet
2个回答

63

我有一个怀疑。 只是一个怀疑,但非常强烈。

那个值代表2064年10月19日。巴西的时区在当地午夜开始观察夏令时-那时他们的时钟会向前调整,因此午夜本身不存在。10月19日是这些过渡之一。

这是使用Noda Time,我的.NET日期/时间API的一些示例代码。它检查它所知道的每个时区的一天开始是否实际上是午夜:

using System;
using NodaTime;

class Test
{
    static void Main()
    {
        var localDate = new LocalDate(2064, 10, 19);
        var provider = DateTimeZoneProviders.Tzdb;
        foreach (var id in provider.Ids)
        {
            var zone = provider[id];
            var startOfDay = zone.AtStartOfDay(localDate).LocalDateTime.TimeOfDay;
            if (startOfDay != LocalTime.Midnight)
            {
                Console.WriteLine(id);
            }
        }
    }
}

这会生成一个非常相似的列表:

America/Bahia
America/Campo_Grande
America/Cuiaba
America/Sao_Paulo
Brazil/East

我怀疑 Brazil/East 可能是 America/Sao_Paolo 的别名,这就是为什么它不在你的列表中。

无论如何,回到你的儒略日问题 - 我怀疑格式化程序总是想返回一个处于当地午夜的 NSDate *。对于这些时区来说,2064 年 10 月 19 日的午夜时间不存在......因此它返回 nil。个人认为应该返回上午1点的值,但是......


谢谢。你让我找到了正确的方向。这肯定与夏令时转换有关。记录一下:iOS5不会表现出这种行为,它总是返回NSDate。我已经向苹果提交了一个错误报告。 - lpa
1
@lpa:出于兴趣,对于那些时区它返回什么值?如果它返回一个本地时间为午夜的值,那么这是另一种错误吗? - Jon Skeet
在iOS5中,对于那些时区它返回什么值? - lpa
@lpa:是的,完全正确。在那些日期上不存在本地午夜 - 返回1am是有意义的,因为那是当地一天的开始。 - Jon Skeet
抱歉回复晚了。iOS5.0返回1am,巴西除外,返回12am。在iOS5.1中,所有时区都返回1am。 - lpa
我建议它应该返回1am的值,您可以这样告诉它:https://dev59.com/Z2Af5IYBdhLWcg3w_Gyr#40320768。 - Martin R

16

感谢Jon Skeet为我指明正确的方向。但是,我只想在iOS环境中澄清他的答案。

当您请求NSDateFormatter将儒略日转换为NSDate时,您只能在要解析的字符串中指定整数部分(通常可以指定一天中小时/分钟/秒的小数部分)。 由于苹果将儒略日划分为午夜(而不是天文学中的正午,可以在此处阅读更多信息:http://www.unicode.org/reports/tr35/#Date_Field_Symbol_Table),而且某些午夜根本不存在(感谢@JonSkeet指出这一点),NSDateFormatter识别出该特定时间点在该时区中不存在,并返回nil。

值得记录的是,iOS5不会像这样行事,我同意Jon Skeet的观点,即NSDateFormatter应返回已校准DST的NSDate,而不是nil,因为该特定儒略日实际上存在!我向苹果提交了一个错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接