Comparing Hours in Swift - ios

While querying data from a database I receive the hours a process is started and ended in two separate string fields for example start = "1100" and end = "+0200" which indicate it's hours of operation are from 11am-2am. What is the proper way to represent this in swift so that I can determine the amount of time left from the current time to the end time of the process.
EDIT:
I found an interesting way using the date formatter if I remove any possible prefix of + and use the below code it seems to work correctly; however, the date is not set is their a work around?
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HHmm"
let date = dateFormatter.dateFromString("1340")

You can create NSDate from components using NSCalendar.currentCalendar().dateWithEra (there are other similar functions, look up NSCalendar for details). You will need to add some logic to determine if the 2AM is today or tomorrow etc.
Then you can compare two NSDate dates. To determine time left to the end you would probably use NSDate method timeIntervalSinceDate. You can also use NSDateComponentsFormatter to get the remaining time nicely formatted.

Related

NSDate date in objective-c gives different datetime than Date() in swift

I understand that IOS uses place independent times unless specified otherwise. However, for an unknown reason when I log to console [NSDate date] from Objective-C, I am getting the actual time where I'm located whereas when I print to console Date() from swift, I'm getting a getting Greenwich meantime.
In objective-C the first mention of date in the method is:
__block NSDate* rightNow = [NSDate date];
LogDebug(#"right now%#",rightNow);
/logs as: right nowFri Oct 26 14:18:37 2018
In swift, the first mention of date in the method is:
let now = Date()
print("right now date is",now)
//Prints as: right now date is 2018-10-26 18:19:10 +0000
I do set formats using dateformatter for date in other methods in the Objective-C class, but I don't see how they could carry over into this method.
What could account for there being different? Is there a way to force them to be one or the other?
Thanks in advance for any suggestions.
Although date() and NSDate are supposed to be the same thing, they log differently in the console whether using NSLog or a library such as LogDebug. You should be aware of this when examining console logs.
Where there is a practical consequence to the difference between the two, is that now.timeIntervalSince(someDate!) returns a different result than NSDate().timeIntervalSince(someDate). To avoid issues, compare apples with apples, in other words date() with date() and NSDate() with NSDate()

Comparing working hours of an entity per day against the date (day of the week and hours) from the device

I would like to compare the working hours per day of an entity, fetched from JSON, against the day and hour information from the device. Related to that, I have two questions:
1) What is the best/right way of saving information of working hours per day for a particular entity in a JSON? I think, its structure should be something like this:
"working_hours": {
"<DAY_OF_THE_WEEK_1>":"<WORKING_HOURS>",
"<DAY_OF_THE_WEEK_2>":"<WORKING_HOURS>",
"<DAY_OF_THE_WEEK_3>":"<WORKING_HOURS>",
"<DAY_OF_THE_WEEK_4>":"<WORKING_HOURS>",
"<DAY_OF_THE_WEEK_5>":"<WORKING_HOURS>",
"<DAY_OF_THE_WEEK_6>":"<WORKING_HOURS>",
"<DAY_OF_THE_WEEK_7>":"<WORKING_HOURS>"
}
Here, I use days of the week as keys in a dictionary. However, I'm not sure what is the best/right way to enter "<DAY_OF_THE_WEEK_N>" and "<WORKING_HOURS>" inside a JSON.
2) After fetching this information, how to compare it against the time information from the device. Basically, I need to check the day of the week and then checking whether the current from the device is within the interval of a particular <WORKING_HOURS>.
For those who ask me whether I'm asking about comparing two numbers: I don't ask about that. I need to know the right format to save working hours in JSON also taking into account the day changes.
Currently, the problem is, when I save a time like 21:00 and transform it to a Date:
let workingHoursEnd = "21:00"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "H:mm"
dateFormatter.timeZone = TimeZone.autoupdatingCurrent
let dateFromString = dateFormatter.date(from: workingHoursEnd)
I get Jan 1, 2000 at 9:00 PM. The time format is ok, I can change it later, however, what I need is to get the current date (especially, day of the week) because I need to take into account the current day, too.
If you can answer to both questions (or even to one of them), I would appreciate your help.

iOS app occasionally sends an invalidly-formatted ISO8601 date to REST API

A couple of times a day, our PHP REST API logs an error causing by an invalidly-formatted ISO8601 date, coming from a GET request sent by our iOS app. The interesting thing is that most of the calls are fine (eg. 2015-07-07T00:00:00+10:00), but every so often we get a strange one (eg. 2015-07-07T12:00:00 am+10:00).
The code I believe is causing this is as follows:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
NSString *iso8601StringStart = [dateFormatter stringFromDate:self.searchStartTime];
Is there any circumstance in which NSDateFormatter could somehow (incorrectly) get am/pm from "yyyy-MM-dd'T'HH:mm:ssZZZZZ", when it's clearly the unintended behaviour? Are there certain kinds of NSDate that cause different behaviour? I'm stumped. The date given is always created via dateFromComponents.
I do not believe that that format string could ever generate the date with the am/pm annotations which you show. If I were you, my first course would be to double check that those dates are really being generated by those lines of code.
However, if you're sure this is happening, the only issue I can see is that it might be incorrect that you are not explicitly setting the locale and the calendar of the date formatter object. The date format syntax is defined by the unicode consortium, and the governing spec does say in section 4.5 that "If locales are not listed, dayPeriods fallback to AM/PM". I don't understand the whole document, but it suggests that being very explicit is the safest path.
If your only requirement is ISO8601, then you could use RFC3339 in UTC time zone, since this is a profile of ISO8601. This creates a correct formatter for that format:
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z"
formatter.timeZone = NSTimeZone(forSecondsFromGMT: 0)
formatter.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierISO8601)!
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
My final solution (towards which I was nudged by algal's answer):
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"]];
The Unicode spec was helpful (thanks algal), as was this Apple Technical QA, which suggested the en_US_POSIX as a specific solution.
"On iOS, the user can override the default AM/PM versus 24-hour time setting (via Settings > General > Date & Time > 24-Hour Time), which causes NSDateFormatter to rewrite the format string you set, which can cause your time parsing to fail."
Most helpfully, I found this explanation of the behaviour by huyz, although a little old:
When iPhone users change their region format between, say, “United States” and “France”, the users’ “24-Hour Time” setting is automatically switched to the mode that is most prevalent in that region. In France, that would set 24-Hour Time to “ON”, and in the U.S., that would set it to “OFF”. The users can then manually override that setting and that’s where trouble starts.
The problem comes from NSDateFormatter somehow “getting stuck” in the 12 or 24-hour time mode that the user has manually selected. So if a French user manually selects 12-hour mode, and the application requested NSDateFormatter to output time with the 24-hour format “HHmm”, it would actually receive time in a 12-hour format, e.g. “01:00 PM”, as if the application had instead requested “hhmm aa”. The reverse would happen if a US user manually selected 24-hour mode: outputting time with the 12-hour format “hhmm aa” would actually get you time in the 24-hour format instead, e.g. “17:00″.

Is there a way to work with times of the day without including the date aspect?

I want to create a an array of times of day, this is to store the times when a student starts and ends school. Since this is a typical weekly setup, this doesn't require the date aspect to be included so I was wondering if I could store these times in an array without having to include the date aspect, since its irrelevant?
Thanks.
let date = NSDate()
let formatter = NSDateFormatter()
formatter.dateFormat = "HH:MM:SS"
let dateString = formatter.stringFromDate(date)
"12:06:70"
So you could format a date as a String comprising only the date component and create a [String]. However I believe but may be wrong that there will likely often be a good case for storing the time-date as the original type. For example if you need to later perform time related calculations.

Getting a NSDate object from a string

Before flagging this question as a duplicate, please read on.
I need to compare two NSDates. A date I get from a server with the current date.
From NSDate.date() I get this date 2014-09-25 12:48:23 +0000 which is wrong (the time part). I needed to add 5 hours to get the current time so I did the following.
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd hh:mm:ss ZZZZZ"
let dateString = formatter.stringFromDate(NSDate.date())
The result is the correct date - 2014-09-25 06:21:56 +05:30
But there's a little hitch. This date is a String, not a NSDate. I need it to be a NSDate object to compare it with another date.
I tried converting it back like this,
let date = formatter.dateFromString(dateString)
And I get a wrong result - 2014-09-25 00:55:53 +0000. I tried passing the date string to a new NSDateFormatter to see if that works but again I still I get the wrong date.
My question is, how can I convert this date string to a NSDate object which also retains the correct time.
Thank you.
You are thoroughly confused about NSDate.
An NSDate is a point in time. It has no time zone information. If we both call [NSDate date] right now, we will get the same NSDate, even when you are in India and I'm in the UK. That's intentional. It's the same time. The time displayed on my watch and on your watch is different, but NSDate is the same. You can't convert NSDate to an "Indian" date.
You use calendars and timezones to convert NSDates to strings that you display to a user, in the way your users expect it. That's what you have done. You got a string that makes sense to Indian users. If an Indian user types a time, you take that string and convert it to an NSDate. The NSDate will be in Universal time. If you and I both typed in the time on our watch right now and converted it, you would type a time that looks like 5 1/2 hours earlier than mine. But it's the same time. If you convert it to NSDate, we will both get the exact same NSDate.
So how do you change your NSDate? Quite simple: You don't. NSDate is absolute time, independent of your location on earth.
Upon further Googling, I came across this post. The method described in it does exactly what I want. The original code is in Objective-C and since my question is in Swift, I'm going to post its Swift translation. \
func toLocalTime() -> NSDate {
let timeZone = NSTimeZone.localTimeZone()
let seconds = timeZone.secondsFromGMTForDate(self)
return NSDate(timeInterval: Double(seconds), sinceDate: self)
}
I added these as extension methods of NSDate so you can simply call them like this.
NSDate.date().toLocalTime()
You can compare two dates using any of the following NSDate functions: compare, earlierDate, laterDate, isEqualToDate. You should not compare date strings (oh, goodness, no, think of the nightmare); convert 'date strings' into 'NSDate' as soon as inputed.
In order to compare two dates that arose from strings correctly, you'll need the date strings to be unambiguous. In practice, that requires the date to have a time zone attached. If your server isn't providing a time zone and can't be modified to provide one, then you'll be forced to assume one (which would typically be the time zone where the server is located, assuming one server).

Resources