天天看点

iOS客户端NSDateFormatter那些坑

nsdateformatter 会收到用户偏好设置的影响,所以有一些坑:

有时候,我们需要把时间字符串转换为long类型的时间戳。比如下面例子:

但这里忽略了时区问题:

我们从模拟器中,“设置”-&gt; "通用" -&gt; "时间与日期" -&gt;关闭自动设置,选择"纽约"时区。上面代码计算出的time值 为<code>1454734800000</code>。 然后我们选“北京”时区,计算出的time值为 <code>1454688000000</code>显然,两个值不一样,而且在纽约时区下计算出的时间戳的值更大。

我们来看<code>timeintervalsince1970</code>函数,官方说明

整个地球分为二十四时区,每个时区都有自己的本地时间。但是在全球范围,我们需要一个标准时间。我们熟悉的标准时间是 格林尼治时间。格林尼治标准时间(中国大陆翻译:格林尼治平均时间或格林尼治标准时间,台、港、澳翻译:格林威治标准时间;英语:greenwich mean time,gmt)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义为通过那里的经线。自1924年2月5日开始,格林尼治天文台每隔一小时会向全世界发放调时信息。理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的utc。其以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间。

北京时区是<code>东八区</code>,领先utc八个小时。时区差东为正,西为负。在此,把东八区时区差记为 +0800。纽约的时区是<code>西五区</code>,比utc落后五个小时,记为 -0500. 即北京时间领先纽约时间十三个小时.

utc 时间为<code>1970年1月1号00:00:00</code>的时候。北京时间是<code>1970年1月1号8点</code>,纽约时间还是<code>1969年12月31号19:00</code>。想象,每个时区有自己的时间坐标轴,原点代表的时间点是utc的“1970年1月1号00:00:00”, 在坐标上标出各自的"2016-02-06 00:00:00"这一点,它们离原点的距离就是我们要算的时间戳。

iOS客户端NSDateFormatter那些坑

timetamp.png

显然,将"2016-02-06 00:00:00"转化为格林尼治标准的时间戳。在纽约时区计算出来的值要比北京时区大。

如果将"2016-02-06 00:00:00"是服务端(东八区)下发的时间,我们在客户端需要转为时间戳,建议,把nsdateformatter的时区设定在东八区。

iOS客户端NSDateFormatter那些坑

paste_image.png

<a href="http://my.oschina.net/yongbin45/blog/151376" target="_blank">timezone</a>

ios 设置-&gt;通用-&gt;语言与地区-&gt;日历。有<code>公历</code>、<code>日本日历</code>、<code>佛教日历</code>.公元2016年,日本日历是平成28年。佛历2560年。

所以如果用户在设置中选日本日历,上面代码计算的l时间戳又不一样了:<code>64189900800</code>。

补救方法: 手工设置nsdateformatter的日历

<a href="https://developer.apple.com/library/ios/documentation/cocoa/conceptual/dataformatting/articles/dfdateformatting10_4.html#//apple_ref/doc/uid/tp40002369-sw1" target="_blank">官网说明:</a>

hh :24小时制

hh :12 小时制

这是ios sdk3.1的bug,在设置中时区自动为纽约的时候,24小时制会自动关闭。自动为法国时区的时候,24小时制会开启。但是如果法国用户手动选择12小时制,"hh"的格式不起作用,程序返回的是"01:00 pm"这12小时类型的日期。

<a href="http://multinc.com/2009/09/27/iphone-sdk-time-bug-for-international-users/" target="_blank">参考博客:</a>

<a href="http://oleb.net/blog/2011/11/working-with-date-and-time-in-cocoa-part-2/" target="_blank">working with date and time</a>

<code>yyyy</code> is ordinary calendar year.

<code>yyyy</code> is week-based calendar year.“将这一年中第一周的周日当作今年的第一天”.因此有时结果和yyyy相同,有时就会不同

<a href="http://www.unicode.org/reports/tr35/tr35-31/tr35-dates.html#date_format_patterns" target="_blank">date field symbol table</a>

没错 0 0 ,这也有坑。比如一些日漫粉会把语言选为 "日语",关闭"24-小时制". 这时候:

这样的代码nsdate对象为null.time为0.

换"时区"或者" 地域"后手动把24-小时制关闭,还是返回null. 具体原因,暂不明白。

方法:

nslocale对象可以指定语言。

输出结果: