Neat way to get end of day for date? - ios

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())
}

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.

Get tomorrow's date with Swift 2

I have this code which gives today's date in this formate M/dd/yy
let dateFormater = NSDateFormatter()
dateFormater.dateFormat = "M/dd/yy"
let todayDate = dateFormater.stringFromDate(NSDate())
How can I get the same thing but with next day's date please?
First, you get a NSDate for the day you need, in this example (one day from now is tomorrow):
var oneDayfromNow: Date? {
Calendar.current.date(byAdding: .day, value: 1, to: Date())
}
print(oneDayfromNow)
Then you convert it to your format as string (your case M/dd/yy):
if let oneDayfromNow {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "M/dd/yy"
let str = dateFormatter.string(from: oneDayfromNow)
print(str)
}
It's a bit complicated, but it's all things that you need to know anyway.
Why it's difficult: You would think that you could just take NSDate (timeIntervalSinceNow:24 * 60 * 60), adding one day to now. But when you turn on daylight savings time, then 11:30pm plus 24 hours is 00:30am two days later. When daylight savings time is turned off, then 00:30am plus 24 hours can be 11:30pm on the same day.
So you need to create an NSCalendar object, convert NSDate () into components, add one day to the components, convert back to an NSDate (all that gives you the same time on the next day, handling all special cases), and then format the result as you did now.
I finally used this code to fix it :
let dateFormater = NSDateFormatter()
dateFormater.dateFormat = "M/dd/yy"
let todayDate = dateFormater.stringFromDate(NSDate().dateByAddingTimeInterval(24 * 60 * 60))

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.

How do I get timestamp interval in swift?

I saw this answer : Get current NSDate in timestamp format
But for some reason it gives me errors.
What I need is to be able to specify the timestamp between now and in the next 2 minutes, or interval between last 2 minutes and now.
Eg: interval => now-2minutes to now (Time between previous 2 minutes and now)
Thank you.
If I got it right, this should clarify things for you:
let calendar = NSCalendar.currentCalendar()
let now = NSDate()
let comps = NSDateComponents()
comps.minute = 2
let twoMinutesFromNow = calendar.dateByAddingComponents(comps, toDate: now, options: NSCalendarOptions.MatchStrictly)
let interval = twoMinutesFromNow?.timeIntervalSinceDate(now)
Here is a nice article about handling NSDates with the use of NSCalendar and NSDateComponents
var curr = NSDate(timeIntervalSince1970: 10000)
var timestep = curr

Resources