Getting a NSDate object from a string - ios

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).

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

TimeZone changed while converting string to Date

When I'm changing date(which is in string form) to Date form,Then timezone change IST to UTC.Because of that I'm not getting notification
My code is :
#IBAction func datePickerDidSelectNewDate(_ sender: UIDatePicker) {
let selectedDate = sender.date
let dateStr = Date().currentTimeZoneDate(date: selectedDate as NSDate)
let date = dateFormatter.date(from: dateStr)
let delegate = UIApplication.shared.delegate as? AppDelegate
delegate?.scheduleNotification(at: date!)
}
In my code, I'm getting selectedDate is in UTC time zone.So I have converted it into currentTimeZone.But when I change String form to Date,means dateStrto date it again convert to UTC timeZone.
What is currentTimeZoneDate? This seems a deep misunderstanding of Date. Dates a never "in a time zone." Dates are an absolute point in time. All observers, no matter where they are, should agree on when a given Date occurs (ignoring relativistic effects).
You should configure UIDatePicker with the time zone you want it to represent. The default is localTimeZone, which always is the current time zone at the point that this action is called (so sender.date has already been adjusted). If you want it to represent some other time zone, change UIDatePicker.timeZone. Don't mess with the Date. Definitely don't convert it to a string and back.
It's unclear what scheduleNotification(at:) does, or what the final goal is. NSUserNotification by default will adjust all of its delivery dates if the time zone changes. If you don't want this (if you want to maintain the precise instant that the user selected, rather than the nominal time they selected), then you should set deliveryTimeZone on the notification (generally to NSTimeZone.default, which is a non-updating version of "the current time zone").
Swift's Date type (and also its Objective-C NSDate) doesn't have any notion of TimeZone in itself. It's just a simple timestamp. This means that if you set a timezone to a UIDatePicker, you can use its date as it is in that timezone without a need of converting it.
More explanation: when you po date in a debugger console, the debugger just select the UTC timezone as a presentation of the printing date. Its behavior doesn't related to the Date instance.

Comparing Hours in Swift

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.

Long Date (UNIX Date) issues

The problem is as follows :
Quick details of the app : Sorting of data (ascending) according to the date.
The UNIX date / long date from the web service in form of JSON (is of 13 digits). When the long date is parsed, I get an invalid value of the date.
Long date : 1428498595000
Converted date : Sun, 26 Apr 47237 13:16:40 (After parsing)
[Notice the year]
When the online converter is used (example) : http://www.onlineconversion.com/unix_time.htm , the same output is reproduced.
My purpose is to get the dates sorted in ascending order, but unfortunately, as the year is shown irrelevant, it makes sorting impossible.
Long date 1428498595 (After manually removing three zeros to test it on the website) : Wed, 08 Apr 2015 13:09:55 GMT (This is the correct date that needs to be shown)
Can anyone help me understand what can be done so that we could manually remove the last three zeros?
[Storing the parsed data in SQLite and then producing the stored data in UITableView]
This is in AppDelegate
+(NSString * )convertUnixTime_to_SytemTime :(NSTimeInterval )timeInterval
{
NSDate * convertedDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"YYYY-MM-DD";
NSTimeZone *localTime = [NSTimeZone systemTimeZone];
[dateFormatter setTimeZone:localTime];
NSString *timeStamp = [dateFormatter stringFromDate:convertedDate];
return timeStamp;
}
This is in the code
data.News_LastModifiedDate = [AppDelegate convertUnixTime_to_SytemTime:[[subcomponents objectAtIndex:2] doubleValue]];
Thanks much in advance.
The date/time is in milliseconds since UNIX epoch, instead of seconds. You can divide by 1000.0 in order to keep the fractional seconds (if they ever appear):
NSTimeInterval seconds = (NSTimeInterval)1428498595000 / 1000.0;
EDIT. To address other aspects of your question:
Storing the parsed data in SQLite and then producing the stored data
in UITableView
Store it as is; as a 64-bit int.
+(NSString * )convertUnixTime_to_SytemTime :(NSTimeInterval )timeInterval
This method doesn't convert the UNIX time to system time; it formats the date into a string (wrongly by the look of it). Forget it and use just the first line of code only:
NSDate * convertedDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
Where timeInterval is the original number converted to seconds as above. The only time you want the date as a string is during presentation, not during processing.
Your timestamp is in Milliseconds. Simply divide it by 1000 to get the correct date/time.
data.News_LastModifiedDate = [AppDelegate convertUnixTime_to_SytemTime:[[subcomponents objectAtIndex:2] doubleValue]/1000];
Should work for you.
Whenever you read JSON data, there should be an API description that tells you what data is delivered in which form. You would read the API description and do whatever needs doing. If there is no API description, you do whatever you can.
When you read a date from JSON data, you should as the first step convert whatever you find to an NSDate*. JSON has no built-in date type. It has a standard format for dates, using RFC3339, but your JSON doesn't do that. Apparently it uses UTC in milliseconds since 1970, stored as an integer. The JSON parser would return this as an NSNumber. That's fortunately quite easy to handle:
NSNumber* dateAsNumber = "whatever is needed to extract the integer";
NSTimeInterval dateAsInterval = [dateAsNumber doubleValue] / 1000;
NSDate* dateAsDate = [NSDate dateWithTimeIntervalSince1970: dateAsInterval];
You shouldn't convert NSDate to anything else. NSDate is the standard type for handling dates on iOS. CoreData will handle it. If you use SQL directly, convert it just before storing to the database and after loading from the database and use NSDate everywhere else.
BTW. What is SystemTime? If you use a method name like convertUnixTime_to_SytemTime, I get very worried, because no two people will ever agree what system time is. And your method doesn't return a system time, it returns something converted to a string.

NSDate and NSDateformatter wrong output

So, i'm trying to create a NSDate object for sunrise and sunset. I get the date based on NSDatePicker, i get coordinates from a Map, and i get the timezone from the GPS from the map.
I use this code to get the NSDate object: https://github.com/MosheBerman/KosherCocoa-legacy
This one to get the coordinates: https://github.com/digdog/MapKitDragAndDrop
And this one to get the the timezone based on coordinates: https://github.com/Alterplay/APTimeZones.
Right now my physical location is in Los Angeles, and the sunrise and sunset i'm using to testing is back home in Denmark.
-(NSString *)sunriseDate{
//Create the GeoLocation based on 'latitude' and 'longitude' (getting the from MapKitDragAndDrop) and 'location.timeZone' (getting that from APTimeZones).
GeoLocation *position = [[GeoLocation alloc] initWithName:#"position" andLatitude:latitude andLongitude:longitude andTimeZone:location.timeZone];
AstronomicalCalendar *astronomicalCalender = [[AstronomicalCalendar alloc] initWithLocation:position];
//daysBetween is the value from NSDatePicker
astronomicalCalender.workingDate = [NSDate dateWithTimeIntervalSinceNow:kSecondsInADay*[self daysBetween]];
NSDate *sunriseDate = [astronomicalCalender sunrise];
NSLog(#"Sunrise time: %#", sunriseDate);
//This spits out: Sunrise time: 2014-03-05 06:09:53 AM +0000 which is the right time.
NSDateFormatter *sunriseTime = [[NSDateFormatter alloc] init];
[sunriseTime setDateFormat:#"HH:mm:ss"];
NSString *sunriseString = [sunriseTime stringFromDate:sunriseDate];
NSLog(#"Sunrisestring: %#", sunriseString);
//This spits out: 10:09:53 PM.
return sunriseString;
}
Why does this happen and can anyone maybe give me a solution to this?
To anyone who might stumble into the same thing.
I found a library on github https://github.com/Alterplay/APTimeZones that helped me determine the timezone based on the coordinates.
Then i used
[sunriseTime setTimeZone:location.timeZone];
This put out the right time for the timezone.
Hope this helps anyone!
You need to match the input format correctly.
You may only be interested in the time but the NSDateFormatter doesn't care. NSDate is never JUST a time. It is a point in time and so includes the date too. It doesn't work without the date and time sections.
Also, this is probably one of THE MOST ASKED questions on Stack Overflow. Any other NSDate to NSString (or vice versa) question will answer this.
Your date format should be...
#"YYYY-MM-dd hh:mm:ss a EEEE"
I believe. Something like that anyway.
This spits out: 10:09:53 PM. This is correct local time for your time zone, which differs by 8 hours from Greenwich time Sunrise time: 2014-03-05 06:09:53 AM +0000. That's all. You have evening when a german man wakes up.
As Fogmeister said, you should include the timezone when creating a NSDateFormatter. Take a loot at Apple's Docs, Data Formatting Guide:
Fixed Formats
To specify a custom fixed format for a date formatter, you use setDateFormat:. The format string uses the format patterns from the Unicode Technical Standard #35.
The Unicode official site:
http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#Date_Format_Patterns
You may try using this: [sunriseTime setDateFormat:#"yyyy-MM-dd hh:mm:ss a EEEE"];
instead of [sunriseTime setDateFormat:#"HH:mm:ss"];

Resources