Force region when serializing dates to JSON from NSDicitionary? - ios

I am initializing an AFJSONRequestOperation (using the AFNetworking libraries) with a request that contains date fields. When the endpoint receives the request, the date is formatted in the region of the device. For example:
"last_update" = "dom dic 1 10:57:52 -0500 2013";
I would like to force the formatting to a region such as Canada or the US.
Is this possible as our endpoint is choking on dates that are not formatted with English such as:
"last_update" = "Wed Dec 1 10:57:52 -0500 2013";

If your API requires this, check out NSDateFormatter, which creates string representations of NSDate objects in any way you want. Just set the locale like this:
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[dateFormatter setLocale:usLocale];
Here's a good intro to this class at NSHipster.
You may find it simpler, though, to change your API to accept the number of seconds since 1 January 1970, GMT as the date. This is a common way to express date and time when transferring data between platforms. You can call the timeIntervalSince1970 method on your NSDate object to retrieve this data. Then the API can translate it to a locale, or a time zone, if it needs to.

Related

iOS NSDateFormatter needs NSLocale even it's UTC

I have a doubt that I cannot understand why is the way it is and I appeal to the Gods of this site :)
I have a date coming like this:
"1982-01-01T00:00:00Z"
As I'm displaying whatever the server sends (I know, customer requirement, not good practice...), I'm forcing the device to have that TimeZone with the following method, simplified without error checking, not optimized, and all that kind of things:
+ (NSString *) yearStringFromDate: (NSDate *) date
{
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:#"YYYY"];
[formatter setTimeZone:[self timezoneForSIHF]];
return [formatter stringFromDate:date];
}
This should be UTC, BUT if I don't set the locale I'm getting, and JUST sometimes the incorrect year. So by adding this I get the year correct for all cases:
[formatter setLocale:[NSLocale localeWithLocaleIdentifier:#"en_US_POSIX"]];
For further info, I'm testing in a real device and simulator with different languages (en, de, es).
Why is this?
Why does the locale affect the date even though the timeZone is correct?
Why sometimes is working with some dates and sometimes it's not?
For example, 1982 is returning without setting the locale, 1981 and if I set it 1982. This doesn't happen with 1980, returning in both cases 1980 (or 1987, or ...)
Thanks in advance for all your replies :D
Cheers!
When converting ISO 8601/RFC 3339 date string to NSDate object, one uses the en_US_POSIX locale in case the user is not using a Gregorian calendar. See Technical Q&A 1480.
In your case, though, you are trying to get year string representation from date object. In that case you might not need en_US_POSIX. That's only necessary if you need to use Gregorian calendar regardless of what sort of calendar the device might currently be using.
As noted by others, though, you should be using yyyy and not YYYY. The former returns the calendar year. The latter returns the year, in "Week of Year" based calendars, which may not always be the same value as calendar year.
See the date formatting patterns for a discussion contrasting y and Y.
By the way, if you really need the year component of the date, you can also use the NSDateComponents related methods of NSCalendar.
Use yyyy instead of YYYY.
Reference.

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"];

What types of dates are these?

I have a plist with this format date:
Mar 11, 2013 10:16:31 AM
which shows up in my console as
2013-03-11 16:16:31 +0000
whereas a webservice is returning something that in the console looks like this:
2013-03-01T18:21:45.231Z
How do I fix my plist date to the same format as the web service?
Regarding your three date formats:
The first is just the date format when you look at a NSDate in a plist in Xcode, a human readable date in the current locale (but if you look at the plist in a text editor, you'll see it's actually written in #"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'" format).
The second is the default formatting from the description method a NSDate (e.g. you NSLog a NSDate).
The third is RFC 3339/ISO 8601 format (with fractions of a second), often used in web services.
See Apple's Technical Q&A QA1480.
As an aside, that Technical Note doesn't include the milliseconds, so you might want to use something like #"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'", for example:
NSDate *date = [NSDate date];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = enUSPOSIXLocale;
formatter.dateFormat = #"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'SSS'Z'";
formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
NSString *dateString = [formatter stringFromDate:date];
NSLog(#"%#", dateString);
You can use this if you want to store the date as a string in RFC 3339/ISO 8601 format in the plist (or alternatively, if you need to convert a NSDate to a string for transmission to your web service). As noted above, the default plist format does not preserve fractions of a second for NSDate objects, so if that's critical, storing dates as strings as generated by the above code can be useful.
And this date formatter can be used for converting dates to strings (using stringFromDate), as well as converting properly formatting strings to NSDate objects (using dateFromString).

Is this reference or code in mistake or bug?

I copied some text from NSDate Reference as below, please check Return Value,
it is said the format will be in YYYY-MM-DD HH:MM:SS ±HHMM, but I got as below
in my app, so the reference is mistake? or code in mistake?
Saturday, January 1, 2011 12:00:00 AM Japan Standard Time
or
2011年1月1日土曜日0時00分00秒 日本標準時
descriptionWithLocale:
Returns a string representation of the receiver using the given locale.
- (NSString *)descriptionWithLocale:(id)locale
Parameters
locale
An NSLocale object.
If you pass nil, NSDate formats the date in the same way as the description
method.
On Mac OS X v10.4 and earlier, this parameter was an NSDictionary object.
If you pass in an NSDictionary object on Mac OS X v10.5, NSDate uses the
default user locale—the same as if you passed in [NSLocale currentLocale].
Return Value
A string representation of the receiver, using the given locale, or if the
locale argument is nil, in the international format YYYY-MM-DD HH:MM:SS ±HHMM,
where ±HHMM represents the time zone offset in hours and minutes from GMT (for
example, “2001-03-24 10:45:32 +0600”)
If you're looking for a date in the YYYY-MM-DD HH:MM:SS ±HHMM format, pass in 'nil' for the locale; otherwise, pass in your locale.

Resources