我们的 iOS APP 有一个小 Bug,场景简化后是这样:
接口返回一个时间字符串,APP 里比较它与当前时间,如果当前时间晚于它,就显示一个按钮,否则不显示。
本来是一个很简单的逻辑,但是,有一部分用户反馈,按钮该显示的时候却没有显示。
分析
结合用户反馈的信息,经过多次尝试后,才发现这个行为竟然与用户手机的时间制式有关——如果用户手机设置里的 24小时制 开关没有打开,那么这个 Bug 就会出现。
相关的逻辑是这样写的:
NSDate *remoteDate = [NSDate dateFromStr:remoteDateString]; if (remoteDate) { // 比较 remoteDate 和 本地当前时间,控制按钮显隐 }
这个 dateFromStr:
是一个 category 方法,实现是这样的:
+ (NSDate*)dateFromStr:(NSString *)dateStr { NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; return [dateFormatter dateFromString:dateStr]; }
经过调试,发现 remoteDate
在 24小时制 开关关闭时,返回的是 nil
,而在打开时,返回的是正确的时间。
苹果官方文档里,NSDateFormatter
的 dateFromString:
方法是这样描述的:
Returns a date representation of a given string interpreted using the receiver’s current settings.
Return Value
A date representation of string. If dateFromString: can’t parse the string, returns nil.
同时还给出了 Working With Fixed Format Date Representations 的参考链接,里面有说明:
When working with fixed format dates, such as RFC 3339, you set the dateFormat property to specify a format string. For most fixed formats, you should also set the locale property to a POSIX locale ("en_US_POSIX"), and set the timeZone property to UTC.
这个页面里还给出了一个 QA 链接 Technical Q&A QA1480 “NSDateFormatter and Internet Dates”,里面有这样的描述:
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.
...
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.
里面提到了用户可以通过设置 24小时制 来影响 NSDateFormatter
的行为,还提到了当尝试把固定格式的日期字符串转换成日期对象时,应该设置 locale
。
至此破案了,这个 Bug 就是由于没有设置 NSDateFormatter
的 locale
属性导致的。
解决
修改后的代码是这样的,仅加了一行 locale
设置:
+ (NSDate*)dateFromStr:(NSString *)dateStr { NSDateFormatter * dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]]; return [dateFormatter dateFromString:dateStr]; }
经过测试功能正常了,不管用户手机的 24小时制 开关是否打开,都能正常解析服务端返回的时间字符串了。