Check if date has same day in swift - ios

Hi there in my app I need to filter some documents through date. I need to see if the first date is the same day of the second date, so I searched on Apple Documentation to see if there are a solution to do this without create a method and I found this instruction isDate(_:inSameDayAs:), but if i try to compare the following date:
2020-07-29 16:15:50 +0000
2020-07-29 22:00:00 +0000
As you can see the day is the same, but I'm not able to understand why it return false, what's wrong?
CODE
Here's my code to check the difference between days:
myArray.filter({Calendar.current.isDate($0.log.createdDate, inSameDayAs:date)})

Date represents instants in time. Two instants of time could be in the same day in one timezone, but not in the same day in another timezone. These two instants in time:
2020-07-29 16:15:50 +0000
2020-07-29 22:00:00 +0000
are in the same day in the UTC timezone. However, in a timezone where the offset is 5 hours ahead of UTC (UTC+5) for example, the two times will not be in the same day, because they will become:
2020-07-29 21:15:50
2020-07-30 03:00:00
in that timezone
Now you should see that the timezone is crucial at determining whether two dates are in the same day.
Calendar.current uses the local timezone of the device for almost everything it does. isDate(_:inSameDayAs:) is no exception. In your device's timezone, the two dates are not in the same day. However, when you print them out without a formatter, they are always printed in the UTC timezone. In the UTC timezone, they are in the same day, making you think Calendar.current is wrong. Assuming you actually want to see if the two dates are in the same day in your device's timezone, then Calendar.current is right, and you don't need to fix anything.
To print the two dates in your timezone, use a formatter:
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .long
print(formatter.string(from: yourDate))
If you actually want to see if the two dates are in the same UTC day, then you can set the timezone of the Calendar:
var calendar = Calendar.current
calendar.timeZone = TimeZone(identifier: "UTC")!
// call calendar.isDate(_:_sameDayAs:) rather than using Calendar.current

Related

Adding time from string into date object swift

I have a time coming from service in string form like so "12:30 PM".
I am able to get this time but I want to add this time in current date. like so
31/3/2021 12:30 PM
The current date is in date object and coming time is in string format.
Please let me know what is a right way to do so? I am right now taking 12, 30,from string and setting it via Calendar. But dont know how to set am pm . Please let me know how to append time with date object. Thanks in advance.
First, if that is the date string you are getting from your service, it is incomplete. It needs a time zone.
Here's what I would do:
Assuming the service always uses the same time zone, find out that time zone.
Create a date formatter for that date string format, including the AM/PM bit.
Set the date formatter to use the time zone from step 1.
Convert the date string to a Date object using your DateFormatter.
Use the current calendar to extract the hours and minutes values into a DateComponents object.
Get the current date, and use the Calendar function date(bySettingHour:minute:second:of:matchingPolicy:repeatedTimePolicy:direction:) to set the hour and minutes of the current date to the values you got from step 5.
You should search in the Xcode help system for:
Dates and Times (overview)
Calendrical calculations (discussion specific to doing math on dates and times)
DateFormatter. (See this article for info on the characters to use to build your dateFormat string.)
Calendar
DateComponents
Calendar
You can do this by setting up your DateFormatter with the correct timeZone, calendar, defaultDate, and dateFormat. Here's an example:
import Foundation
let parser = DateFormatter()
parser.calendar = Calendar(identifier: .gregorian)
parser.timeZone = TimeZone(identifier: "US/Eastern")!
parser.defaultDate = parser.calendar!.date(
from: DateComponents(
timeZone: parser.timeZone!, era: 1,
year: 2021, month: 3, day: 31,
hour: 12, minute: 0, second: 0))!
parser.dateFormat = "hh:mm a"
print(parser.date(from: "12:30 PM"))
Output:
Optional(2021-03-31 16:30:00 +0000)

Wrong date on setting Calendar timeZone to UTC

Allover the app I use Date objects that when I NSLog the value it shows me:
2020-05-24 22:00:00 +0000
Which I think locally means the 25th (- 1 for summer, -1 for timezone). I want to do some Calendar date comparisons:
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(identifier: "UTC")!
// In order to have start on Monday
calendar.firstWeekday = 2
Using this calendar, lets say I want to get the starting date of current week:
extension Date
{
var startOfWeek: Date {
return Calendar.gregorian.date(from: Calendar.gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))!
}
}
If I NSLog:
Date().startOfWeek
It will show me:
2020-05-25 00:00:00 +0000
If I disable the timeZone line on Calendar, it shows me:
2020-05-24 22:00:00 +0000
I always thought the second one is the correct UTC version. Am I wrong? Because I thought all core data dates, all dates are in the 2nd version. In short: If I set Calendar to UTC, my date comparissons are wrong. If I don't they are good. And all this time dates are in UTC.
You are wrong because CoreData dates are not affected by TimeZone. Dates are dates. Think of them as numeric values. When you translate that value to a date and hour then, and only then, the TimeZone is applied.
In your example everything is correct. For a calendar whose TimeZone is UTC, 2020-05-25 00:00:00 +0000 is the beginning of the week. If you use other TimeZone values (for example the default value from Locale) then the your week start at 2020-05-24 22:00:00 +0000. That means that in your TimeZone the hour is 2020-05-25 00:00:00.

DateFormatter wrong time output when a different month is selected

I have a separate DatePicker and TimePicker component in my app.
Once the user has selected both the desired Date and Time, I construct a new Date object like this:
let timeStamp = Date(year: selectedDate.year, month: selectedDate.month, day: selectedDate.day, hour: selectedTime.hour, minute: selectedTime.minute)
I then use DateFormatter to output the exact time that the user has selected like this:
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm"
formatter.string(from: timeStamp)
Now I have a very weird bug where sometimes time output will be correct (time will be displayed in UTC+2) and sometimes it'll be incorrect (time will be displayed in UTC+1) and I have absolutely no idea what could be causing this.
Example 1 (correct output):
User selects: May 26, 2020 - 18:38
Date ISO output: "2020-05-26T16:38:00Z"
DateFormatter output: "18:38"
This is the correct output
Example 2 (wrong output):
User selects: March 26, 2020 - 18:38
Date ISO output: "2020-03-26T16:38:00Z"
DateFormatter output: "17:38"
This is not the correct output. Time should be 18:38 like in the above example.
Someone please tell me how is this possible? Literally the only difference is user picked March instead of May (different month) and that for some reason confuses the DateFormatter, so Time output is in a different timezone.
I am using SwiftDate to work with dates in general.
Set correct formatter.locale, you can try Locale(identifier: "en_US_POSIX") or try to use formatter.timeZone property. Maybe TimeZone.current or TimeZone(secondsFromGMT: 0) will fix your problem
That is probably because in May daylight saving is in effect and the difference to UTC changes from +1 to +2 hours
You can use the current TimeZone and add configure your DateFormatter with it
let timezone = TimeZone.current
dateFormatter.timeZone = timezone
That should make sure that you always use the same timezone that is currently used by your device

Swift 4 wrong date and time

I am struggle with timezone because I tried to set time to midnight to 23:00:00, however, the result show start with 6am until 5am next day. I tried to set timezone to current, still same result. Here my code
let day1num = Int(todaynumber.string(from: Date()))! + 1
let day1start = Calendar.current.date(bySetting: .day, value: day1num, of: Date())
let day1end = Calendar.current.date(bySetting: .hour, value: 23, of: day1start!)
print("\(day1start!) to \(day1end!)")
I got result
2020-04-22 06:00:00 +0000 to 2020-04-23 05:00:00 +0000
I don't want 6am to 5am next day, I want result:
2020-04-22 00:00:00 +0000 to 2020-04-22 23:00:00 +0000
How can I solve it?
Thanks!
Your issue is you're misunderstanding Date and how it prints to the console.
Dates are representative of moments in time. Think of them as an Integer that represents the amount of time that has passed since a reference date. It's NOT a human-readable string. To convert a date to a human-readable string, you need to use a DateFormatter and make sure to set the timeZone of the date formatter to be the time zone in which you want the string to be representative of (it will default to the system's current time zone). The timeZone will impact what the resulting string is. For example, if you have a date formatter where the time zone is Pacific time, it might return a value like April 22, 2020 6:00 PM, but then if you change the time zone to mountain time and get the string from the date, it will return April 22, 2020 7:00 PM.
When you print a Date instance to the console, the system formats it to be a human-readable string in the UTC time zone. If I'm doing my math correctly, you're in mountain time, which is why the value you're seeing logged is 6 hours ahead of the value you're expecting.
If you want to see the date logged to the console as the user will see it, you should use a DateFormatter instance. It will default to use the user's system time zone, then use the Date value you've calculated to get the String representation of that date, and then log that String to the console, rather than the date itself.
let formatter = DateFormatter()
formatter.dateStyle = .medium // Customize as needed
formatter.timeStyle = .medium // Customize as needed
// formatter.timeZone = ... something else if you don't want to use the system time zone
let day1StartAsString = formatter.string(from: day1start!)
let day1EndAsString = formatter.string(from: day1end!)
print("\(day1StartAsString) to \(day1EndAsString)")

Why compareDate from NSCalendar seems to need to set an UTC timeZone to work properly?

I create two dates like the following:
let date1 = stringToDate("2015-02-12 12:29:29")!
let date2 = stringToDate("2015-02-11 19:18:49")!
func stringToDate(var dateString: String) -> NSDate? {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.timeZone = NSTimeZone(name: "UTC")
return dateFormatter.dateFromString(dateString)
}
As you can see, the two date are different and are not on the same day.
To test if two dates are on the same day, I use the following method:
func isSameDayThan(date1: NSDate, date2: NSDate) -> Bool {
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = NSTimeZone(abbreviation: "GMT+10")!
return calendar.compareDate(date1, toDate: date2, toUnitGranularity: .DayCalendarUnit) == .OrderedSame
}
There I don't precise any timeZone in the calendar. The local TimeZone of my device is set to GMT+10.
In that case, isSameDayThan(date1, date2)-> true
If I change the timeZone to something inferior or equal to GMT+04, then I get isSameDayThan(date1, date2)-> false.
What I don't understand is that the result is different depending on the timeZone, but I am comparing two NSDate() and NSDate() has nothing to do with time zone if I'm not wrong.
The timezone comes into play because you compare the dates with a granularity that is timezone dependent. So you are actually comparing against the local representation of the date. The point in time model that is often used to describe NSDate doesn't know about days and weeks. From a abstract standpoint (i.e. the point in time that is the same everywhere in the universe) it actually doesn't even know about seconds.
Anyway, if you would compare with == you would obviously not need a timezone. That's the only comparison that is truly independent from the local representation. If two points in time are exactly the same they are equal. Easy.
Everything beyond a straight == comparison has to be converted into local units. Not only you have to use the correct calendar, but you have to use the correct timezone as well.
Luckily there are no calendars that have days that are shorter or longer than 24 hours. And there are no timezones that differ in seconds either. Because we know that, you can actually see if dates are within the same minute with an easy calculation. e.g.:
Int(date1.timeIntervalSince1970 / 60) == Int(date2.timeIntervalSince1970 / 60)
No calendar needed because we (currently) don't have calendars that have minutes that are not 60 seconds long. No timezone needed, because we don't have timezones with offsets that differ in the number of seconds.
But we have a few timezones that have offsets that are only fractions of an hour. For example India Time Zone which has an offset of +05:30. So starting with hours the boundaries of the granularity units are timezone dependent.
If you have two NSDates which are set to 9:25 and 9:35 UTC, they are in the same hour if you compare in any timezone that has an offset that does not differ in the number of minutes (e.g. 00 in +x:00). They are in the same hour in UTC, they are in the same hour in UTC+5:00 and UTC-5:00.
But if you compare in India Time Zone these two dates are actually in different hours. Because 9:25 UTC in IST is 2:55, and 9:35 UTC is 3:05 in IST.
In your example you are comparing to the granularity of the day. Which needs to take all timezones into account. But we can still ignore the calendar, because all calendars use days that are 24 hours long.
But if you would compare to the granularity of a week, month or year you would have to take the calendar into account as well. There are calendars that have totally different months. Just because two dates are in the same month in gregorian calendars doesn't mean that they are in the same month in hebrew calendars.
Yes, it's complicated. And that's the reason all date calculation appear so verbose. People often try to hide the complexity behind a fancy helper function. Which often leads to problems. So be aware of creation functions like isSameDay().
Each time you compare a date you have to make the decision what timezone and calendar to use. If you rely on helper functions you will miss the one instance where you should actually compare against UTC instead of the local timezone.
TL;DR: If you compare with granularity you should always set the correct calendar and the correct timezone.
Th two dates are different days in the UTC time zone. But in the GMT+10 time zone they are both the same day - February 12.
2015-02-12 12:29:29 UTC = 2015-02-12 22:29:29 UTC+10
2015-02-11 19:18:49 UTC = 2015-02-12 05:18:49 UTC+10
By Default, the comparison is done in the local time zone but your date objects were specifically created in the UTC time zone.
If you create the NSDate objects from the strings using the default time zone and compare them using the default time zone, then the dates would have two different days.

Resources