Get dats from week number of particular year - ios

I need one function where i will pass any week number and year, that function should return dates for that week. Like if I pass week 2 and year 2016 then function should return me 3 January to 9 January.

Just a very simple solution using NSCalendar functionality:
let week = NSDateComponents()
week.yearForWeekOfYear = 2016 // the year
week.weekOfYear = 10 // week index
let calendar = NSCalendar.currentCalendar()
// start of week or nil if the week does not exist
let weekStart = calendar.dateFromComponents(week)
// add 1 week to start (this is essentially the start of the next week)
let weekEnd = calendar.dateByAddingUnit(.WeekOfYear, value: 1, toDate: weekStart!, options: [])
print(weekStart)
print(weekEnd)

Related

Number of days in month returns wrong value after 10:00 PM

I am having a small issue with getting the total days in a month using Swift.
I have extended the Date class and created this function:
func daysInMonth() -> Int {
print(self.day) ##30
print(self.month) ##12
print(self) ## 2021-11-30 23:46:29 +0000
print(Calendar.current.range(of: .day, in: .month, for: self)?.count) ##31
return Calendar.current.range(of: .day, in: .month, for: self)?.count ?? 0
}
I have set the Date&Time to the 30th of November, at 11:45 PM in the settings of my Mac, in Preferences.
I called the above function at 11:46 PM and obtained the above results (inline, next to the print statements).
The date output is correct as well as the day. The month output is wrong and the result is 31 days in the month of November.
If I run this exact same code before 10:00 PM, I get the right result which is 30 days.
Does anyone know why this is happening?
Thank you,
Paprika
It's a GMT offset issue combined with the current day in a month.
When you create a date without set a day, it will be set to the first day of the month.
So, if your timezone offset is for example -4 means your are 4 hours behind the GMT 0 and by default the timezone defined at Calendar.current is equal the system timezone. So what it means? Means you'll obtain the previous month if you test it in a boundary of 23 + (-4) or the next month if your offset is positive.
You can test this behaviour copying'n paste the following code in the Playground.
func getDaysInMonth(month: Int, year: Int, offset: Int = 0) -> Int? {
let someDate = DateComponents(year: year, month: month, hour: 3)
var current = Calendar.current
let timezone = TimeZone(secondsFromGMT: 60 * 60 * offset)!
current.timeZone = timezone
guard let someDay = current.date(from: someDate) else { return nil }
print("date: \(someDay)") // this will always
return someDay.daysInCurrentMonth
}
for hour in -12...12 {
print("hour: \(hour)\ndays: \(getDaysInMonth(month: 10, year: 2021, offset: hour) ?? -1)")
print("---\n")
}
extension Date {
var daysInCurrentMonth: Int? {
Calendar.current.range(of: .day, in: .month, for: self)?.count
}
}
Notice the days will change starting by your current system time zone (notice only the month will change).
How to fix this?
In your case, I guess you just want to show how many days a month have, so you can just set the to zero like this:
TimeZone(secondsFromGMT: 0)
Do this change at a instance of Calendar.current and check if it works for you.
It appears there something wrong with your Date extension methods for .day and .month.
Without seeing code it's hard to determine what the problem is though. Below is some code for returning the current month (Int) and current numbered day of month (Int)
extension Date
{
var month: Int
{
let date = Date()
let calendar = Calendar.current
let components = calendar.dateComponents([.month], from: date)
return components.month
}
var day: Int
{
let date = Date()
let calendar = Calendar.current
let components = calendar.dateComponents([.day], from: self)
return components.day
}
}
Please also ensure your time/date settings are correct on your mac/simulator/device. If these are wrong - it could have been jumping to a different month if you were in a timezone that was ahead a few hours.

How to query for 'task' records by date

We are trying to make a app that keeps a users schedule. He wants a simple tableview to display a different section for each day and he wants all the tasks for that day to display in order. Here is the db schema im using:
TASK_ID , TASK_NAME(STRING), TASK_DURRATION(AMOUNT OF TIME IN MILI), TASK_START_TSTAMP(EPOCH TIMESTAMP), TASK_END_TSTAMP(EPOCH TIMESTAMP), TASK_COMPLETED(BOOL)
How would I begin to go about querying for tasks by day? Like if i wanted to return all the tasks for the next 7 days.
I have to :
•Allow users to create tasks with a specific duration and in some cases a specific start time
•Query for the next 7 days worth of tasks chunked by date.
So what do I do? Find a way to get a array of the next 7 days, loop through and run a query for each one for all the tasks scheduled on that day?
You want to calculate startDate and endDate to be used in your SQL using calendrical calculations of NSCalendar.
Let's say it's 3:24pm on January 20th, and you want all tasks in the next seven days, up to 3:24pm on January 27th, you could do something like:
let startDate = NSDate()
let endDate = NSCalendar.currentCalendar().dateByAddingUnit(.Day, value: 7, toDate: startDate, options: [])!
Then build your query using those start and end dates.
If, however, you want all events scheduled anytime on January 20th through January 26th (inclusive), you could use something like:
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Day, .Month, .Year], fromDate: NSDate())
let startDate = calendar.dateFromComponents(components)!
let endDate = calendar.dateByAddingUnit(.Day, value: 7, toDate: startDate, options: [])!
Then you could build your SQL using startDate and endDate, if you're storing those as UNIX epoch double values:
let sql = "select * from TASKS where TASK_START_TSTAMP >= \(startDate.timeIntervalSince1970) and TASK_START_TSTAMP < \(endDate.timeIntervalSince1970)"

Swift - NSDate and last week of year

I am making a TimeTable app, and i have a method that adds 1 week to the current date, this works as it is supposed to, however if the week transitions from December to January, it adds 1 day extra.
Here is my code:
func getWeekDates(var date: NSDate) -> [NSDate] {
var dates: [NSDate] = [NSDate]()
for var i = 0; i < 5; i++ {
date = date.dateAtWeekStart() + 1.day - 1.week
date += i.day
dates.append(date)
}
return dates
}
And dateAtWeekStart():
func dateAtWeekStart() -> NSDate {
let flags : NSCalendarUnit = [NSCalendarUnit.Year,NSCalendarUnit.Month ,
NSCalendarUnit.WeekOfYear,
NSCalendarUnit.Weekday]
let components = NSCalendar.currentCalendar().components(flags, fromDate: self)
components.weekday = 1 // Sunday
components.hour = self.hour
components.minute = self.minute
components.second = self.second
return NSCalendar.currentCalendar().dateFromComponents(components)!
}
(dateAtWeekStart() is a function made in an extension to NSDate)
The reason i am adding 1 day and removing 1 week, is because dateAtWeekStart returns next sunday, so for example 08-10-2015.dateAtWeekStart() returns 11-10-2015.
So this works fine normally, however if we take this year as an example, 29-12-2015.dateAtWeekStart() returns 04-01-2015 instead of 03-01-2016.
By the way, the region on the device is set to Denmark.
dateAtWeekStart, comes from a helper class called SwiftDate made by malcommac: https://github.com/malcommac/SwiftDate
UPDATE EDIT:
I am still having trouble figuring out how to fix this, i tried adding year to components like so: components.year = self.year, but it sets the year to 2014 for some reason when returning the components..
That dateAtWeekStart() method simply does not work.
[.YearForWeekOfYear, .WeekOfYear] are sufficient as calendar units to
determine the (start of a) week uniquely. The additional units can make
the calculation undetermined. Also you can not just set
components.weekday = 1 because in some regions Monday (2) is the first
day of the week.
So it is actually a bit easier:
extension NSDate {
func dateAtWeekStart() -> NSDate {
let cal = NSCalendar.currentCalendar()
// cal.firstWeekday = 1 // If you insist on Sunday being the first day of the week.
let flags : NSCalendarUnit = [.YearForWeekOfYear, .WeekOfYear]
let components = cal.components(flags, fromDate: self)
return cal.dateFromComponents(components)!
}
}
This should work in all cases and give the start of the week (at midnight) for the given date. There are also other methods
one could use, such as rangeOfUnit().
If you want Sunday to be considered as the first day of the week
instead of using the user's regional settings
then you have to set the firstWeekday property of the calendar.
The code to add days or weeks to a date also looks highly suspicious.
The extensions method for Int in the SwiftDate project treats
a day as 24*60*60 seconds. This is not correct, because in regions with
daylight saving times, a day can have 23 or 25 hours when the clocks
are adjusted. The correct way to add one week to a date is to
use calendar components again:
date = cal.dateByAddingUnit(.WeekOfYear, value: 1, toDate: date, options: [])!
Update for Swift 3:
extension Date {
func dateAtWeekStart() -> Date {
var cal = Calendar.current
// cal.firstWeekday = 1 // If you insist on Sunday being the first day of the week.
let components = cal.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)
return cal.date(from: components)!
}
}

Improper date conversion?

I'm trying to create a function that will get the Gregorian start and end dates for a month passed in of any arbitrary calendar.
So if I pass Hebrew calendar, month 3, year 5775, I would expect back something like this (2015-05-19T00:00:00.000-05:00, 2015-06-17T00:00:00.000-05:00)
let (start,end) = gregorianMonthForCalendarMonth(NSCalendar(calendarIdentifier: NSCalendarIdentifierHebrew)!, 3, 5775)
println(start)
println(end)
What I'm getting is (2014-11-23T00:00:00.000-05:00, 2014-12-22T00:00:00.000-05:00)
Not sure what I'm doing wrong.
My functions:
let fullDateFormatter = getFullDateFormatter()
func getFullDateFormatter() -> NSDateFormatter {
let _dateFormatter = NSDateFormatter()
_dateFormatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
_dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'.'SSSZZZZZ"
return _dateFormatter
}
func gregorianMonthForCalendarMonth(calendar:NSCalendar, month:Int, year:Int) -> (start:String, end:String) {
// get 1st day of month
let components = NSDateComponents()
components.year = year
components.month = month
components.day = 1
let start = calendar.dateFromComponents(components)
// get number of days in the month
let days = calendar.rangeOfUnit(NSCalendarUnit.CalendarUnitDay, inUnit: NSCalendarUnit.CalendarUnitMonth, forDate: start!)
components.day = days.length
let end = calendar.dateFromComponents(components)
return (start!.stringValue(),end!.stringValue())
}
extension NSDate {
func stringValue() -> String {
let strDate = fullDateFormatter.stringFromDate(self)
return strDate
}
}
The problem would appear to stem from the interpretation of what the "third" month of 5775 is. Kislev is the 3rd month of the civil year. And 1 Kislev 5775 is 23 November 2014.
However, Silvan is the 3rd month of the ecclesiastical year and 1 Silvan 5775 is 19 May 2015.
Apple's choice of the civil year month numbering is somewhat understandable as the Hebrew numeric year value increments on Tishrei (the first month of the civil year, the seventh month of the ecclesiastical year).
Admittedly, many Hebrew date conversion sites list the months in the order they occur in the ecclesiastical year, but if you dive into those sites, you'll see that they accurately reflect the year number changing on the first of Tishrei. I've noticed that many of these sites don't ask for numeric month number, but rather let you select from a list of month names, eliminating the sort of confusion that simply referring to the "third" month might otherwise entail.

Neat way to get end of day for date?

I am querying a database for values between a startDate and an endDate. I am therefore looking for a neat way to get the NSDate for the end of the day for a date. Just like you can get startOfDayForDate().
I guess you could do :
let cal = NSCalendar.currentCalendar()
let startOfDay = cal.startOfDayForDate(NSDate())
let aDay:NSTimeInterval = 60*60*23 + 60*59 + 59
let endofDay = startOfDay.dateByAddingTimeInterval(aDay)
Is adding component.day + 1 to startOfDayForDate the correct method to get the "end of the day" for a date, or is there a better method?
A better way would be to get the start of the next day and subtract 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 second.
Do you get what I want to say? It's very hard to define the end of the day. 23:59 is certainly not the end of the day, there is almost a whole minute left until the next day. And even 23:59:59 is not the end of a day. Because there is an infinite amount of fraction seconds between this time and the start of the next day. As far as I know NSDate supports nanoseconds out of the box, so in the current implementation there are at least 1 000 000 000 possible NSDates between 23:59:59 and the next day.
That's why you should see if you can find a way to use the start of the next day.
For example, instead of if (startOfDay <= date && date <= endOfDay) you could use if (startOfDay <= date && date < startOfNextDay).
To calculate the start of the next day you have to use NSCalendar. In iOS8 Apple added a couple of nice methods to make these calculations short:
let calendar = NSCalendar.currentCalendar()
let startOfDay = calendar.startOfDayForDate(NSDate())
let startOfNextDay = calendar.dateByAddingUnit(.CalendarUnitDay, value: 1, toDate: startOfDay, options: nil)!
EDIT: Since you now state that you want to query a database you don't need to find the end of the day. Check if the date is on or after the start of the day, and before the start of the next day.
Try this:
var endOfDay: Date? {
var components = DateComponents()
components.day = 1
components.second = -1
var calendar = Calendar.current
calendar.timeZone = TimeZone(abbreviation: "GMT")!
return calendar.date(bySettingHour: 11, minute: 59, second: 59, of: Date())
}

Resources