Reputation: 1606
The example code below gets DateComponents from the current Date, modifies the components, and creates a new Date from the modified components. It also shows creating a new DateComponents object, filling it out, and then creating a new Date from that.
import Foundation
let utcHourOffset = -7.0
let tz = TimeZone(secondsFromGMT: Int(utcHourOffset*60.0*60.0))!
let calendar = Calendar(identifier: .gregorian)
var now = calendar.dateComponents(in: tz, from: Date())
// Get and display current date
print("\nCurrent Date:")
print("\(now.month!)/\(now.day!)/\(now.year!) \(now.hour!):\(now.minute!):\(now.second!) \(now.timeZone!)")
let curDate = calendar.date(from: now)
print("\(curDate!)")
// Modify and display current date
now.year = 2010
now.month = 2
now.day = 24
now.minute = 0
print("\nModified Date:")
print("\(now.month!)/\(now.day!)/\(now.year!) \(now.hour!):\(now.minute!):\(now.second!) \(now.timeZone!)")
let modDate = calendar.date(from: now)
print("\(modDate!)")
// Create completely new date
var dc = DateComponents()
dc.year = 2014
dc.month = 12
dc.day = 25
dc.hour = 10
dc.minute = 12
dc.second = 34
print("\nNew Date:")
print("\(dc.month!)/\(dc.day!)/\(dc.year!) \(dc.hour!):\(dc.minute!):\(dc.second!) \(now.timeZone!)")
let newDate = calendar.date(from: dc)
print("\(newDate!)")
In the case where I modify the components, setting different year, month, day, etc., then use the components to get a date, I get the unexpected result that the new date has all the modified components except the year, which remains unchanged.
In the case where I create a DateComponents object and fill it out then create a Date from it, it works as expected.
The output of the code is shown below:
Current Date:
3/9/2017 19:5:30 GMT-0700 (fixed)
2017-03-10 02:05:30 +0000
Modified Date:
2/24/2010 19:0:30 GMT-0700 (fixed)
2017-02-25 02:00:30 +0000
New Date:
12/25/2014 10:12:34 GMT-0700 (fixed)
2014-12-25 17:12:34 +0000
I expected the modified Date to be 2010-02-25 02:00:30 +0000
rather than 2017-02-25 02:00:30 +0000
. Why isn't it? Why does it work in the second case?
The docs for DateComponents say: "An instance of NSDateComponents is not responsible for answering questions about a date beyond the information with which it was initialized...". Being that the DateComponents object was initialized with a year, it doesn't seem like this would apply, but it's the only thing I saw in the docs that might explain the behavior I observe.
Upvotes: 1
Views: 468
Reputation: 318794
If you log now
and dc
you will see the problem. now
is being created from a Date
. This fills in all of the date components including yearForWeekOfYear
and several of the weekday related components. These components are causing modDate
to come out incorrectly.
newDate
works as expected because only the specific components are being set.
You can get modDate
to come out correctly if you reset some of the extra components. Specifically, adding:
now.yearForWeekOfYear = nil
just before creating modDate
will result in the expected date for modDate
. Of course the best solution is to create a new instance of DateComponents
and use specific values from the previous DateComponents
as needed:
let mod = DateComponents()
mod.timeZone = now.timeZone
mod.year = 2010
mod.month = 2
mod.day = 24
mod.hour = now.hour
mod.minute = 0
mod.second = now.second
print("\nModified Date:")
print("\(mod.month!)/\(mod.day!)/\(mod.year!) \(mod.hour!):\(mod.minute!):\(mod.second!) \(mod.timeZone!)")
let modDate = calendar.date(from: mod)
print("\(modDate!)")
Upvotes: 3