NSDateFormatter 会收到用户偏好设置的影响,所以有一些坑:
时区校验
有时候,我们需要把时间字符串转换为long类型的时间戳。比如下面例子:
NSString *timeStr = @"2016-02-06 00:00:00"
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate *fromdate = [format dateFromString:timeStr];
long long time = (long long)[fromdate timeIntervalSince1970];
但这里忽略了时区问题:
我们从模拟器中,“设置”-> "通用" -> "时间与日期" ->关闭自动设置,选择"纽约"时区。上面代码计算出的time值 为1454734800000
。 然后我们选“北京”时区,计算出的time值为 1454688000000
显然,两个值不一样,而且在纽约时区下计算出的时间戳的值更大。
UTC (Coordinated Universal Time)
我们来看timeIntervalSince1970
函数,官方说明
The interval between the date object and 00:00:00 UTC on 1 January 1970。根据UTC标准,计算NSDate对象距离1970年1月1号 00:00:00 的时间戳。
整个地球分为二十四时区,每个时区都有自己的本地时间。但是在全球范围,我们需要一个标准时间。我们熟悉的标准时间是 格林尼治时间。格林尼治标准时间(中国大陆翻译:格林尼治平均时间或格林尼治标准时间,台、港、澳翻译:格林威治标准时间;英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义为通过那里的经线。自1924年2月5日开始,格林尼治天文台每隔一小时会向全世界发放调时信息。理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的UTC。其以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间。
北京时区是东八区
,领先UTC八个小时。时区差东为正,西为负。在此,把东八区时区差记为 +0800。纽约的时区是西五区
,比UTC落后五个小时,记为 -0500. 即北京时间领先纽约时间十三个小时.
UTC 时间为1970年1月1号00:00:00
的时候。北京时间是1970年1月1号8点
,纽约时间还是1969年12月31号19:00
。想象,每个时区有自己的时间坐标轴,原点代表的时间点是UTC的“1970年1月1号00:00:00”, 在坐标上标出各自的"2016-02-06 00:00:00"这一点,它们离原点的距离就是我们要算的时间戳。

timetamp.png
显然,将"2016-02-06 00:00:00"转化为格林尼治标准的时间戳。在纽约时区计算出来的值要比北京时区大。
如果将"2016-02-06 00:00:00"是服务端(东八区)下发的时间,我们在客户端需要转为时间戳,建议,把NSDateFormatter的时区设定在东八区。
[format setTime Zone:[NSTimeZone timeZoneWithName:@"Asia/Shanghai"]]

Paste_Image.png
timeZone
timeZone
+ (NSArray *)knownTimeZoneNames;
+ (NSDictionary *)abbreviationDictionary;
+ (id)timeZoneWithName:(NSString *)tzName;
+ (id)timeZoneWithAbbreviation:(NSString *)abbreviation;
+ (id)timeZoneForSecondsFromGMT:(NSInteger)seconds;
日历校验
iOS 设置->通用->语言与地区->日历。有公历
、日本日历
、佛教日历
.公元2016年,日本日历是平成28年。佛历2560年。
所以如果用户在设置中选日本日历,上面代码计算的l时间戳又不一样了:64189900800
。
补救方法: 手工设置NSDateFormatter的日历
[format setCalendar: [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar]];
或者设置locale.日历可以由NSLocale 中 NSLocaleCalendar这个属性指定
官网说明:
NSDateFormatter treats the numbers in a string you parse as if they were in the user’s chosen calendar. For example, if the user selects the Buddhist calendar, parsing the year 2010 yields an NSDate object in 1467 in the Gregorian calendar. (For more about different calendrical systems and how to use them, see Date and Time Programming Guide.)
12 小时制 和24小时制
这是iOS SDK3.1的bug,在设置中时区自动为纽约的时候,24小时制会自动关闭。自动为法国时区的时候,24小时制会开启。但是如果法国用户手动选择12小时制,"HH"的格式不起作用,程序返回的是"01:00 PM"这12小时类型的日期。
参考博客:
First, a little background on the iPhone user interface. 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″.
YYYY和yyyy
working with Date and Time
Pay special attention to the year format specifier @"yyyy". It is different than the capitalized @YYYY, which represents the year of the date’s week and not the year of the day. 99% of the time, you probably want to use @”yyyy”.
Year (in "Week of Year" based calendars). Normally the length specifies the padding, but for two letters it also specifies the maximum length. This year designation is used in ISO year-week calendar as defined by ISO 8601, but can be used in non-Gregorian based calendar systems where week date processing is desired. May not always be the same value as calendar year.
Date Field Symbol Table
语言和时间制
没错 0 0 ,这也有坑。比如一些日漫粉会把语言选为 "日语",关闭"24-小时制". 这时候:
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
[format setTimeZone:[NSTimeZone timeZoneWithName:@"Asia/Shanghai"]];
[format setCalendar: [[NSCalendar alloc]
initWithCalendarIdentifier:NSGregorianCalendar]];
NSDate *fromdate = [format dateFromString:timeString];
long long time = (long long)[fromdate timeIntervalSince1970];
NSLog(@"%@", fromdate);
NSLog(@"%lld", time);
这样的代码NSDate对象为null.time为0.
换"时区"或者" 地域"后手动把24-小时制关闭,还是返回null. 具体原因,暂不明白。
此时开启"24-小时制"或者format的格式改HH为hh format setDateFormat:@"yyyy-MM-dd hh:mm:ss"]
又有值返回。12小时制和24小时制
方法:
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh"];
NSLocale对象可以指定语言。
NSLog(@"language:%@", [NSLocale preferredLanguages]);
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh"];
[format setLocale:locale];
NSLog(@"%@",[locale objectForKey:NSLocaleLanguageCode]);
输出结果:
language:(
"ja-US",
"zh-Hans-US",
"en-US"
)
2016-02-14 11:47:09.527 importDemo[3025:1406067] zh