格式化显示日期/时间的一点总结

虽然“按照一定格式显示日期/时间”这个需求已经做了很多次了,但是好像每次做的时候都是google一下人家写的代码,改改必要的东西贴进自己的代码里,其实并不知道这其中应该注意些什么,以及怎样的写法是更合适的。恰逢今天突遇了一个小bug,就认真查看了一下相关文档,把该注意的地方记了下来~


使用NSDateFormatter自带的格式来显示日期/时间

NSDateFormatter自带了几种格式,使用这些格式来显示日期/时间会受到用户在系统设置中设置的个人偏好的影响。这些格式分别是:

格式名称 对应的日期格式 对应的时间格式
NSDateFormatterNoStyle / /
NSDateFormatterShortStyle 12/13/52 3:30pm
NSDateFormatterMediumStyle Jan 12, 1952 3:30:32pm
NSDateFormatterLongStyle January 12, 1952 3:30:32pm
NSDateFormatterFullStyle Tuesday, April 12, 1952 AD 3:30:42pm PST

通过给NSDateFormatter的对象分别设置dateStyle和timeStyle,可以将NSDate对象转换成对应格式的字符串。
官方文档中有这样的示例代码:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];

NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:162000];

NSString *formattedDateString = [dateFormatter stringFromDate:date];
NSLog(@"formattedDateString: %@", formattedDateString);
// Output for locale en_US: "formattedDateString: Jan 2, 2001".

国际化

在使用NSDateFormatter的时候来格式化一个日期时,实际上用户的一些个人偏好也会默认的被考虑进去。上面的这段示例代码在我的mac(语言为简体中文,北京时间)中就会输出:

formattedDateString: 2001年1月3日

NSDateFormatter类中有一个property叫做locale,这个property会影响输出的日期格式,默认情况下,这个locale就是用户的currentLocale。
NSLocale是与国际化相关的基础类,使用[NSLocale currentLocale]可以得到当前用户关于国际化的一些设定,包括语言、日期和时间格式等。
locale不是用户的默认语言,虽然它们有时会很相似。官方文档上举了一个例子:一个居住在德国的说英语的人可能会选择英语作为他的默认语言,选择德国作为他的地区,那么系统的文字将是英文,但是日期、时间和数字可能会跟随德国习惯的格式,比如在时间上使用24小时制。

在OS X中,可以到“系统偏好设置->语言与地区”中设置当前的locale,在iOS中则可以到“设置->通用->语言与地区”中进行设置。

比如,把“日历”从“公历”改成“日本日历”,则上一段代码的输出就变成了:

平成13年1月3日

使用代码

NSLocale *locale = [dateFormatter locale];
NSCalendar *calendar = [locale objectForKey:NSLocaleCalendar];
NSLog(@"calendar: %@", calendar.calendarIdentifier);

可以查看当前dateFormatter使用的calendar类型:

calendar: japanese

所以,如果想要在显示日期/时间时排除用户的locale设置,则需要自定义一个NSLocale对象。比如对dateFormatter设置:

NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
[dateFormatter setLocale:locale];

此时,虽然我的设备的语言是简体中文,日历是日本日历,示例代码输出的却是:

formattedDateString: Jan 3, 2001

在创建NSLocale对象时需要使用localeIdentifier,例如en_US,fr_FR,ja_JP和en_GB,这些标识符包含一个语言码(例如en代表英语)和一个地区码(例如US代表美国)。
还可以在localeIdentifier中设置更多信息,比如calendar的类型:

NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN@calendar=chinese"];
[dateFormatter setLocale:locale];

得到输出:

formattedDateString: 庚辰年十二月九日

自定义格式

自定义固定的格式

通过NSDateFormatter中的setDateFormat:方法可以自定义日期/时间的格式。格式遵循Unicode Technical Standard #35
官方文档中有这样的示例代码:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd 'at' HH:mm"];
 
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:162000];
 
NSString *formattedDateString = [dateFormatter stringFromDate:date];
NSLog(@"formattedDateString: %@", formattedDateString);
// For US English, the output may be:
// formattedDateString: 2001-01-02 at 13:00
自定义用户友好的格式

好吧,在某个时刻之前,我只知道setDateFormat:这个方法,直到有一天,我发现用“MMM d”格式在英文下显示良好(比如“Jan 13”),但是换到中文,就变成了奇怪的“1月 3”。
抓狂了很久,可是设计稿里要求不显示年份,所以NSDateFormatter预设的格式都不符合这一需求。直到我google出了dateFormatFromTemplate:options:locale:这一方法。(实际上,需要显示给用户看的日期/时间不应该用setDateFormat:来设置格式,可能会出现各种问题。)

dateFormatFromTemplate:options:locale: 这个方法会重新安排给定的自定义格式,来适应指定的locale。

官方的示例代码:

NSDateFormatter *dateFormatter = [NSDateFormatter new];
NSString *localeFormatString = [NSDateFormatter dateFormatFromTemplate:@"dMMM" options:0 locale:dateFormatter.locale];
dateFormatter.dateFormat = localeFormatString;
NSString *localizedString = [dateFormatter stringFromDate:[NSDate date]];

其中,dateFormatFromTemplate:options:locale:中指定的格式只是表示了哪些元素(比如示例代码里的月份和日期)需要加入,这些元素的顺序是无关的。

官方举的几个例子:

Language (Region) Date using format string “MMM d” Date using template “dMMM”
English (United States) Nov 13 Nov 13
French (France) nov. 13 13 nov.
Chinese (China) 11月13 11月13日

Tips

  1. 需要注册Notification来监听locale和时区的改变
    这两个Notification分别是:
    NSCurrentLocaleDidChangeNotificationNSSystemTimeZoneDidChangeNotification

  2. NSDateFormatter(其实是任何一个NSFormatter)的创建都是很昂贵的,所以在实际的开发中,应该尽可能的重复利用每个formatter。推荐的做法是,每个需要用到的formatter只创建一次。

  3. 在iOS7和OS X v10.9之前,NSDateFormatter不是线程安全的。

  4. 可以通过设置dateFormatter.doesRelativeDateFormatting = YES;,让日期在某些语言下,显示为用户友好的"Today"、“Tomorrow”等。
    比如:

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterFullStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
dateFormatter.doesRelativeDateFormatting = YES;
NSString *formattedDateString = [dateFormatter stringFromDate:[NSDate date]];
NSLog(@"formattedDateString: %@", formattedDateString);

输出:

formattedDateString: 今天 下午2:11

至于到底能把“今天”、“明天”、“后天”、“昨天”、“前天”等中的几个转换成relative format,则和语言有关。
(粗略尝试了一下,中文可以支持“前天”到“后天”,英文只能支持“yesterday”到“tomorrow”)

  1. iOS8之后,NSDateFormatter(其实也包括其他一些NSFormatter的子类)新增了一个叫formattingContext的property,主要用来确定英文等语言中,输出的字符串首字母是否需要大写的问题。

参考:

Internationalization and Localization Guide: Formatting Data Using the Locale Settings
Data Formatting Guide: Date Formatters
NSHipster NSFormatter
NSHipster NSLocale

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容