DateComponents 是 Foundation 框架中的一个结构体,用于表示一个或多个日期和时间的组成部分(比如年、月、日、小时、分钟等)。它常与 Calendar 和 Date 配合使用,以便对日期和时间进行创建、提取、比较和计算。
基本用法
1、空组件初始化
var components = DateComponents()
dc.year = 2025
dc.month = 12
dc.day = 8
2、指定字段初始化
let components = DateComponents(year: 2024, month: 12, day: 14, hour: 10, minute: 30)
常用属性
1、isValidDate
判断是否能组成日期
var dc = DateComponents(year: 2024, month: 2, day: 30)
dc.calendar = Calendar.current
print(dc.isValidDate) // false
注:判断依据实际上是根据calendar来判断的,如果有calendar,可以组成日期(默认值为0001-01-01 00:00:00 +0000)。
2、isLeapMonth
判断是否为闰月,只在某些历法中返回true。
print(dc.isLeapMonth) // false
常用方法
1、date()
使用calendar组合成Date。
var dc = DateComponents()
dc.year = 2024
dc.month = 1
dc.day = 1
dc.calendar = Calendar.current
let d = dc.date() // 转换成 Date?
2、isValidDate(in:)
传入自定义Calendar。
dc.isValidDate(in: Calendar(identifier: .gregorian))
配合Calendar使用
1、创建日期
通过 DateComponents 创建一个指定的日期,例如生日、事件日期等。
let calendar = Calendar.current
let components = DateComponents(year: 2024, month: 12, day: 14, hour: 10, minute: 30)
if let date = calendar.date(from: components) {
print(date) // 转换成功后的具体日期
}
2、提取时间部分
从 Date 中只提取时间部分(时分秒),并忽略日期。
let now = Date()
let timeComponents = Calendar.current.dateComponents([.hour, .minute, .second], from: now)
if let hour = timeComponents.hour, let minute = timeComponents.minute {
print("Current time: \(hour):\(minute)")
}
3、计算日期间的差异
通过 Calendar 的 dateComponents(_:from:to:) 方法,可以计算两个日期之间的差异,返回一个 DateComponents。
let calendar = Calendar.current
let startDate = Date()
let endDate = calendar.date(byAdding: .day, value: 5, to: startDate)!
let difference = calendar.dateComponents([.day, .hour], from: startDate, to: endDate)
print("Difference: \(difference.day ?? 0) days, \(difference.hour ?? 0) hours")
4、加减日期
通过 Calendar 的 date(byAdding:to:) 方法,可以使用 DateComponents 调整日期。
例如,月份+1:
let next = Calendar.current.date(byAdding: DateComponents(month: 1), to: Date())
注意事项
1、不完整的 DateComponents
DateComponents 可能包含部分组件(比如只有 year 和 month),在使用时需确保其包含的字段是完整且有效的,否则使用默认值(0001-01-01 00:00:00 +0000)。
如果缺失calendar,则无法正常使用date()方法创建日期。
2、与时区相关
如果使用 DateComponents 创建 Date,它会使用当前 Calendar 的时区。如果需要自定义时区,需提前设置 Calendar 的 timeZone。
var calendar: Calendar {
var c = Calendar.current
c.timeZone = TimeZone(identifier: "UTC")!
return c
}
总结
DateComponents 可以表示不完整的时间信息,比如 “2024 年”、“6 月” 或 “某一天的 14 点”。
因为Date 表示具体的时间点,但它本身无法直接操作年、月、日等组件。使用DateComponents,可以构建一个特定的时间,用于创建、提取、比较和计算日期与时间。
它与 Calendar 和 Date 紧密结合,通常用于构造日期、解析日期、加减日期。
DateComponents需要借助Calendar生成日期,例如date()方法的内部使用的是:
returnedDate = calendar.date(from: self)
默认使用的是:
Calendar.current
也就是说,调用DateComponents的date()方法,等同于:
Calendar.current.date(from: components)
DateComponents只负责存储字段,解析日期、拆解日期、组合日期等工作都需要Calendar实现。
相关文章
2、Swift日期时间类Calendar:https://fangjunyu.com/2024/12/14/swift%e6%97%a5%e6%9c%9f%e6%97%b6%e9%97%b4%e7%b1%bbcalendar/
扩展知识
1、DateComponents结构体
Swift定义了唯一的指定初始化器:
struct DateComponents {
// MARK: Core fields (Optional Int)
var era: Int?
var year: Int?
var month: Int?
var day: Int?
var hour: Int?
var minute: Int?
var second: Int?
var nanosecond: Int?
// MARK: Week-based fields
var weekday: Int?
var weekdayOrdinal: Int?
var weekOfMonth: Int?
var weekOfYear: Int?
var yearForWeekOfYear: Int?
// MARK: Extra
var quarter: Int?
// MARK: Locale and calendar context
var calendar: Calendar?
var timeZone: TimeZone?
// MARK: Computed info
var isLeapMonth: Bool?
var isValidDate: Bool { get }
var isValidDate(in: Calendar) -> Bool
// MARK: Duration conversion
func date() -> Date? // 需要 calendar 字段非空
}
字段解析:
1、era:纪元编号,0表示公元前(BC),1表示公元(AD),特殊历法(日本历法、印度历法的era则不同)。
2、year、month、day、hour、minute、second、nanosecond:年、月、日、时、分、秒和钠秒,1秒=10亿纳秒。
3、weekday:星期几(1=星期日,2=星期一,以此类推,取决于Calendar.firstWeekday),范围1-7。
4、weekdayOrdinal:当月的第几个某星期,第二个星期三。
5、weekOfMonth:月内的第几周,范围:1-5。
6、weekOfYear:年内的第几周,范围1-53。
7、yearForWeekOfYear:ISO 周历使用,常用于国际周号。
8、quarter:季度,对应四个季度,范围1-4。
9、calendar:日历,对应gregorian、iso8601等日历,没有calendar无法使用date()、isValidDate。
10、timeZone:时区,例如UTC。
11、isLeapMonth:是否为闰月(农历十月),只在某些历法中返回true。
12、isValidDate:根据Calendar判断是否为有效日期。
13、isValidDate(in:):传入自定义Calendar。
14、date():使用calendar组合成Date。
注意事项:weekdayOrdinal和weekOfMonth的区别在于weekdayOrdinal是按第一个周来计算的(包括前面的空格),weekOfMonth则按照完整的周进行计算。


