blob: 9cde6aba1064afca74647418e0f66300c4fda9f4 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import CoreFoundation
/**
`DateComponents` encapsulates the components of a date in an extendable, structured manner.
It is used to specify a date by providing the temporal components that make up a date and time in a particular calendar: hour, minutes, seconds, day, month, year, and so on. It can also be used to specify a duration of time, for example, 5 hours and 16 minutes. A `DateComponents` is not required to define all the component fields.
When a new instance of `DateComponents` is created, the date components are set to `nil`.
*/
public struct DateComponents : ReferenceConvertible, Hashable, Equatable, _MutableBoxing {
public typealias ReferenceType = NSDateComponents
internal var _handle: _MutableHandle<NSDateComponents>
/// Initialize a `DateComponents`, optionally specifying values for its fields.
public init(calendar: Calendar? = nil,
timeZone: TimeZone? = nil,
era: Int? = nil,
year: Int? = nil,
month: Int? = nil,
day: Int? = nil,
hour: Int? = nil,
minute: Int? = nil,
second: Int? = nil,
nanosecond: Int? = nil,
weekday: Int? = nil,
weekdayOrdinal: Int? = nil,
quarter: Int? = nil,
weekOfMonth: Int? = nil,
weekOfYear: Int? = nil,
yearForWeekOfYear: Int? = nil) {
_handle = _MutableHandle(adoptingReference: NSDateComponents())
if let _calendar = calendar { self.calendar = _calendar }
if let _timeZone = timeZone { self.timeZone = _timeZone }
if let _era = era { self.era = _era }
if let _year = year { self.year = _year }
if let _month = month { self.month = _month }
if let _day = day { self.day = _day }
if let _hour = hour { self.hour = _hour }
if let _minute = minute { self.minute = _minute }
if let _second = second { self.second = _second }
if let _nanosecond = nanosecond { self.nanosecond = _nanosecond }
if let _weekday = weekday { self.weekday = _weekday }
if let _weekdayOrdinal = weekdayOrdinal { self.weekdayOrdinal = _weekdayOrdinal }
if let _quarter = quarter { self.quarter = _quarter }
if let _weekOfMonth = weekOfMonth { self.weekOfMonth = _weekOfMonth }
if let _weekOfYear = weekOfYear { self.weekOfYear = _weekOfYear }
if let _yearForWeekOfYear = yearForWeekOfYear { self.yearForWeekOfYear = _yearForWeekOfYear }
}
// MARK: - Properties
/// Translate from the NSDateComponentUndefined value into a proper Swift optional
private func _getter(_ x : Int) -> Int? { return x == NSDateComponentUndefined ? nil : x }
/// Translate from the proper Swift optional value into an NSDateComponentUndefined
private func _setter(_ x : Int?) -> Int { if let xx = x { return xx } else { return NSDateComponentUndefined } }
/// The `Calendar` used to interpret the other values in this structure.
///
/// - note: API which uses `DateComponents` may have different behavior if this value is `nil`. For example, assuming the current calendar or ignoring certain values.
public var calendar: Calendar? {
get { return _handle.map { $0.calendar } }
set { _applyMutation { $0.calendar = newValue } }
}
/// A time zone.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var timeZone: TimeZone? {
get { return _handle.map { $0.timeZone } }
set { _applyMutation { $0.timeZone = newValue } }
}
/// An era or count of eras.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var era: Int? {
get { return _handle.map { _getter($0.era) } }
set { _applyMutation { $0.era = _setter(newValue) } }
}
/// A year or count of years.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var year: Int? {
get { return _handle.map { _getter($0.year) } }
set { _applyMutation { $0.year = _setter(newValue) } }
}
/// A month or count of months.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var month: Int? {
get { return _handle.map { _getter($0.month) } }
set { _applyMutation { $0.month = _setter(newValue) } }
}
/// A day or count of days.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var day: Int? {
get { return _handle.map { _getter($0.day) } }
set { _applyMutation { $0.day = _setter(newValue) } }
}
/// An hour or count of hours.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var hour: Int? {
get { return _handle.map { _getter($0.hour) } }
set { _applyMutation { $0.hour = _setter(newValue) } }
}
/// A minute or count of minutes.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var minute: Int? {
get { return _handle.map { _getter($0.minute) } }
set { _applyMutation { $0.minute = _setter(newValue) } }
}
/// A second or count of seconds.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var second: Int? {
get { return _handle.map { _getter($0.second) } }
set { _applyMutation { $0.second = _setter(newValue) } }
}
/// A nanosecond or count of nanoseconds.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var nanosecond: Int? {
get { return _handle.map { _getter($0.nanosecond) } }
set { _applyMutation { $0.nanosecond = _setter(newValue) } }
}
/// A weekday or count of weekdays.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var weekday: Int? {
get { return _handle.map { _getter($0.weekday) } }
set { _applyMutation { $0.weekday = _setter(newValue) } }
}
/// A weekday ordinal or count of weekday ordinals.
/// Weekday ordinal units represent the position of the weekday within the next larger calendar unit, such as the month. For example, 2 is the weekday ordinal unit for the second Friday of the month.///
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var weekdayOrdinal: Int? {
get { return _handle.map { _getter($0.weekdayOrdinal) } }
set { _applyMutation { $0.weekdayOrdinal = _setter(newValue) } }
}
/// A quarter or count of quarters.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var quarter: Int? {
get { return _handle.map { _getter($0.quarter) } }
set { _applyMutation { $0.quarter = _setter(newValue) } }
}
/// A week of the month or a count of weeks of the month.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var weekOfMonth: Int? {
get { return _handle.map { _getter($0.weekOfMonth) } }
set { _applyMutation { $0.weekOfMonth = _setter(newValue) } }
}
/// A week of the year or count of the weeks of the year.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var weekOfYear: Int? {
get { return _handle.map { _getter($0.weekOfYear) } }
set { _applyMutation { $0.weekOfYear = _setter(newValue) } }
}
/// The ISO 8601 week-numbering year of the receiver.
///
/// The Gregorian calendar defines a week to have 7 days, and a year to have 356 days, or 366 in a leap year. However, neither 356 or 366 divide evenly into a 7 day week, so it is often the case that the last week of a year ends on a day in the next year, and the first week of a year begins in the preceding year. To reconcile this, ISO 8601 defines a week-numbering year, consisting of either 52 or 53 full weeks (364 or 371 days), such that the first week of a year is designated to be the week containing the first Thursday of the year.
///
/// You can use the yearForWeekOfYear property with the weekOfYear and weekday properties to get the date corresponding to a particular weekday of a given week of a year. For example, the 6th day of the 53rd week of the year 2005 (ISO 2005-W53-6) corresponds to Sat 1 January 2005 on the Gregorian calendar.
/// - note: This value is interpreted in the context of the calendar in which it is used.
public var yearForWeekOfYear: Int? {
get { return _handle.map { _getter($0.yearForWeekOfYear) } }
set { _applyMutation { $0.yearForWeekOfYear = _setter(newValue) } }
}
/// Set to true if these components represent a leap month.
public var isLeapMonth: Bool? {
get { return _handle.map { $0.leapMonthSet ? $0.isLeapMonth : nil } }
set {
_applyMutation {
// Technically, the underlying class does not support setting isLeapMonth to nil, but it could - so we leave the API consistent.
if let b = newValue {
$0.isLeapMonth = b
} else {
$0.isLeapMonth = false
}
}
}
}
/// Returns a `Date` calculated from the current components using the `calendar` property.
public var date: Date? {
if let d = _handle.map({$0.date}) {
return d as Date
} else {
return nil
}
}
// MARK: - Generic Setter/Getters
/// Set the value of one of the properties, using an enumeration value instead of a property name.
///
/// The calendar and timeZone and isLeapMonth properties cannot be set by this method.
public mutating func setValue(_ value: Int?, for component: Calendar.Component) {
_applyMutation {
$0.setValue(_setter(value), forComponent: Calendar._toCalendarUnit([component]))
}
}
/// Returns the value of one of the properties, using an enumeration value instead of a property name.
///
/// The calendar and timeZone and isLeapMonth property values cannot be retrieved by this method.
public func value(for component: Calendar.Component) -> Int? {
return _handle.map {
$0.value(forComponent: Calendar._toCalendarUnit([component]))
}
}
// MARK: -
/// Returns true if the combination of properties which have been set in the receiver is a date which exists in the `calendar` property.
///
/// This method is not appropriate for use on `DateComponents` values which are specifying relative quantities of calendar components.
///
/// Except for some trivial cases (e.g., 'seconds' should be 0 - 59 in any calendar), this method is not necessarily cheap.
///
/// If the time zone property is set in the `DateComponents`, it is used.
///
/// The calendar property must be set, or the result is always `false`.
public var isValidDate: Bool {
return _handle.map { $0.isValidDate }
}
/// Returns true if the combination of properties which have been set in the receiver is a date which exists in the specified `Calendar`.
///
/// This method is not appropriate for use on `DateComponents` values which are specifying relative quantities of calendar components.
///
/// Except for some trivial cases (e.g., 'seconds' should be 0 - 59 in any calendar), this method is not necessarily cheap.
///
/// If the time zone property is set in the `DateComponents`, it is used.
public func isValidDate(in calendar: Calendar) -> Bool {
return _handle.map { $0.isValidDate(in: calendar) }
}
// MARK: -
public var hashValue: Int {
return _handle.map { $0.hash }
}
public static func ==(lhs: DateComponents, rhs: DateComponents) -> Bool {
// Don't copy references here; no one should be storing anything
return lhs._handle._uncopiedReference().isEqual(rhs._handle._uncopiedReference())
}
// MARK: - Bridging Helpers
internal init(reference: NSDateComponents) {
_handle = _MutableHandle(reference: reference)
}
}
extension DateComponents : CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable {
public var description: String {
return _handle.map { $0.description }
}
public var debugDescription: String {
return _handle.map { $0.debugDescription }
}
public var customMirror: Mirror {
var c: [(label: String?, value: Any)] = []
if let r = calendar { c.append((label: "calendar", value: r)) }
if let r = timeZone { c.append((label: "timeZone", value: r)) }
if let r = era { c.append((label: "era", value: r)) }
if let r = year { c.append((label: "year", value: r)) }
if let r = month { c.append((label: "month", value: r)) }
if let r = day { c.append((label: "day", value: r)) }
if let r = hour { c.append((label: "hour", value: r)) }
if let r = minute { c.append((label: "minute", value: r)) }
if let r = second { c.append((label: "second", value: r)) }
if let r = nanosecond { c.append((label: "nanosecond", value: r)) }
if let r = weekday { c.append((label: "weekday", value: r)) }
if let r = weekdayOrdinal { c.append((label: "weekdayOrdinal", value: r)) }
if let r = quarter { c.append((label: "quarter", value: r)) }
if let r = weekOfMonth { c.append((label: "weekOfMonth", value: r)) }
if let r = weekOfYear { c.append((label: "weekOfYear", value: r)) }
if let r = yearForWeekOfYear { c.append((label: "yearForWeekOfYear", value: r)) }
if let r = isLeapMonth { c.append((label: "isLeapMonth", value: r)) }
return Mirror(self, children: c, displayStyle: Mirror.DisplayStyle.struct)
}
}
// MARK: - Bridging
extension DateComponents : _ObjectTypeBridgeable {
public static func _isBridgedToObjectiveC() -> Bool {
return true
}
public static func _getObjectiveCType() -> Any.Type {
return NSDateComponents.self
}
@_semantics("convertToObjectiveC")
public func _bridgeToObjectiveC() -> NSDateComponents {
return _handle._copiedReference()
}
public static func _forceBridgeFromObjectiveC(_ dateComponents: NSDateComponents, result: inout DateComponents?) {
if !_conditionallyBridgeFromObjectiveC(dateComponents, result: &result) {
fatalError("Unable to bridge \(DateComponents.self) to \(self)")
}
}
public static func _conditionallyBridgeFromObjectiveC(_ dateComponents: NSDateComponents, result: inout DateComponents?) -> Bool {
result = DateComponents(reference: dateComponents)
return true
}
public static func _unconditionallyBridgeFromObjectiveC(_ source: NSDateComponents?) -> DateComponents {
var result: DateComponents? = nil
_forceBridgeFromObjectiveC(source!, result: &result)
return result!
}
}