iOS 应用偶尔会向 REST 发送格式无效的 ISO8601 日期 API
iOS app occasionally sends an invalidly-formatted ISO8601 date to REST API
一天几次,我们的 PHP REST API 会记录一个错误,该错误是由格式无效的 ISO8601 日期引起的,来自我们的 iOS 应用程序发送的 GET 请求.有趣的是,大多数电话都很好(例如 2015-07-07T00:00:00+10:00),但每隔一段时间我们就会收到一个奇怪的电话(例如 2015-07-07T12:00:00上午+10:00).
我认为导致此问题的代码如下:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
NSString *iso8601StringStart = [dateFormatter stringFromDate:self.searchStartTime];
是否存在 NSDateFormatter
以某种方式(错误地)从 "yyyy-MM-dd'T'HH:mm:ssZZZZZ" 获取 am/pm 的情况,而这显然是意外行为?是否有某些类型的 NSDate 会导致不同的行为?我很难过。给出的日期总是通过 dateFromComponents
.
创建
我不相信该格式字符串可以生成带有您显示的 am/pm 注释的日期。如果我是你,我的第一门课程是仔细检查这些日期是否真的是由这些代码行生成的。
但是,如果您确定会发生这种情况,我能看到的唯一问题是 可能不正确,因为您没有明确设置区域设置和日历日期格式化程序对象。日期格式语法由 unicode 联盟定义,governing spec 在 4.5 节中确实说 "If locales are not listed, dayPeriods fallback to AM/PM"。我不理解整个文档,但它表明非常明确是最安全的路径。
如果您的唯一要求是 ISO8601,那么您可以在 UTC 时区使用 RFC3339,因为这是 ISO8601 的配置文件。这会为该格式创建一个正确的格式化程序:
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")
我的最终解决方案(我被 algal 的回答推动了):
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
Unicode spec was helpful (thanks algal), as was this Apple Technical QA,其中建议en_US_POSIX作为具体解决方案。
"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."
最有帮助的是,我找到了this explanation of the behaviour by huyz,虽然有点旧:
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″.
一天几次,我们的 PHP REST API 会记录一个错误,该错误是由格式无效的 ISO8601 日期引起的,来自我们的 iOS 应用程序发送的 GET 请求.有趣的是,大多数电话都很好(例如 2015-07-07T00:00:00+10:00),但每隔一段时间我们就会收到一个奇怪的电话(例如 2015-07-07T12:00:00上午+10:00).
我认为导致此问题的代码如下:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
NSString *iso8601StringStart = [dateFormatter stringFromDate:self.searchStartTime];
是否存在 NSDateFormatter
以某种方式(错误地)从 "yyyy-MM-dd'T'HH:mm:ssZZZZZ" 获取 am/pm 的情况,而这显然是意外行为?是否有某些类型的 NSDate 会导致不同的行为?我很难过。给出的日期总是通过 dateFromComponents
.
我不相信该格式字符串可以生成带有您显示的 am/pm 注释的日期。如果我是你,我的第一门课程是仔细检查这些日期是否真的是由这些代码行生成的。
但是,如果您确定会发生这种情况,我能看到的唯一问题是 可能不正确,因为您没有明确设置区域设置和日历日期格式化程序对象。日期格式语法由 unicode 联盟定义,governing spec 在 4.5 节中确实说 "If locales are not listed, dayPeriods fallback to AM/PM"。我不理解整个文档,但它表明非常明确是最安全的路径。
如果您的唯一要求是 ISO8601,那么您可以在 UTC 时区使用 RFC3339,因为这是 ISO8601 的配置文件。这会为该格式创建一个正确的格式化程序:
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")
我的最终解决方案(我被 algal 的回答推动了):
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
Unicode spec was helpful (thanks algal), as was this Apple Technical QA,其中建议en_US_POSIX作为具体解决方案。
"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."
最有帮助的是,我找到了this explanation of the behaviour by huyz,虽然有点旧:
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″.