objective-c - 使用 dateFromComponents 时,将 NSDateComponents 中的日期设置为大于 31 的日期?

标签 objective-c nsdate nsdatecomponents

假设我有一个约会,我想做两件事:

  1. 将小时重置为指定的小时。
  2. 将一天增加一定数量。

通过阅读文档,您似乎需要使用两个不同的步骤:

// 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 日,这似乎也能正常工作,正确地回到 2014 年 2 月 1 日上午 9 点。

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

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

请注意 32 在公历中是如何超出范围的。

依赖这条捷径是否危险(例如,如果它只是碰巧起作用)?或者是否有任何关于 dateFromComponents 的文档说明它可以以这种方式使用?

此外,它如何知道我何时希望换行以及何时我想显式覆盖这些值之一?

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

最佳答案

我没有真正的证据或文档引用,但我发现了一些迹象表明这种在日期计算中的“回绕”确实有效。

  • 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 日,被“宽大”对待。

关于objective-c - 使用 dateFromComponents 时,将 NSDateComponents 中的日期设置为大于 31 的日期?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22134686/

相关文章:

objective-c - 执行选择器 :withObject:afterDelay not working inside viewDidLoad method

iphone - NSDate 完整样式但没有年份

ios - objective-C/iOS : Get date for specific day in week (sunday)

ios - 为什么我在 iOS 中的 DateComponents 对象不能正确地保存到 CloudKit 或从中检索?

iPhone - 获取两个日期之间的天数

iphone - 应用程序崩溃、内存不足

objective-c - 如何在 Cocoa 应用程序中正确包装 C 库

ios - 如何在 iOS 中读取、写入和检查不区分大小写的文件名?

ios - 如何测试内部调用 NSDate() 的方法?

ios - 将时间添加到日期选择器日期和时间会产生相同的日期和时间。 swift