Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Calendar Date By Setting Hour To 0 Changes Day

Tags:

swift

nsdate

If I run the following code:

let date = Date()
let midnight = Calendar.current.date(bySetting: .hour, value: 0, of: date)!
let difference = Int64(midnight.timeIntervalSince(date))
print(String(difference))

Difference is a positive value of about 13 hours (since the local time is about 11 AM now). However, I would expect that the difference would be negative 11 hours; since I'm setting the .hour value to 0 I expect that time would move backward, not forward.

I would expect to have to find midnight tomorrow with this code:

let midnight = Calendar.current.date(bySetting: .hour, value: 0, of:
   Calendar.current.date(byAdding: .day, value: 1, to: date)!)!

But that gives +37 hours instead of +13.

The current behavior is actually the behavior I want (I want to know the time until midnight tomorrow). But, I would like to understand why this is happening, and in particular I want to make sure that this approach of finding midnight tomorrow will work regardless of time zone. Thanks!

EDIT: Note, I only care about the hours until midnight, which is why I don't change the minute or second value when finding midnight.

like image 564
kmell96 Avatar asked Dec 24 '22 08:12

kmell96


2 Answers

I think this is expected as it says in the documentation,

The algorithm will try to produce a result which is in the next-larger component to the one given (there’s a table of this mapping at the top of this document). So for the “set to Thursday” example, find the Thursday in the Week in which the given date resides (which could be a forwards or backwards move, and not necessarily the nearest Thursday). For more control over the exact behavior, use nextDate(after:matching:matchingPolicy:behavior:direction:).

Using nextDate(after date: Date, matching components: DateComponents, matchingPolicy: Calendar.MatchingPolicy, repeatedTimePolicy: Calendar.RepeatedTimePolicy = default, direction: Calendar.SearchDirection = default) -> Date? yields correct result as you would expect.

Example is here,

let calendar = Calendar.current
var hourComponent = DateComponents()
hourComponent.hour = 0

let midnightSameDay = calendar.nextDate(after: date,
                                        matching: hourComponent,
                                        matchingPolicy: .nextTime,
                                        direction: .backward)
print(mightnightSameDay) // 2018-04-19 00:00:00 +0000

let midnightNextDay = calendar.nextDate(after: date,
                                        matching: hourComponent,
                                        matchingPolicy: .nextTime,
                                        direction: .forward)
print(mightnightNextDay) // 2018-04-20 00:00:00 +0000

And there is yet another method that you could use for this which uses search direction and matching policy,

let midnightSameDay = calendar.date(bySettingHour: 0,
                                    minute: 0,
                                    second: 0,
                                    of: date,
                                    direction: .backward)
like image 151
Sandeep Avatar answered Dec 29 '22 10:12

Sandeep


date(bySetting:value:of) makes a best guess that is "implementation-defined" towards creating a new Date based on the new parameters. By setting the hour to 0 - sometimes this will create a Date on the same calendar day with the hour set accordingly, and sometimes the new Date will be tomorrow. It doesn't sound like that will give you the reliability that you need, but hopefully it answers your question.

The docs go further in depth:

https://developer.apple.com/documentation/foundation/calendar/2292915-date

like image 36
Andy Obusek Avatar answered Dec 29 '22 11:12

Andy Obusek