I have problem with my DateFormatter.
My iOS app communicates with server and uses If-Modified-Since header with date created with following formatter:
modifiedSinceDateFormatter = DateFormatter()
modifiedSinceDateFormatter!.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz"
modifiedSinceDateFormatter!.locale = Locale(identifier: "en_US")
modifiedSinceDateFormatter!.timeZone = TimeZone(abbreviation: "GMT")
It works as expected - returning date in following format: Fri, 08 Sep 2017 07:02:20 GMT.
But I was looking through the server logs and found that once request was made with following date format sob., 26 sie 2017 10:17:01 CEST (that's correct Polish locale and timezone - I expect my users to use Polish locale).
So my question is:
How is it possible that this formatter returned date in the wrong locale? Are there some options that user can activate to override this locale (like Accessibility options)? Can it be some jailbroken device?
EDIT: And it happened again: wt., 17 kwi 2018 08:40:02 CEST. Interesting that there was few requests (at same moment from single device) and only one of them failed - with wrong date).
I found the following explanation on how to ensure you get your date properly parsed using English names for months and days
Unless you specifically need month and/or weekday names to appear in the user's language, you should always use the special locale of en_US_POSIX. This will ensure your fixed format is actually fully honored and no user settings override your format. This also ensures month and weekday names appear in English. Without using this special locale, you may get 24-hour format even if you specify 12-hour (or visa-versa). And dates sent to a server almost always need to be in English.
I found the quote on this page
Related
This question already has answers here:
How can I parse / create a date time stamp formatted with fractional seconds UTC timezone (ISO 8601, RFC 3339) in Swift?
(13 answers)
Closed 1 year ago.
I am trying to get date format of a date that I am getting from server.
What will be the format of this date:
2021-10-14T17:53:03.753588+05:30
What I tried:
yyyy-MM-dd'T'HH:mm:ssXXX
But Its not working.
How to (my tips & steps):
When you are struggling to find the date format for String -> Date ask you this: What my format is really doing? What's it's parsing/interpreting? Just let's see with Date -> String...
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXX"
print(formatter.string(from: Date()))
Output: 2021-10-14T13:06:38+02:00: Is it the "same" as the string we have? No, some are the same, but not all...
Let's continue with another tip:
Let's put our format and the string one above the other one:
2021-10-14T17:53:03.753588+05:30
yyyy-MM-dd'T'HH:mm:ssXXX
Then, let's add "spaces", to make each pattern match its corresponding input:
2021-10-14 T 17:53:03.753588 +05:30
yyyy-MM-dd 'T' HH:mm:ss XXX
Then, let's check the doc (it's bookmarked in my web browser) for interpretation of the pattern and check if they correspond if needed, and to find the missing one if needed too.
Ok, so we aren't interpreting .753588 at all, that's why it's failing...
It's for the fractional seconds, so if we change the format to: "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", it should work. Note, you can replace XXX with Z if you want...
Now, remember that patterns are case sensitive, so if you have strange hours, minutes, or nil because of that, check if you didn't misuse minutes vs month, 12h format vs 24 hour format...
If you have hours diff (or usually 30min diff), the issue could be then a timezone issue.
If you have a day diff, it could also be a timezone issue (interpret it as hours diffs around midnight, so there is a day change).
If you have a year diff, check if you didn't misuse yyyy vs YYYY.
Etc. But that should cover most of your cases (basic issues).
Try this format.
"yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX"
As the date format you mentioned in your question contains milliseconds as well as timezone offset info.
Once date is parsed then based on your need you can set output date format and will get formatted date.
let sampleDate = "2021-10-14T17:53:03.753588+05:30"
let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX" // Set input date format.
let inputDate = dateFormatter.date(from: sampleDate)
print("Input Date:- \(inputDate)")
dateFormatter.dateFormat = "yyyy-MM-dd hh:mm:ss" // Set output date format as per need
let outputDate = dateFormatter.string(from: inputDate ?? Date())
print("Output Date:- \(outputDate)")
I have a special case to check where a user inputs the future date and time and I need to verify if that time is valid (what do I mean by valid is explained below) considering user might be affected by daylight saving in his timezone.
For Example:
Assume the user's timezone is Adelaide, Australia. Open the link to see how timezone affects in Adelaide OR see below.
4 Oct 2020 - Daylight Saving Time Starts
When local standard time is about to reach
Sunday, 4 October 2020, 2:00:00 am clocks are turned forward 1 hour to
Sunday, 4 October 2020, 3:00:00 am local daylight time instead.
Now based on the above information my understanding is if my user selected the date-time between
4 October 2020, 2:01:00 am - 4 October 2020, 2:59:00 am
it's not valid as the hour is forwarded to 3 am.
How can I validate that in an iOS app? (Assuming i)
Basically I need to inform the user that the time selected is affected by DST and users need to select a different time.
I've looked into Date and Timezone APIs and couldn't seem to find anything which can validate this.
Thanks in advance, any help would be appreciated.
I guess the easiest way would be to convert the string (or whatever the user enters, seems you are not using the UIDatePicker) into a date and check if this is possible.
For Mid-Europe, DST is starting on March 29th, 2020, so
let df = DateFormatter()
df.locale = Locale(identifier: "de_DE")
df.timeZone = TimeZone(identifier:"Europe/Berlin")
df.dateFormat="yyyy-MM-dd hh:mm"
if let theDate = df.date(from: "2020-03-29 02:01")
{
print(theDate)
} else {
print ("Illegal date")
}
will print Illegal date.
A little more tricky is switch back to non-DST, because (in Mid-Europe) there are two possible dates for 2020-10-25 02:01 - it could mean winter or summer time with two different UTC representations.
Because this post is related to comparing a given date to "today's date" keep in mind that I am posting this on a 2017-01-26
Basically, I am doing the following:
Getting an ISO DATE from the server
Transforming said date to an NSDate
using NSCalendar.currentCalendar().isDateInToday(myDate!) to actually check if that date is today.
However, it seems like I am doing something wrong, or missing something since the output seems abnormal.
I copied this output from my console:
String sent by the server --> 2017-1-27T0:00:00.000Z
Conversion I make with a dateFormatter --> (2017-01-27 00:00:00 +0000)
2017-01-27 00:00:00 +0000 is TODAY
The output above is obviously wrong. Because that is tomorrow, not today.
Now, in the following output, the date sent by the server is actually today but NSCalendar.currentCalendar().isDateInToday(myDate!) says otherwise
String sent by the server --> 2017-1-26T0:00:00.000Z
Conversion I make with a dateFormatter Optional(2017-01-26 00:00:00 +0000)
2017-01-26 00:00:00 +0000 is NOT TODAY
My question is, what could I be doing wrong?
I've double checked that my computer, the simulator and the dateFormatter.locale are properly set.
I've tried locales in string like "es_CL"
and also
dateFormatter.locale = NSLocale.currentLocale()
This is probably about the timezone. Your server returns dates is UTC, that is GMT-00:00.
If you are in Chile, your local timezone is -3 hours. Therefore today actually starts at 2017-01-26 03:00:00 +0000 and ends at 2017-01-27 03:00:00 +0000.
In that respect, the result you are seeing is completely correct. The returned daytime is not "today" in your local timezone.
To change this behavior, you can set the timezone on your calendar to UTC:
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = TimeZone(secondsFromGMT: 0)
I've noticed something weird while using date formatters. Below is the code for the date formatter.
let formatter = NSDateFormatter()
formatter.dateFormat = "EEE dd MMM h:mm a"
formatter.locale = NSLocale.systemLocale()
print(formatter.stringFromDate(NSDate()) )
The output is: "Fri 18 M03 1:05 PM". Which is kind of weird. However removing the formatter's locale gives me the output that I want: "Fri 18 Mar 1:05 PM".
I also tried printing out NSLocale.systemLocale(), and the output is an empty string. Is that normal? And what is actually happening to the date formatter when you change the locale?
FYI: I'm testing this on an actual device. And also changing the Region formats in device settings have no affect on the locale identifier.
If you want to use the system setting, should use this one NSLocale.currentLocale()
In Apple's API Document already have a point on this.
Discussion
Use the system locale when you don’t want any localizations. Use the current locale to format text that you display to users.
FYI: https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSLocale_Class/index.html#//apple_ref/occ/clm/NSLocale/systemLocale
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″.