Date from week of year returning date not in week - ios

I have come across a rather strange "bug". When getting a date for a week of a year using this method:
let dates = NSMutableArray()
let cal = NSCalendar.currentCalendar()
cal.firstWeekday = 2
let formatter = NSDateFormatter()
formatter.dateFormat = "ww YYYY"
formatter.calendar = cal
let date = formatter.dateFromString(week as String)
println(date)
The string week is 52 2014, so the expected date would be Monday December 22th, but instead it returns Saturday December 20th, at 23:00. First of all, I thought I'd handled the first day of week by setting the firstWeekday-option of the calendar, but no luck. In addition, the date returned isn't even in week 52.
Just to double check I ran cal.components(NSCalendarUnit.WeekOfYearCalendarUnit, fromDate: date!).weekOfYear to double check I'm not an idiot, and no sir, the week for the date produced is 51, the week before the desired week.
Any idea how I can reach the expected result?

Any idea how I can reach the expected result?
What actually is your desired result? Do you want to know the first day of the week or the first day in the last day? Than you could tray this:
let now = NSDate()
var startDate: NSDate? = nil
var duration: NSTimeInterval = 0
let cal = NSCalendar.currentCalendar()
cal.firstWeekday = 2
cal.rangeOfUnit(.WeekCalendarUnit, startDate: &startDate, interval: &duration, forDate: now);
let endDate = startDate?.dateByAddingTimeInterval(duration)
print(startDate)
print(endDate)
it prints
"Optional(2014-12-21 23:00:00 +0000)"
"Optional(2014-12-28 23:00:00 +0000)"
the endDate is the first second that is not in the week anymore.
Note that the offset of 1 hour results from the fact that it is printed in UTC time, that is actually GMT winter time. Indeed these dates are 2014-12-22 00:00:00 and 2014-12-29 00:00:00 in my time zone (GMT+1)
or simply
let components = NSDateComponents()
components.weekOfYear = 52
components.weekday = 2
components.year = 2014
let cal = NSCalendar.currentCalendar()
let day = cal.dateFromComponents(components)
This code adapted to respect user's calendar:
let cal = NSCalendar.currentCalendar()
let components = NSDateComponents()
components.weekOfYear = 52
components.weekday = cal.firstWeekday
components.year = 2014

Changing the firstWeekday from 1 to 2 won't change the date, it will change just the First weekday from Sunday to Monday.
You can do it like this:
func dateFromWeekOfYear(year:Int, weekOfYear:Int, weekday:Int) -> NSDate {
return NSCalendar.currentCalendar().dateWithEra(1, yearForWeekOfYear: year, weekOfYear: weekOfYear, weekday: weekday, hour: 0, minute: 0, second: 0, nanosecond: 0)!
}
let date1 = dateFromWeekOfYear(2014, 52, 1) // Dec 21, 2014, 12:00 AM
let date2 = dateFromWeekOfYear(2014, 52, 2) // Dec 22, 2014, 12:00 AM
let date3 = dateFromWeekOfYear(2014, 52, 3) // Dec 23, 2014, 12:00 AM
If dealing with a string and you want to set he Stand Alone local day of week you can do it like this:
let myDate = "2 52 2014"
let cal = NSCalendar.currentCalendar()
let formatter = NSDateFormatter()
formatter.dateFormat = "c ww Y"
formatter.calendar = cal
if let date1 = formatter.dateFromString(myDate) {
date1 // "Dec 22, 2014, 12:00 AM"
}
If you need further reference you can use this:

Related

Check if NSDate is in current week not working for specific date?

I have tried couple of ways to check if NSDate is in current week:
Method 1
func isInThisWeek(date: Date) -> Bool
{
return Calendar.current.isDate(date, equalTo: Date(), toGranularity: .weekOfYear)
}
Method 2
func dateFallsInCurrentWeek(date: Date) -> Bool
{
let currentWeek = Calendar.current.component(Calendar.Component.weekOfYear, from: Date())
let datesWeek = Calendar.current.component(Calendar.Component.weekOfYear, from: date)
return (currentWeek == datesWeek)
}
Now here is the case where I am getting FALSE though this date is in current week.
I tested on: Monday, August 10, 2020 6:00:00 PM (My time zone: +5:30 GMT). So as per calendar, this date belongs to 10 Aug - 16 Aug week.
What may be wrong? In my iPad in which I am testing this, has starting day of Week is Monday as following:
All calendars would consider sunday as the first weekday. If you would like to consider monday as the start of your week you need to use iso8601 calendar.
let date = Date(timeIntervalSince1970: 1597516200) // .description // "2020-08-15 18:30:00 +0000"
let tz = TimeZone(secondsFromGMT: 5*3600 + 1800)! // GMT+0530 (fixed)
let components = Calendar.current.dateComponents(in: tz, from: date)
components.day // 16
components.weekday // Sunday
// Current Calendar starts on sunday so it goes from 9...15 and 16...22
// To get the current week starting on monday you need iso8601 calendar
let equalToDate = DateComponents(calendar: .current, timeZone: tz, year: 2020, month: 8, day: 10, hour: 18).date!
equalToDate.description // "2020-08-10 12:30:00 +0000"
Calendar(identifier: .iso8601).isDate(date, equalTo: equalToDate, toGranularity: .weekOfYear) // true

HKActivitySummary dateComponents a day behind

For some strange reason when executing a HKActivitySummaryQuery the returned date component for each summary is a day behind.
The query returns data from the correct date but the dateComponents date of the data is behind by a day. I've tried setting the timezone and locale but results remain the same.
Summary Model
struct ActivitySummary {
init?(_ summary: HKActivitySummary) {
var calendar = Calendar.current
calendar.timeZone = TimeZone.current
guard let date = summary.dateComponents(for: calendar).date else { return nil }
print("ORIGINAL: ", date.description(with: Locale.current))
//Expected: Tuesday, January 30, 2018 at 7:00:00 PM Eastern Standard Time
//Results: Monday, January 29, 2018 at 7:00:00 PM Eastern Standard Time
let other = calendar.dateComponents( [ .year, .month, .day ], from: date)
print("START OF DAY: ", date.startOfDay.description(with: Locale.current))
//Expected: Tuesday, January 30, 2018 at 12:00:00 AM Eastern Standard Time
//Results: Monday, January 29, 2018 at 12:00:00 AM Eastern Standard Time
}
}
HKAcitivitySummaryQuery
func summaryQuery(){
let predicate = HKQuery.predicate(forActivitySummariesBetweenStart: fromDate.components(), end: toDate!.components())
let query = HKActivitySummaryQuery(predicate: predicate) { (query, summaries, error) in
guard let summaries = summaries, summaries.count > 0 else {
return
}
//
var activitySummaries: [ActivitySummary] = []
activitySummaries = summaries.compactMap({
ActivitySummary($0)
})
}
}
Maybe the calendar you're working with is wrong. set your calendar like this :
let calendar = Calendar.current
Try manually setting the time zone for the DateComponents before converting them into a Date:
let calendar = Calendar.current
var dateComponents = summary.dateComponents(in: calendar)
dateComponents.timeZone = calendar.timeZone
let date = dateComponents.date!
The DateComponents associated with each HKActivitySummary do not include a time zone. The resulting Date may be slightly off from what you’re expecting because it’s not in the correct time zone (Swift defaults to UTC if a time zone is not specified). Manually specifying the time zone should resolve this issue.

How to calculate days difference between two NSDate objects in different time zones?

I have two NSDate which are initiated from UTC dates.
Lets call them (A & B)
I know that the A represents a day in China and B represents a day in USA. (I know the time zones.) How can I calculate the difference in days between the two...?
I have been using the following method which is obviously incorrect.
class func daysDifferenceIn(firstDate: NSDate, firstTimeZone: String, secondDate: NSDate, secondTimeZone: String) -> Int {
objc_sync_enter(self)
let firstDateComponents = NSCalendar.CommonCalendar.componentsInTimeZone(NSTimeZone(name: firstTimeZone)!, fromDate: firstDate)
let secondDateComponents = NSCalendar.CommonCalendar.componentsInTimeZone(NSTimeZone(name: secondTimeZone)!, fromDate: secondDate)
NSCalendar.CommonCalendar.timeZone = NSTimeZone(name: firstTimeZone)!
let firstCalDate = NSCalendar.CommonCalendar.dateFromComponents(firstDateComponents)
NSCalendar.CommonCalendar.timeZone = NSTimeZone(name: secondTimeZone)!
let secondCalDate = NSCalendar.CommonCalendar.dateFromComponents(secondDateComponents)
objc_sync_exit(self)
return firstCalDate!.numberOfDaysUntilDateTime(secondCalDate!)
}
func numberOfDaysUntilDateTime(toDateTime: NSDate, inTimeZone timeZone: NSTimeZone? = nil) -> Int {
let calendar = NSCalendar.CommonCalendar
if let timeZone = timeZone {
calendar.timeZone = timeZone
}
var fromDate: NSDate?, toDate: NSDate?
calendar.rangeOfUnit(.Day, startDate: &fromDate, interval: nil, forDate: self)
calendar.rangeOfUnit(.Day, startDate: &toDate, interval: nil, forDate: toDateTime)
let difference = calendar.components(.Day, fromDate: fromDate!, toDate: toDate!, options: [])
return difference.day
}
I can manually subtract day components from firstDateComponents and secondDateComponents which I don't want to do as I have to look for edge cases of 31 and 28 and so on.
Any help is appreciated. Thanks.
UPDATE
First date is 2017-02-10 16:15:00 +0000
Second date is 2017-02-11 03:20:00 +0000 Both are UTC.
firstTimeZone String "Asia/Shanghai"
secondTimeZone String "America/Los_Angeles"
So the day difference is -1 Day. Basically I am implementing flight status and you can see the following link as the flight lands 1 day prior to take day. As it flies from West to East.
https://www.google.co.in/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=ua+890
Also a description of Date components.
Printing description of firstDateComponents:
<NSDateComponents: 0x600000142310>
Calendar: <CFCalendar 0x60000088b6d0 [0x10c5d3df0]>{identifier = 'gregorian'}
TimeZone: Asia/Shanghai (GMT+8) offset 28800
Era: 1
Calendar Year: 2017
Month: 2
Leap month: no
Day: 11
Hour: 0
Minute: 15
Second: 0
Nanosecond: 0
Quarter: 0
Year for Week of Year: 2017
Week of Year: 6
Week of Month: 2
Weekday: 7
Weekday Ordinal: 2
Printing description of secondDateComponents:
<NSDateComponents: 0x60000014b8f0>
Calendar: <CFCalendar 0x60000049b620 [0x10c5d3df0]>{identifier = 'gregorian'}
TimeZone: America/Los_Angeles (PST) offset -28800
Era: 1
Calendar Year: 2017
Month: 2
Leap month: no
Day: 10
Hour: 19
Minute: 20
Second: 0
Nanosecond: 0
Quarter: 0
Year for Week of Year: 2017
Week of Year: 6
Week of Month: 2
Weekday: 6
Weekday Ordinal: 2
This is an odd case. You're looking for the difference in calendar dates between two Dates when those dates are evaluated in a specific time zone.
I did some playing, and came up with code that works for dates that fall in the same year:
let date = Date()
guard let nycTimeZone = TimeZone(abbreviation: "EST"),
let nzTimeZone = TimeZone(abbreviation: "NZDT") else {
fatalError()
}
var nycCalendar = Calendar(identifier: .gregorian)
nycCalendar.timeZone = nycTimeZone
var nzCalendar = Calendar(identifier: .gregorian)
nzCalendar.timeZone = nzTimeZone
let now = Date()
let nycDayOfYear = nycCalendar.ordinality(of: .day, in: .year, for: now)
var nzDayOfYear = nzCalendar.ordinality(of: .day, in: .year, for: now)
I'm using New York and Aukland, NZ as my time zones because as of the time of this writing, those zones are on different dates.
As of now (~12:00 PM on Feb 11, 2017 in US Eastern Standard Time (UTC - 5) the code above gives
nycDayOfYear = 42
and
nzDayOfYear = 43
It would take some work to make that calculation work across year boundaries.
Curiously, the following code:
var nzDayOfEra = nzCalendar.ordinality(of: .day, in: .era, for: now)
let nycDayOfEra = nycCalendar.ordinality(of: .day, in: .era, for: now)
Gives the same value for both NZ and NYC. I'm not sure why.
EDIT:
Ok, I did some experimenting and got code that works. What I do is to convert both dates to month/day/year date components using a calendar set to the local time zone for each time. Then I use a method dateComponents(_:from:to:) to calculate the difference between those 2 DateComponents, in days:
import UIKit
let date = Date()
guard let nycTimeZone = TimeZone(abbreviation: "EST"),
let nzTimeZone = TimeZone(abbreviation: "NZDT") else {
fatalError()
}
var nycCalendar = Calendar(identifier: .gregorian)
nycCalendar.timeZone = nycTimeZone
var nzCalendar = Calendar(identifier: .gregorian)
nzCalendar.timeZone = nzTimeZone
let now = Date()
let nycDateComponents = nycCalendar.dateComponents([.month, .day, .year], from: now)
let nzDateComponents = nzCalendar.dateComponents([.month, .day, .year], from: now)
let difference = Calendar.current.dateComponents([.day],
from: nycDateComponents,
to: nzDateComponents)
let daysDifference = difference.days
As of this writing that gives a daysDifference of 1. Since we're using the dateComponents(_:from:to:) function, it takes care of the math to calculate the number of days difference between the 2 month/day/year DateComponents.
A NSDate represents a moment in time. It has no time zone. Only string representations of dates have time zone information.
If you have the dates, just take the number of days between them. Don't worry about time zones.
Usually:
let difference = calendar.components(.Day, fromDate: self, toDate: toDate!, options: [])
return difference.day
should be enough.
Have you tried using let interval = laterDate.timeIntervalSinceDate(earlierDate)? This will return the difference between the two dates in seconds.

How to get Monday 00:00 of current week? Swift 3

I'm trying to return Monday 00:00 from my date. This is my code:
func getMonday(myDate: Date) -> Date {
let cal = Calendar.current
let comps = cal.dateComponents([.weekOfYear, .yearForWeekOfYear], from: myDate)
let beginningOfWeek = cal.date(from: comps)!
return beginningOfWeek
}
My problem is that it does not return Monday 00:00 , but Saturday 22:00.
Example:
let monday1 = getMonday(myDate: date) //date is: 2016-10-04 17:00:00
print(monday1) //Prints: 2016-10-01 22:00:00 (Saturday)
My question is:
How to return Monday 00:00 from myDate?
Thank you very much.
Your code returns the first day in the given week, that may be
a Sunday or Monday (or perhaps some other day), depending on your locale.
If you want Monday considered to be the first weekday then set
cal.firstWeekday = 2
If you want the Monday of the given week, independent of what the
start of the week is, then set comps.weekday = 2:
func getMonday(myDate: Date) -> Date {
let cal = Calendar.current
var comps = cal.dateComponents([.weekOfYear, .yearForWeekOfYear], from: myDate)
comps.weekday = 2 // Monday
let mondayInWeek = cal.date(from: comps)!
return mondayInWeek
}
Note that printing a Date always uses the GMT time zone,
you'll need a date formatter to print the result according to your local time zone. Example:
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm"
let now = Date()
print(df.string(from: now)) // 2016-10-02 20:16
let monday1 = getMonday(myDate: now)
print(df.string(from: monday1)) // 2016-09-26 00:00

how to get NSDate of a specific next day and time

I have some events for which I need to calculate NSDates.
For example I'm trying to get the next Monday at 8:00 AM.
So I tried some stuff but nothing works:
1.
let nextMonday = NSCalendar.currentCalendar().dateBySettingUnit(NSCalendarUnit.Weekday, value: 2, ofDate: startDate, options: NSCalendarOptions.MatchNextTime)
let nextMondayEight = NSCalendar.currentCalendar().dateBySettingUnit(NSCalendarUnit.Hour, value: 8, ofDate: nextMonday!, options: NSCalendarOptions.MatchNextTime)
I get:
2016-04-12 05:00:00 +0000
That's Tuesday at 8:00 (the time difference is my local time GMT -3).
2.
let unitFlags: NSCalendarUnit = [.Day, .Month, .Year]
let comp = NSCalendar.currentCalendar().components(unitFlags, fromDate: NSDate())
comp.timeZone = NSTimeZone.localTimeZone()
comp.weekday = 1
comp.hour = 8
comp.minute = 0
comp.second = 0
let compDate = NSCalendar.currentCalendar().dateFromComponents(comp)
print("time: \(compDate!)")
I get:
2016-04-11 05:00:00 +0000
That's today at 8:00 and not next Monday at 8:00.
Any suggestions?
Thanks
NSCalendar has a method nextDateAfterDate:matchingComponents:options for this kind of date math.
let calendar = NSCalendar.currentCalendar()
let components = NSDateComponents()
components.hour = 8 // 8:00
components.weekday = 2 // Monday in Gregorian Calendar
let nextMondayEightOClock = calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchStrictly)

Resources