格式化顯示日期/時(shí)間的一點(diǎn)總結(jié)

雖然“按照一定格式顯示日期/時(shí)間”這個(gè)需求已經(jīng)做了很多次了,但是好像每次做的時(shí)候都是google一下人家寫的代碼,改改必要的東西貼進(jìn)自己的代碼里,其實(shí)并不知道這其中應(yīng)該注意些什么,以及怎樣的寫法是更合適的。恰逢今天突遇了一個(gè)小bug,就認(rèn)真查看了一下相關(guān)文檔,把該注意的地方記了下來(lái)~


使用NSDateFormatter自帶的格式來(lái)顯示日期/時(shí)間

NSDateFormatter自帶了幾種格式,使用這些格式來(lái)顯示日期/時(shí)間會(huì)受到用戶在系統(tǒng)設(shè)置中設(shè)置的個(gè)人偏好的影響。這些格式分別是:

格式名稱 對(duì)應(yīng)的日期格式 對(duì)應(yīng)的時(shí)間格式
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

通過(guò)給NSDateFormatter的對(duì)象分別設(shè)置dateStyle和timeStyle,可以將NSDate對(duì)象轉(zhuǎn)換成對(duì)應(yīng)格式的字符串。
官方文檔中有這樣的示例代碼:

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".

國(guó)際化

在使用NSDateFormatter的時(shí)候來(lái)格式化一個(gè)日期時(shí),實(shí)際上用戶的一些個(gè)人偏好也會(huì)默認(rèn)的被考慮進(jìn)去。上面的這段示例代碼在我的mac(語(yǔ)言為簡(jiǎn)體中文,北京時(shí)間)中就會(huì)輸出:

formattedDateString: 2001年1月3日

NSDateFormatter類中有一個(gè)property叫做locale,這個(gè)property會(huì)影響輸出的日期格式,默認(rèn)情況下,這個(gè)locale就是用戶的currentLocale。
NSLocale是與國(guó)際化相關(guān)的基礎(chǔ)類,使用[NSLocale currentLocale]可以得到當(dāng)前用戶關(guān)于國(guó)際化的一些設(shè)定,包括語(yǔ)言、日期和時(shí)間格式等。
locale不是用戶的默認(rèn)語(yǔ)言,雖然它們有時(shí)會(huì)很相似。官方文檔上舉了一個(gè)例子:一個(gè)居住在德國(guó)的說(shuō)英語(yǔ)的人可能會(huì)選擇英語(yǔ)作為他的默認(rèn)語(yǔ)言,選擇德國(guó)作為他的地區(qū),那么系統(tǒng)的文字將是英文,但是日期、時(shí)間和數(shù)字可能會(huì)跟隨德國(guó)習(xí)慣的格式,比如在時(shí)間上使用24小時(shí)制。

在OS X中,可以到“系統(tǒng)偏好設(shè)置->語(yǔ)言與地區(qū)”中設(shè)置當(dāng)前的locale,在iOS中則可以到“設(shè)置->通用->語(yǔ)言與地區(qū)”中進(jìn)行設(shè)置。

比如,把“日歷”從“公歷”改成“日本日歷”,則上一段代碼的輸出就變成了:

平成13年1月3日

使用代碼

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

可以查看當(dāng)前dateFormatter使用的calendar類型:

calendar: japanese

所以,如果想要在顯示日期/時(shí)間時(shí)排除用戶的locale設(shè)置,則需要自定義一個(gè)NSLocale對(duì)象。比如對(duì)dateFormatter設(shè)置:

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

此時(shí),雖然我的設(shè)備的語(yǔ)言是簡(jiǎn)體中文,日歷是日本日歷,示例代碼輸出的卻是:

formattedDateString: Jan 3, 2001

在創(chuàng)建NSLocale對(duì)象時(shí)需要使用localeIdentifier,例如en_US,fr_FR,ja_JP和en_GB,這些標(biāo)識(shí)符包含一個(gè)語(yǔ)言碼(例如en代表英語(yǔ))和一個(gè)地區(qū)碼(例如US代表美國(guó))。
還可以在localeIdentifier中設(shè)置更多信息,比如calendar的類型:

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

得到輸出:

formattedDateString: 庚辰年十二月九日

自定義格式

自定義固定的格式

通過(guò)NSDateFormatter中的setDateFormat:方法可以自定義日期/時(shí)間的格式。格式遵循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
自定義用戶友好的格式

好吧,在某個(gè)時(shí)刻之前,我只知道setDateFormat:這個(gè)方法,直到有一天,我發(fā)現(xiàn)用“MMM d”格式在英文下顯示良好(比如“Jan 13”),但是換到中文,就變成了奇怪的“1月 3”。
抓狂了很久,可是設(shè)計(jì)稿里要求不顯示年份,所以NSDateFormatter預(yù)設(shè)的格式都不符合這一需求。直到我google出了dateFormatFromTemplate:options:locale:這一方法。(實(shí)際上,需要顯示給用戶看的日期/時(shí)間不應(yīng)該用setDateFormat:來(lái)設(shè)置格式,可能會(huì)出現(xiàn)各種問(wèn)題。)

dateFormatFromTemplate:options:locale: 這個(gè)方法會(huì)重新安排給定的自定義格式,來(lái)適應(yīng)指定的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:中指定的格式只是表示了哪些元素(比如示例代碼里的月份和日期)需要加入,這些元素的順序是無(wú)關(guān)的。

官方舉的幾個(gè)例子:

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. 需要注冊(cè)Notification來(lái)監(jiān)聽(tīng)locale和時(shí)區(qū)的改變
    這兩個(gè)Notification分別是:
    NSCurrentLocaleDidChangeNotificationNSSystemTimeZoneDidChangeNotification。

  2. NSDateFormatter(其實(shí)是任何一個(gè)NSFormatter)的創(chuàng)建都是很昂貴的,所以在實(shí)際的開(kāi)發(fā)中,應(yīng)該盡可能的重復(fù)利用每個(gè)formatter。推薦的做法是,每個(gè)需要用到的formatter只創(chuàng)建一次。

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

  4. 可以通過(guò)設(shè)置dateFormatter.doesRelativeDateFormatting = YES;,讓日期在某些語(yǔ)言下,顯示為用戶友好的"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

至于到底能把“今天”、“明天”、“后天”、“昨天”、“前天”等中的幾個(gè)轉(zhuǎn)換成relative format,則和語(yǔ)言有關(guān)。
(粗略嘗試了一下,中文可以支持“前天”到“后天”,英文只能支持“yesterday”到“tomorrow”)

  1. iOS8之后,NSDateFormatter(其實(shí)也包括其他一些NSFormatter的子類)新增了一個(gè)叫formattingContext的property,主要用來(lái)確定英文等語(yǔ)言中,輸出的字符串首字母是否需要大寫的問(wèn)題。

參考:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容