Youtube API returns date string in RFC3339 format. I found how to parse it on manual, anyway, this is too long.
- (NSString *)userVisibleDateTimeStringForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString
// Returns a user-visible date time string that corresponds to the
// specified RFC 3339 date time string. Note that this does not handle
// all possible RFC 3339 date time strings, just one of the most common
// styles.
{
NSString * userVisibleDateTimeString;
NSDateFormatter * rfc3339DateFormatter;
NSLocale * enUSPOSIXLocale;
NSDate * date;
NSDateFormatter * userVisibleDateFormatter;
userVisibleDateTimeString = nil;
// Convert the RFC 3339 date time string to an NSDate.
rfc3339DateFormatter = [[[NSDateFormatter alloc] init] autorelease];
enUSPOSIXLocale = [[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"] autorelease];
[rfc3339DateFormatter setLocale:enUSPOSIXLocale];
[rfc3339DateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
date = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
if (date != nil) {
// Convert the NSDate to a user-visible date string.
userVisibleDateFormatter = [[[NSDateFormatter alloc] init] autorelease];
assert(userVisibleDateFormatter != nil);
[userVisibleDateFormatter setDateStyle:NSDateFormatterShortStyle];
[userVisibleDateFormatter setTimeStyle:NSDateFormatterShortStyle];
userVisibleDateTimeString = [userVisibleDateFormatter stringFromDate:date];
}
return userVisibleDateTimeString;
}
I can make a function contains this, but I want to know is there pre-defined way on Cocoa foundations or standard C or POSIX library to do this. And I want to use it if there it is. Can you let me know is there more simpler way? Or It will be very appreciate if you confirm this is most simple way :)
The pure stuff-that-comes-with-Cocoa way is exactly what you're doing. You can make this method both shorter and faster by creating the date formatters elsewhere, probably in init, and using/reusing them in this method.
You need two formats because fractional seconds are optional, and the timezone should be Z5, not Z. So you create two formatters with formats
#"yyyy'-'MM'-'dd'T'HH':'mm':'ssX5"
#"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSSX5"
and try them both. That's obviously for RFC3339; your strings might not be in that format. Glad you didn't ask for RFC822 which is a pain to do correctly. But you should really have a method first that returns NSDate, because most uses don't actually need a string formatted for the user.
I had some problems with parsing RFC 3339 in Obj-c since the fractional seconds and zone seems to be optional.
The most reliable function that I found was this Gist (of which I am not the author): https://gist.github.com/mwaterfall/953664
Related
I have a NSDate on the iPhone App, that I need on the corresponding Watch App to do calculations, I don t even need to show it in a local format. Somehow it is working not really good if I transfer NSDate objects, I read that it s better to use Strings for transfer.
So I format my NSDate to a NSString. I tried different ways, and on the Watch side if I want to reverse it, I obviously use the same format. But it always returns nil and I don t understand why.
My code on the iPhone side:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd/MM/yyyy HH:mm"];
NSString *dateString = [formatter stringFromDate:myDateObject];
I also tried:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[formatter setDateStyle:NSDateFormatterShortStyle];
[formatter setTimeStyle:NSDateFormatterShortStyle];
NSString *dateString = [formatter stringFromDate:myDateObject];
and I tried with:
formatter.locale = [NSLocale currentLocale];
On the Watch side I need to reverse my String to a date object:
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy HH:mm"
let date = dateFormatter.date (from: result["dateString"] as! String)
and:
let dateFormatter = DateFormatter()
ddateFormatter.formatterBehavior = .behavior10_4
dateFormatter.locale = .current
ateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
let date = dateFormatter.date (from: result["dateString"] as! String)
and with
dateFormatter.locale = .current
when I mouseover
result["dateString"]
I See a date string like
12/17/20, 12:27 PM
but the date(from string: String) always returns nil.
I also tried to set the format to fit like:
dateformat = "MM/dd/yyyy, h:mm a"
but nothing works. As I said, I don t need a specific localized format, I just need day and time, a date object to calculate with. What can I do?
Thanks for help!
First, make your date format match your input
A input like "12/17/20, 12:27 PM" suggests a format of something like MM/dd/yy, h:m a
Next, remove any possible issues the user's current locale may introduce by using something like dateFormatter.locale = Locale(identifier: "en_US_POSIX"), unless you can guarantee the locale format, this should suffice for most cases
For example
let dateString = "12/17/20, 12:27 PM"
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "MM/dd/yy, h:m a"
let date = dateFormatter.date (from: dateString)
will generates 2020-12-17 01:27:00 +0000 on my PC (and could change depending on your system, which is why, it's always important to use a known DateFormatter to debug dates - removes possible confusion)
A great way to serialise dates is to use timeIntervalSinceReferenceDate which returns a double value. So on the iPhone side you'd have
double t = [myDateObject timeIntervalSinceReferenceDate];
Note that formally this will return TimeInterval but that is just a double and easy to cast.
The double you can either use as is or convert to string - this depends on how you transmit it. You have e.g. -setDouble:forKey and -doubleForKey with NSUserDefaults where you can use it as is as a double or you can print it to string using something like
[NSString stringWithFormat:#"%.20f", t]
This produces 20 digits after the decimal separator, which is overkill but you won't loose anything at least.
Once you have read or converted the double on the watch side you can of simply use NSDate's
init(timeIntervalSinceReferenceDate: TimeInterval)
to convert the double back to a date.
Below is a string represented a date
NSString *dateStr = #"2011-07-06";
And when I am trying to convert it to NSDate by doing :
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"yyyy-MM-dd"];
NSDate *tmp = [format dateFromString:dateStr];
NSLog(#"tmp is %#",[tmp description]);
What I am getting from the console is
tmp is 2011-07-06 04:00:00 +0000
I dont understand why I am getting extra info :04:00:00 +0000 for the result
Please help if you experienced it before
Your code
NSString *dateStr = #"2011-07-06";
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"yyyy-MM-dd"];
NSDate *tmp = [format dateFromString:dateStr]
will result in a NSDate object, that represents your local time at 0:00 — the beginning of the day.
but if you print a plain date object, it will result in a string that represents the time in GMT timezone, as internally all dates are normalized to that timezone.
As your string is 4 hours ahead, we can tell, that you most likely are in East Europe, maybe Moscow.
So if you want to see the string in your timezone, you need to use a NSDateFormatter to create it from the date object.
NSLog(#"tmp is %#",[formatter stringFromDate:tmp]);
to check, if it is correct, what I said, change the format to contain the time components.
formatter.format = [#"yyyy-MM-dd HH:mm"];
NSLog(#"tmp is %#",[formatter stringFromDate:tmp]);
The formatter will also take "oddities" like Leap Day, Leap Month (yes — those exits), Daylight Saving Times, Leap Seconds … in account — accordantly to the current calendar.
A great WWDC 2011 Video: Performing Calendar Calculations — a must-see for every cocoa developer.
BTW: to print a object with NSLog you dont need to call -description on it to pass in a string. NSLog will do this internally.
NSLog(#"tmp is %#", tmp);
is just fine.
The answer is simple, NSLog just converts the NSDate to a NSString, using its formatter with GMT (zero) timezone.
Your formatter is by default set to your default time zone, which is probably -4:00. When you print it out, NSLog converts it to 0:00, adding 4 hours.
In general, it's unsafe to parse dates without specifying their timezone.
I'm trying to convert a string to an NSDate,
however the format always comes out as nil
The date I'm trying to convert is:
2012-08-16T16:20:52.619000+00:00
The date format I'm trying is:
#"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZZZ"
If I change the date to:
#"2012-08-16T16:20:52.619000+0000" // removing the : from +00:00
it works a treat, however I would
(I have also tried
#"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZ:ZZ"
#"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZZ':'ZZ"
but that didn't work either).
Is it even possible to do this without doing string manipulation and removing the final ":"?
I did a final search around this and found out that you have to use
getObjectValue
rather than
dateFromString
In case someone else runs in to this issue, I post my method for converting such strings to NSDate
+ (NSDate *)dateFromString:(NSString *)dateString {
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSSSSSZ"];
NSDate *theDate = nil;
NSError *error = nil;
[dateFormat getObjectValue:&theDate forString:dateString range:nil error:&error];
[dateFormat release];
return theDate;
}
It looks like you are using ISO 8601 formatted dates. If you are getting these from a web service, the format changes according to the format. Check this out:
http://boredzo.org/iso8601parser/
This will convert dates according to the format, and even when the format changes slightly.
How about something like
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss'Z'"];
the Z has to be in single quotes.
I know there are many NSDateFormatter questions on here, so if I duplicate, I'm sorry. I just couldn't find anything that was quite what Im asking.
From all the questions here on SO, I have come to the conclusion that -[NSDateFormatter dateFromString:] will always return NULL if your formatter object doesn't have the correct date format. How do you get a date from a string if you don't know the format? I'm trying to get a date from a UITextField.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setLenient:YES];
NSDate *tempDate = [formatter dateFromString:self.birthdayTxtfld.text];
self.currentCustomer.birthday = ([self.birthdayTxtfld.text isEqualToString:#""]) ? NULL : tempDate;
[formatter release];
tempDate is always NULL.
I think your taking the wrong approach. I would on the other hand restrict and format the UITextField so the user has to enter the date in a specific format. Or just use a date picker. There are just way too may different inputs the user could give you.
Or you can read through this: NSDate
Another option is to create a list of accepted date formats:
#define DATEFORMATS #[#"MM/dd/yyyy", #"MM/dd/yy",...
Then Have a method that you pass the date string to and check if you can format it:
+ (NSDateFormatter*)getDateFormat:(NSString*)dateString {
NSArray *dateFormats = DATEFORMATS;
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
NSDate *date = nil;
for (NSString *dateFormat in dateFormats) {
[formatter setDateFormat:dateFormat];
date = [formatter dateFromString:dateString];
if (date) {
return formatter;
}
}
return nil;
}
If you get nil its not a date or its in a format you don't support. Otherwise you will have the correct format you need. You can switch this around to return the date instead of the format. I have it this way because I needed the format not the date for a project.
While I'm in agreement with #Jaybit that you probably need to ditch the text box and use a better input, the answer to this specific question lies in some crafty string parsing. Whenever you are doing string parsing, RegEx is your friend. Web developers end up having to do this crap all the time. This example is in JavaScript, but the RegEx ought to be portable enough that it works in ObjC:
http://www.codingoptimist.com/2009/07/using-javascript-and-regex-to-parse.html
You can do this with RegExKit or NSRegularExpression
I've read the DateFormatting guide and I'm still not able to get a working formatter.
NSString *string = #"0901Z 12/17/09";
//This is a sample date. The Z stands for GMT timezone
//The 0901 is 09h 01m on a 24 hour clock not 12.
//As long as I can get the hours/min & date from the string I can deal with the time zone later
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"hhmm'Z' MM/dd/yy"];
NSDate *date = [dateFormat dateFromString:string];
That works for me when I try it. Adding NSLog(#"%#", date) to the end of your code gives me this output:
2010-02-28 12:17:22.921 app[9204:a0f] 2009-12-17 09:01:00 -0800
What is the problem you're seeing?
Edit: I figured it out, you're not having a problem with 09:01, but with other 24-hour times, like 14:25, right? Change your formatter to:
#"HHmm'Z' MM/dd/yy"
Copied from a similar question I answered here: NSDateFormatter returns nil for #"dd-MM-yy" in iOS 3.0
If you're working with user-visible dates, you should avoid setting a date format string. Formatting dates this way is not localizable and makes it impossible to predict how your format string will be expressed in all possible user configurations. Rather, you should try and limit yourself to setting date and time styles (via -[NSDateFormatter setDateStyle:] and -[NSDateFormatter setTimeStyle:]).
On the other hand, if you're working with fixed-format dates, you should first set the locale of the date formatter to something appropriate for your fixed format. In most cases the best locale to choose is "en_US_POSIX", a locale that's specifically designed to yield US English results regardless of both user and system preferences. "en_US_POSIX" is also invariant in time (if the US, at some point in the future, changes the way it formats dates, "en_US" will change to reflect the new behaviour, but "en_US_POSIX" will not), and between machines ("en_US_POSIX" works the same on iPhone OS as it does on Mac OS X, and as it it does on other platforms).
Once you've set "en_US_POSIX" as the locale of the date formatter, you can then set the date format string and the date formatter will behave consistently for all users.
The above info and more can be found in Apple's Technical Q&A QA1480
Here's a snippet of code from my app which implements the above recommendation :
static NSDateFormatter* dateFormatter = nil;
if (!dateFormatter) {
dateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[[NSLocale alloc]
initWithLocaleIdentifier:#"en_US_POSIX"] autorelease];
NSAssert(enUSPOSIXLocale != nil, #"POSIX may not be nil.");
[dateFormatter setLocale:enUSPOSIXLocale];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
dateFormatter.dateFormat = #"EEE, dd MMM yyyy HH:mm:ss +0000";
}