swift Calendar

作者: 想聽丿伱說衹愛我 | 来源:发表于2020-12-21 16:33 被阅读0次

    一、简介

    Calendar封装了有关时间系统的信息,其中定义了年的开始和长度等。它提供有关日历的信息,并支持日历计算,例如获取符合条件的Date或DateComponents等。

    二、API

    1. 初始化
    • 用户当前使用的日历
    public static var current: Calendar { get }
    

    默认为gregorian (current)

    • 自动跟踪用户设置的日历
    public static var autoupdatingCurrent: Calendar { get }
    

    默认为gregorian (autoupdatingCurrent)

    • 通过identifier初始化
    public init(identifier: Calendar.Identifier)
    
    public enum Identifier {
            case gregorian//公历
            case buddhist//佛历
            case chinese//农历
            case coptic//科普特历
            case ethiopicAmeteMihret//埃塞俄比亚历
            case ethiopicAmeteAlem//埃塞俄比亚阿米特阿莱姆日历
            case hebrew//希伯来历
            case iso8601//国际标准历法
            case indian//印度国定历
            case islamic//伊斯兰历
            case islamicCivil//伊斯兰希吉来日历
            case japanese//和历
            case persian//波斯历
            case republicOfChina//民国纪年
            case islamicTabular//伊斯兰天文历
            case islamicUmmAlQura//伊斯兰历(乌姆库拉)
    }
    
    let c = Calendar(identifier: .gregorian)//公历
    print(c)
    //gregorian (fixed)
    
    1. 属性
    • 日历的标识符
    public var identifier: Calendar.Identifier { get }
    
    let c = Calendar(identifier: .gregorian)//公历
    print(c.identifier)
    //gregorian
    
    • 日历的本地化
    public var locale: Locale?
    

    默认为nil

    • 日历的时区
    public var timeZone: TimeZone
    

    默认为Asia/Shanghai (current)

    • 日历的第一个工作日
    public var firstWeekday: Int
    

    默认为1,表示为星期一。

    • 第一周的最少天数
    public var minimumDaysInFirstWeek: Int
    

    默认为1,表示某月的第一周只有一天。

    1. 标志符
    • 纪元标志符
    public var eraSymbols: [String] { get }
    public var longEraSymbols: [String] { get }
    

    locale属性为en_US时,eraSymbols默认为 ["BCE", "CE"]。
    locale属性为zh_CN时,eraSymbols默认为 ["公元前", "公元"]。

    var c = Calendar(identifier: .gregorian)
    c.locale = Locale(identifier: "en_US")
    print(c.eraSymbols)
    //["BCE", "CE"]
    c.locale = Locale(identifier: "zh_CN")
    print(c.eraSymbols)
    //["公元前", "公元"]
    

    locale属性为en_US时,longEraSymbols默认为 ["Before Christ", "Anno Domini"]。
    locale属性为zh_CN时,longEraSymbols默认为 ["公元前", "公元"]。

    • 月份标志符
    public var monthSymbols: [String] { get }
    public var shortMonthSymbols: [String] { get }
    public var veryShortMonthSymbols: [String] { get }
    
    //独立月份标志符
    public var standaloneMonthSymbols: [String] { get }
    public var shortStandaloneMonthSymbols: [String] { get }
    public var veryShortStandaloneMonthSymbols: [String] { get }
    

    locale属性为en_US时,
    monthSymbols默认为 ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]。
    shortMonthSymbols默认为 ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]。
    veryShortMonthSymbols默认为 ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"]。

    standaloneMonthSymbols默认为 ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]。
    shortStandaloneMonthSymbols默认为 ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]。
    veryShortStandaloneMonthSymbols默认为 ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"]。

    注:standaloneMonthSymbols之类的独立属性用于日历标题之类的地方。monthSymbols之类的非独立属性用于上下文("2020年12月18日 星期五"中的12月)。

    • 星期标志符
    public var weekdaySymbols: [String] { get }
    public var shortWeekdaySymbols: [String] { get }
    public var veryShortWeekdaySymbols: [String] { get }
    
    public var standaloneWeekdaySymbols: [String] { get }
    public var shortStandaloneWeekdaySymbols: [String] { get }
    public var veryShortStandaloneWeekdaySymbols: [String] { get }
    

    locale属性为en_US时,
    weekdaySymbols默认为 ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]。
    shortWeekdaySymbols默认为 ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]。
    veryShortWeekdaySymbols默认为 ["S", "M", "T", "W", "T", "F", "S"]。

    standaloneWeekdaySymbols默认为 ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]。
    shortStandaloneWeekdaySymbols默认为 ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]。
    veryShortStandaloneWeekdaySymbols默认为 ["S", "M", "T", "W", "T", "F", "S"]。

    注:standaloneWeekdaySymbols之类的独立属性用于日历标题之类的地方。weekdaySymbols之类的非独立属性用于上下文("2020年12月18日 星期五"中的星期五)。

    • 季度标志符
    public var quarterSymbols: [String] { get }
    public var shortQuarterSymbols: [String] { get }
    
    public var standaloneQuarterSymbols: [String] { get }
    public var shortStandaloneQuarterSymbols: [String] { get }
    

    locale属性en_US时,
    quarterSymbols默认为 ["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"]。
    shortQuarterSymbols默认为 ["Q1", "Q2", "Q3", "Q4"]。

    standaloneQuarterSymbols默认为 ["1st quarter", "2nd quarter", "3rd quarter", "4th quarter"]。
    shortStandaloneQuarterSymbols默认为 ["Q1", "Q2", "Q3", "Q4"]。

    注:standaloneQuarterSymbols之类的独立属性用于日历标题之类的地方。quarterSymbols之类的非独立属性用于上下文("2020年12月18日 星期五 第四季度"中的 第四季度)。

    • 时间段标志符
    public var amSymbol: String { get }
    public var pmSymbol: String { get }
    

    locale属性为en_US时,
    amSymbol默认为 AM。
    pmSymbo默认为 PM。

    1. 范围
    • 指定组件的最小范围限制
    public func minimumRange(of component: Calendar.Component) -> Range<Int>?
    
    public enum Component {
            case era//纪元
            case year//年
            case month//月
            case day//日
            case hour//时
            case minute//分
            case second//秒
            case weekday//周几
            case weekdayOrdinal//本月的第几个周几
            case quarter//季度
            case weekOfMonth//一个月的周数
            case weekOfYear//一年的周数
            case yearForWeekOfYear//ISO8601标准下的年份
            case nanosecond//纳秒
            case calendar//日历
            case timeZone//时区
    }
    

    关于Component,可查看DateComponents的初始化方法了解详细。

    2月最少为28天,范围为 1..<29。

    let c = Calendar(identifier: .gregorian)
    print(c.minimumRange(of: .day))
    //Optional(Range(1..<29))
    
    • 指定组件的最大范围限制
    public func maximumRange(of component: Calendar.Component) -> Range<Int>?
    

    大月有31天,范围为 1..<32。

    let c = Calendar(identifier: .gregorian)
    print(c.maximumRange(of: .day))
    //Optional(Range(1..<32))
    
    • 小组件可以在大组件中使用的范围
    public func range(of smaller: Calendar.Component, in larger: Calendar.Component,
                      for date: Date) -> Range<Int>?
    

    一年最多有366天,范围为 1..<367。

    let c = Calendar(identifier: .gregorian)
    print(c.range(of: .day, in: .year, for: Date()))
    //Optional(Range(1..<367))
    
    • 指定日期所处的开始日期与持续时间
    public func dateInterval(of component: Calendar.Component, start: inout Date, 
                             interval: inout TimeInterval, for date: Date) -> Bool
    

    如果能成功计算开始日期与持续时间,则返回true。

    如下如示,2020-12-18 07:45:49这个日期在.hour下的开始日期为2020-12-18 07:00:00,持续时间为3600.0,指的就是2020-12-18 07:00:00-2020-12-18 08:00:00这个范围。

    let c = Calendar(identifier: .gregorian)
    var d = Date()
    var t = 0.0
    print(d, t)
    //2020-12-18 07:45:49 +0000 0.0
    print(c.dateInterval(of: .hour, start: &d, interval: &t, for: d))
    //ture
    print(d, t)
    //2020-12-18 07:00:00 +0000 3600.0
    
    • 指定日期所处的日期范围
    public func dateInterval(of component: Calendar.Component, for date: Date)
                                                              -> DateInterval?
    

    功能与上面方法相同,返回的是日期范围。

    如下,2020-12-18 07:54:13在.hour下的日期范围为
    2020-12-18 07:00:00 +0000 to 2020-12-18 08:00:00 +0000

    let c = Calendar(identifier: .gregorian)
    let d = Date()
    print(d)
    //2020-12-18 07:54:13 +0000
    print(c.dateInterval(of: .hour, for: Date()))
    //Optional(2020-12-18 07:00:00 +0000 to 2020-12-18 08:00:00 +0000)
    
    • 指定日期的小组件位于大组件的排序位置
    public func ordinality(of smaller: Calendar.Component, in larger: Calendar.Component,
                          for date: Date) -> Int?
    

    如下,2020-12-18 08:33:00 +0000在上海时区的时间为16:33:00,返回的17指的是在.hour下16时为当天的第17个小时,0时为第1个小时。

    let c = Calendar(identifier: .gregorian)
    let d = Date()
    print(d)
    //2020-12-18 08:33:00 +0000
    print(c.ordinality(of: .hour, in: .day, for: d))
    //Optional(17)
    
    1. 日期的修改
    • 通过日期组件
    public func date(byAdding components: DateComponents, to date: Date,
                     wrappingComponents: Bool = false) -> Date?
    

    给日期增加时间组件的各组件值,获得一个新日期。

    wrappingComponents为true时,当组件值超时最大限制时,会减去其最大值。
    如下,当给当前日期增加了25小时后,实际上只增加了1小时。

    let c = Calendar(identifier: .gregorian)
    let d = Date()
    print(d)
    //2020-12-18 08:51:51 +0000
    print(c.date(byAdding: DateComponents(hour: 25), to: d, wrappingComponents: true))
    Optional(2020-12-18 09:51:51 +0000)
    
    • 通过日历组件
    public func date(byAdding component: Calendar.Component, value: Int, to date: Date,
                     wrappingComponents: Bool = false) -> Date?
    

    与上面方法不同的是,该方法只能一次更改单个组件的值,而上面方法可以同时更改多个组件的值。
    wrappingComponents的作用也与上面方法一致。

    let c = Calendar(identifier: .gregorian)
    let d = Date()
    print(d)
    //2020-12-19 06:08:25 +0000
    print(c.date(byAdding: .hour, value: 30, to: d, wrappingComponents: true))
    //Optional(2020-12-19 12:08:25 +0000)
    
    1. 日期组件
    • 日期组件转换为日期
    public func date(from components: DateComponents) -> Date?
    
    let c = Calendar(identifier: .gregorian)
    print(c.date(from: DateComponents(year: 2020, month: 12, day: 12,
                                      hour: 12, minute: 12, second: 12)))
    //Optional(2020-12-12 04:12:12 +0000)
    
    • 通过日历组件将日期转换为日期组件
    public func dateComponents(_ components: Set<Calendar.Component>,
                               from date: Date) -> DateComponents
    

    该方法会获取属性不完整的日期组件。

    let c = Calendar(identifier: .gregorian)
    print(c.dateComponents([.year, .month, .day, .hour, .minute, .second], from: Date()))
    //year: 2020 month: 12 day: 19 hour: 14 minute: 20 second: 52 isLeapMonth: false 
    
    • 通过时区将日期转换为日期组件
    public func dateComponents(in timeZone: TimeZone, from date: Date) -> DateComponents
    

    该方法会获取属性完整的日期组件。

    let c = Calendar(identifier: .gregorian)
    print(c.dateComponents(in: TimeZone.current, from: Date()))
    //calendar: gregorian (fixed) timeZone: Asia/Shanghai (current) era: 1 year: 2020 
    //month: 12 day: 19 hour: 14 minute: 24 second: 56 nanosecond: 984720945 
    //weekday: 7 weekdayOrdinal: 3 quarter: 0 weekOfMonth: 3 weekOfYear: 51 
    //yearForWeekOfYear: 2020 isLeapMonth: false 
    
    • 获取两个日期的差
    public func dateComponents(_ components: Set<Calendar.Component>, from start: Date,
                               to end: Date) -> DateComponents
    

    差值通过日期组件展示。

    let c = Calendar(identifier: .gregorian)
    print(c.dateComponents([.hour, .minute, .second], from: Date(), to: Date()+10000))
    //hour: 2 minute: 46 second: 40 isLeapMonth: false 
    
    • 获取两个日期组件的差
    public func dateComponents(_ components: Set<Calendar.Component>, 
         from start: DateComponents, to end: DateComponents) -> DateComponents
    

    差值通过日期组件展示。

    let c = Calendar(identifier: .gregorian)
    print(c.dateComponents([.hour, .minute, .second], from: DateComponents(hour: 10), 
                           to: DateComponents(hour: 12, minute: 12, second: 12)))
    //hour: 2 minute: 12 second: 12 isLeapMonth: false 
    
    • 获取指定日期对应日历组件的值
    public func component(_ component: Calendar.Component, from date: Date) -> Int
    
    let c = Calendar(identifier: .gregorian)
    print(Date())
    //2020-12-19 06:43:03 +0000
    print(c.component(.hour, from: Date()))
    //14
    
    1. 日期的运算
    • 获取指定日期的开始日期
    public func startOfDay(for date: Date) -> Date
    
    let c = Calendar(identifier: .gregorian)
    print(Date())
    //2020-12-19 06:44:32 +0000
    print(c.startOfDay(for: Date()))
    //2020-12-18 16:00:00 +0000
    
    • 比较两日期对应组件值的大小
    public func compare(_ date1: Date, to date2: Date, 
                        toGranularity component: Calendar.Component) -> ComparisonResult
    
    public enum ComparisonResult : Int {    
        case orderedAscending = -1//升序
        case orderedSame = 0//相等
        case orderedDescending = 1//降序
    }
    

    注:并不是单独比较组件的值。比如.second时,2分1秒也会大于1分59秒。实际上是将日期转换为秒,再比较大小。

    let c = Calendar(identifier: .gregorian)
    print(c.compare(Date(), to: Date()+59, toGranularity: .second).rawValue)
    //-1
    
    • 两日期对应组件值是否相同
    public func isDate(_ date1: Date, equalTo date2: Date, 
                       toGranularity component: Calendar.Component) -> Bool
    

    注:并不是单独比较组件的值。比如.second时,2分1秒也会大于1分1秒。实际上是将日期转换为秒,再比较大小。

    let c = Calendar(identifier: .gregorian)
    print(c.isDate(Date(), equalTo: Date()+60, toGranularity: .second))
    //false
    
    • 两日期是否在同一天
    public func isDate(_ date1: Date, inSameDayAs date2: Date) -> Bool
    
    • 指定日期是否属于今天
    public func isDateInToday(_ date: Date) -> Bool
    
    • 指定日期是否属于昨天
    public func isDateInYesterday(_ date: Date) -> Bool
    
    • 指定日期是否属于明天
    public func isDateInTomorrow(_ date: Date) -> Bool
    
    • 指定日期是否属于周末
    public func isDateInWeekend(_ date: Date) -> Bool
    

    注意:有些地区的周末并不是周六与周日。

    • 获取指定日期的周末开始日期与持续时间
    public func dateIntervalOfWeekend(containing date: Date, start: inout Date,
                                      interval: inout TimeInterval) -> Bool
    

    若能获取到周末,该方法返回true。

    如下,因为今天是周六,因此周末是从今天开始,持续172800.0秒,也就是2天。

    let c = Calendar(identifier: .gregorian)
    var d = Date()
    var i = 0.0
    print(d, i)
    //2020-12-19 07:14:33 +0000 0.0
    print(c.dateIntervalOfWeekend(containing: Date(), start: &d, interval: &i))
    //true
    print(d, i)
    //2020-12-18 16:00:00 +0000 172800.0
    
    • 获取指定日期的周末的范围
    public func dateIntervalOfWeekend(containing date: Date) -> DateInterval?
    

    功能与上面方法相同,只不过直接返回日期间隔。

    let c = Calendar(identifier: .gregorian)
    print(c.dateIntervalOfWeekend(containing: Date()))
    //Optional(2020-12-18 16:00:00 +0000 to 2020-12-20 16:00:00 +0000)
    
    • 返回指定日期的下一个周末开始日期与持续时间
    public func nextWeekend(startingAfter date: Date, start: inout Date,
         interval: inout TimeInterval, direction: Calendar.SearchDirection = .forward) -> Bool
    
    public enum SearchDirection {
            case forward//查询日期更大的方向,下周末
            case backward//查询日期更大的方向,上周末
    }
    

    若能获取到周末,该方法返回true。

    功能与dateIntervalOfWeekend方法大致相同。
    不同在于,如下,因为今天是周六,nextWeekend会获取到下一个星期的周末,而dateIntervalOfWeekend会获取到本周末。

    let c = Calendar(identifier: .gregorian)
    var d = Date()
    var i = 0.0
    print(d, i)
    //2020-12-19 07:35:25 +0000 0.0
    print(c.dateIntervalOfWeekend(containing: Date(), start: &d, interval: &i))
    //true
    print(d, i)
    //2020-12-18 16:00:00 +0000 172800.0
    print(c.nextWeekend(startingAfter: Date(), start: &d, interval: &i))
    //true
    print(d, i)
    //2020-12-25 16:00:00 +0000 172800.0
    
    • 获取指定日期的下一个周末的范围
    public func nextWeekend(startingAfter date: Date, 
                            direction: Calendar.SearchDirection = .forward) -> DateInterval?
    

    功能与上面方法相同,只不过直接返回日期间隔。

    let c = Calendar(identifier: .gregorian)
    print(Date())
    //2020-12-19 07:42:47 +0000
    print(c.nextWeekend(startingAfter: Date()))
    //Optional(2020-12-25 16:00:00 +0000 to 2020-12-27 16:00:00 +0000)
    
    1. 日期的匹配
    • 匹配满足日期组件(或最接近)的日期
    public func enumerateDates(startingAfter start: Date, 
                               matching components: DateComponents, 
                               matchingPolicy: Calendar.MatchingPolicy, 
                               repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, 
                               direction: Calendar.SearchDirection = .forward, 
                               using block: (Date?, Bool, inout Bool) -> Void)
    

    如果不可能完全匹配,并且matchingPolicystrict,则会将nil传递给闭包,并且枚举结束。
    逻辑上,strict匹配将无限期地搜索到将来,但如果找不到匹配项,则继续进行枚举并没有意义。
    若已成功匹配日期,可通过在闭包中将inout Bool值设置为true来停止枚举并函数返回。
    匹配策略

    public enum MatchingPolicy {
            //如果DateComponents的下一个更高组件没有匹配的时间,算法将返回存在的现有时间。
            //例如,在夏令时过渡时间可能没有2:37 am,那么结果将是3:00 am。
            case nextTime
    
            //与nextTime相同,但会保留下一个组件值。
            //例如,在夏令时过渡时间可能没有2:37 am,那么结果将是3:37 am。
            case nextTimePreservingSmallerComponents
    
            //与nextTime相同,但会减小组件值和保留下一个组件值。
            //例如,在夏令时过渡时间可能没有2:37 am,那么结果将是1:37 am。
            case previousTimePreservingSmallerComponents
    
            //精确查询匹配项
            //例如,在公历中搜索2月29日,上面的三个枚举选项可能改为选择3月1日(如果年份不是闰年)。
            case strict
    }
    

    结果选择策略

    public enum RepeatedTimePolicy {
            //有多个匹配结果时,将返回第一个结果
            case first
            //有多个匹配结果时,将返回最后一个结果
            case last
    }
    

    查询方向

    public enum SearchDirection {
            case forward//向日期更大的方向查询
            case backward//向日期更小的方向查询
    }
    

    闭包

     (Date?, Bool, inout Bool) -> Void
    

    Date?为匹配到的日期。
    Bool为true时,表示此日期为完全匹配日期组件。
    inout Bool赋值为true后,会停止遍历。

    let c = Calendar(identifier: .gregorian)
    c.enumerateDates(startingAfter: Date(), matching: DateComponents(month: 2, day: 29), 
            matchingPolicy: .nextTime, repeatedTimePolicy: .first) { (a, b, c) in
                let formatter = DateFormatter()
                formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
                print(formatter.string(from: a!), b , c)
                if b {
                    //b为true表示完全匹配日期组件
                    //matchingPolicy为strict时,所有结果的b都为true
                    c = true//设置为true可停止遍历
                }
            }
    
    //2021-03-01 00:00:00 false false
    //2022-03-01 00:00:00 false false
    //2023-03-01 00:00:00 false false
    //2024-02-29 00:00:00 true false
    
    • 获取第一个满足日期组件(或最接近)的日期
    public func nextDate(after date: Date, matching components: DateComponents, 
                         matchingPolicy: Calendar.MatchingPolicy, 
                         repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first, 
                         direction: Calendar.SearchDirection = .forward) -> Date?
    

    功能与enumerateDates方法相同,但只返回第一个匹配的日期。

    let a = c.nextDate(after: Date(), matching: DateComponents(month: 2, day: 29), 
              matchingPolicy: .nextTime, repeatedTimePolicy: .first, direction: .forward)
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    print(formatter.string(from: a!))
    
    • 日期是否匹配日期组件
    public func date(_ date: Date, matchesComponents components: DateComponents) -> Bool
    
    let c = Calendar(identifier: .gregorian)
    print(Date())
    //2020-12-21 07:53:08 +0000
    print(c.date(Date(), matchesComponents: DateComponents(month: 12, day: 21)))
    //true
    
    1. 日期的更改
    • 通过组件值更改日期
    public func date(bySetting component: Calendar.Component, value: Int,
                                                              of date: Date) -> Date?
    

    枚举参数可查看enumerateDates方法。
    更改组件值时通常会要求同时更高或耦合的组件。例如,将weekday设置为“星期四”的通常还要求更改day的组件值,并且可能还要求更改monthyear
    如果不存在满足条件的日期,此时会返回下一个可用日期。

    当更改组件值时,更小组件值会重置。例如将month变成3,那么结果会变成03-01 00:00:00

    let c = Calendar(identifier: .gregorian)
    print(Date())
    //2020-12-21 07:29:09 +0000
    let d = c.date(bySetting: .month, value: 3, of: Date())
    //Optional(2021-02-28 16:00:00 +0000)
    print(d)
    
    • 通过时分秒更改日期
    public func date(bySettingHour hour: Int, minute: Int, second: Int, of date: Date,
                     matchingPolicy: Calendar.MatchingPolicy = .nextTime, 
                     repeatedTimePolicy: Calendar.RepeatedTimePolicy = .first,
                     direction: Calendar.SearchDirection = .forward) -> Date?
    

    枚举参数可查看enumerateDates方法。
    如果不存在满足条件的日期,此时会返回下一个可用日期。

    let c = Calendar(identifier: .gregorian)
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
    print(formatter.string(from: Date()))
    //2020-12-21 15:48:26
    let a = c.date(bySettingHour: 11, minute: 12, second: 13, of: Date())
    print(formatter.string(from: a!))
    //2020-12-21 11:12:13
    

    相关文章

      网友评论

        本文标题:swift Calendar

        本文链接:https://www.haomeiwen.com/subject/uwwxnktx.html