代码之家  ›  专栏  ›  技术社区  ›  Bartłomiej Semańczyk

为什么从日期开始的组件不为错误的组件返回零?

  •  0
  • Bartłomiej Semańczyk  · 技术社区  · 3 年前

    这就是我生成日期的方式:

    private var date: Date? {
        let components = DateComponents(
            year: 2023,
            month: 2,
            day: 31,
            hour: 11,
            minute: 14
        )
        return Calendar.current.date(from: components)
    }
    

    输出是什么?

    3 Mar 2023, 11:14
    

    当日组件超出所选月份的预期天数时,如何返回nil?这有什么参数吗?还是应该手动计算?

    3 回复  |  直到 3 年前
        1
  •  1
  •   rob mayoff    3 年前

    这个为什么的问题很难回答。苹果公司没有记录原因。旧的 Date and Time Programming Guide 实际上是说你所做的是未定义的行为:

    为了保证正确的行为,您必须确保所使用的组件对日历有意义。指定超出界限的组件,例如公历中的日值-6或2月30日,会产生未定义的行为。

    我们可以猜测它的未定义行为,而不是显式地产生错误,因为这简化了接口(不需要返回错误的方法)并简化了实现(不需要检查超出范围的值)。

    深入挖掘有助于了解基础 Calendar 与所有Foundations区域设置支持一样,实现基于 International Components for Unicode 图书馆(ICU)。您可以找到相关的Foundation源代码 here 。ICU源代码为 here

    事实证明,底层的ICU对象,一个C++ icu::Calendar a “lenient” flag ,默认情况下为true,并且会产生您所看到的行为:

    根据宽松的解释,诸如“1996年2月942日”之类的日期将被视为相当于1996年二月一日之后的第941天。通过严格的解释,当根据表示日期的时间字段值计算时间时,这样的日期会导致错误。

    基础 日历 创建 icu::日历 并且永远不会改变其宽容的旗帜,所以你会得到宽容的行为。

    为什么对违约从宽?我不知道这是否有记录。

        2
  •  1
  •   flanker    3 年前

    这以前让我觉得很奇怪,虽然我确信这背后有一个逻辑,而不仅仅是基本的数学,但我从来没有弄清楚它是什么。我的解决方法是:

    private var date: Date? {
        let components = DateComponents(
            year: 2023,
            month: 2,
            day: 31,
            hour: 11,
            minute: 14
        )
        guard components.isValidDate else {return nil}
        return Calendar.current.date(from: components)
    }
    

    我觉得我不应该这样做,但我还没有找到一个切实可行的解决方案来避免它。

        3
  •  1
  •   Allan Garcia    3 年前

    也许DateComponents是NSDateComponents的直接桥梁,正如官方文档中所述 here

    重要的

    NSDateComponents对象本身毫无意义;你需要知道 它是根据什么日历来解释的,你需要知道 这些值是单位的绝对值,或 单位。

    同一页中的另一个重要注释是:

    重要的

    Swift对Foundation框架的覆盖提供了 DateComponents结构,该结构桥接到NSDateComponents类。 有关值类型的更多信息,请参阅使用Cocoa 将Swift与Cocoa和Objective-C结合使用的框架(Swift 4.1)。

    更好的方法是使用Struct 日期组件格式样式 here 使用属性 字段 here 以设置日期组件。