使用dateFromComponents时,将NSDateComponents中的日期设置为高于31的值?

时间:2014-03-02 22:43:57

标签: objective-c nsdate nsdatecomponents

说我有约会,我想做两件事:

  1. 将小时重置为指定的小时。
  2. 将一天增加一定数量。
  3. 通过阅读文档,您看起来需要使用两个不同的步骤:

    // Step 1: Get today at 9 AM.
    NSDateComponents *todayComponents = [[NSCalendar currentCalendar] components:(NSCalendarUnitSecond | NSCalendarUnitMinute | NSCalendarUnitHour | NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear ) fromDate:[NSDate date]];
    [todayComponents setHour:9];
    NSDate *todayAt9AM = [[NSCalendar currentCalendar] dateFromComponents:todayComponents];
    
    // Step 2: Add one day to that date.
    NSDateComponents *oneDay = [NSDateComponents new];
    [oneDay setDay:1];
    NSDateComponents *tomorrowAt9AM = [[NSCalendar currentCalendar] dateByAddingComponents:oneDay toDate:todayAt9AM options:0];
    

    然而,似乎有一条捷径只涉及一个步骤:

    NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSCalendarUnitSecond | NSCalendarUnitMinute | NSCalendarUnitHour | NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear ) fromDate:[NSDate date]];
    components.hour = 9;
    components.day += 1;
    NSDate *tomorrowAt9AM = [[NSCalendar currentCalendar] dateFromComponents:components];
    

    即使日期是2014年1月31日,这似乎也能正常工作,正好在上午9点到了2014年2月1日。

    这看起来很奇怪的原因是因为您正在向dateFromComponents发送以下值:

    • 年份:2014
    • 月:1
    • 日:32
    • 小时:9
    • 分钟:34(例如)
    • 秒:12(例如)

    请注意公历中32的超出范围。

    依赖这条捷径是否很危险(例如,如果碰巧只是碰巧工作)?或者是否有任何dateFromComponents文档说它可以这样使用?

    此外,它是如何知道我希望包装发生的时间与我想要显式覆盖其中一个值的时间?

    假设我已将月份设置为3,将月份设置为3,还是首先处理我指定的月份(3),然后处理可能会将月份增加到4的天数?这是未定义的行为,还是遵循一些记录的规则?

1 个答案:

答案 0 :(得分:3)

我没有真正的证据或文档参考,但我发现了一些指标,这些日期计算中的“环绕”确实有效。

  • NSCalendar 是免费的桥接到其核心基金会对应物, CFCalendarRef

  • 来自源代码 CFCalendar.c 我们可以看到所有的日历计算都是使用 ICU Calendar Classes

  • Calendar class 有一个方法setLenient

    /**
     * Specifies whether or not date/time interpretation is to be lenient. With lenient
     * interpretation, a date such as "February 942, 1996" will be treated as being
     * equivalent to the 941st day after February 1, 1996. With strict interpretation,
     * such dates will cause an error when computing time from the time field values
     * representing the dates.
     *
     * @param lenient  True specifies date/time interpretation to be lenient.
     *
     * @see            DateFormat#setLenient
     * @stable ICU 2.0
     */
    void setLenient(UBool lenient);
    
  • 最后,从calendar.cpp中的构造函数可以看到默认的“lenient”值为TRUE

    Calendar::Calendar(UErrorCode& success)
    :   UObject(),
        fIsTimeSet(FALSE),
        fAreFieldsSet(FALSE),
        fAreAllFieldsSet(FALSE),
        fAreFieldsVirtuallySet(FALSE),
        fNextStamp((int32_t)kMinimumUserStamp),
        fTime(0),
        fLenient(TRUE),
        fZone(0)
    {
        clear();
        fZone = TimeZone::createDefault();
        setWeekCountData(Locale::getDefault(), NULL, success);
    }
    

将所有这些放在一起,我们得到了那个设置

components.month = 1;
components.day = 32;

在1月1日之后的第31天(即2月1日)被“宽大地”对待。