你可以使用dateFromString:方法來(lái)創(chuàng)建一個(gè)代表日期的字符串俊性,你也可以使用stringFromDate:方法把字符串解析為一個(gè)日期對(duì)象。你還可以使用getObjectValue:forString:range:error:方法對(duì)解析的字符串的范圍有更多控制侨嘀,
日期格式化器中有很多可讀寫(xiě)的屬性态辛。當(dāng)你要向用戶顯示信息的時(shí)候阶界,你通常只需要使用NSDateFormatter樣式常量即可惭墓。該常量預(yù)定義了可以決定如何格式化顯示日期的屬性坛梁。但是,如果你想要生成一個(gè)精確格式的日期腊凶,你應(yīng)該使用格式字符串(format string)划咐。
如果你需要解析日期字符串,你采用的方法取決于你想要完成的任務(wù)钧萍。如果你想要解析用戶的輸入褐缠,你通常使用樣式常量以便匹配他們的期望。如果你想解析從數(shù)據(jù)庫(kù)或網(wǎng)絡(luò)服務(wù)器得到的日期划煮,你應(yīng)該使用格式字符串送丰。
在所有的情況中缔俄,你都應(yīng)該考慮到格式化器使用用戶區(qū)域(currentLocale)在用戶偏好設(shè)置中疊加默認(rèn)值弛秋。如果你想使用用戶的區(qū)域器躏,但卻沒(méi)有它們獨(dú)特的設(shè)置時(shí),你可以通過(guò)當(dāng)前用戶的區(qū)域(localIdentifier)來(lái)獲取一個(gè)區(qū)域id蟹略,并用它來(lái)只做一個(gè)新的“標(biāo)準(zhǔn)”區(qū)域登失,然后把該標(biāo)準(zhǔn)區(qū)域設(shè)置為格式化器的locale。
使用格式化器樣式來(lái)呈現(xiàn)用戶偏好的日期和時(shí)間
NSDateFormatter可以讓你很容易的使用系統(tǒng)偏好的“國(guó)際偏好”面板中的設(shè)置來(lái)格式化日期挖炬。NSDateFormatter的樣式常量(NSDateFormatter style constants—NSDateFormatterNoStyle, NSDateFormatterShortStyle, NSDateFormatterMediumStyle, NSDateFormatterLongStyle, 和 NSDateFormatterFullStyle)指定一系列屬性揽浙,這些屬性根據(jù)用戶的偏好決定如何顯示日期。
你要分別使用setDateStyle:和setTimeStyle:方法為日期格式化器的組建指定日期和時(shí)間的格式意敛。代碼清單 1展示了你如何使用格式化器樣式格式化一個(gè)日期馅巷。注意,使用NSDateFormatterNoStyle會(huì)抑制時(shí)間組件草姻,并產(chǎn)生只包含日期的字符串钓猬。
代碼清單 1 使用格式化器樣式格式化一個(gè)日期
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".
使用格式字符串來(lái)指定自定義格式
一般來(lái)說(shuō),有兩種情況你需要使用自定義格式:
- 對(duì)于固定格式字符串撩独,例如網(wǎng)絡(luò)日期敞曹。
- 對(duì)于和任何現(xiàn)有樣式都不匹配的用戶可見(jiàn)的元素。
固定格式
想要為日期格式化器指定一個(gè)自定義的固定格式综膀,你要使用setDateFormat:澳迫。格式字符串使用來(lái)自Unicode Technical Standard #35的格式模式。不同的操作系統(tǒng)版本使用不同標(biāo)準(zhǔn)的版本:
- OS X v10.9 和 iOS 7 使用 version tr35-31.
- OS X v10.8 和 iOS 6 使用 version tr35-25.
- iOS 5 使用 version tr35-19.
- OS X v10.7 和 iOS 4.3 使用 version tr35-17.
- iOS 4.0, iOS 4.1, 和 iOS 4.2 使用 version tr35-15.
- iOS 3.2 使用 version tr35-12.
- OS X v10.6, iOS 3.0, 和 iOS 3.1 使用 version tr35-10.
- OS X v10.5 使用 version tr35-6.
- OS X v10.4 使用 version tr35-4.
雖然原則上一個(gè)格式字符串指定一個(gè)固定格式剧劝,但是默認(rèn)情況下橄登,NSDateFormatter讓人會(huì)考慮用戶的偏好(包括區(qū)域設(shè)置)。當(dāng)使用格式字符串的時(shí)候讥此,你必須考慮下面幾點(diǎn):
- NSDateFormatter會(huì)以用戶選中的日歷的方式處理你所解析的字符串中的數(shù)字示绊。例如,如果用戶選中了Buddhist日歷暂论,那么Gregorian日歷的1467會(huì)被解析生成為2010的NSDate對(duì)象面褐。(更多關(guān)于不同日歷系統(tǒng)和如何使用它們的信息,參見(jiàn)Date and Time Programming Guide取胎。)
- 在iOS中展哭,用戶可以重寫(xiě)默認(rèn)的AM/PM與24小時(shí)的時(shí)間設(shè)置。這可能導(dǎo)致你需要重寫(xiě)你設(shè)置的格式字符串闻蛀。
注意Unicode格式字符串的格式匪傍,你應(yīng)該在格式字符串中的字面量放在兩個(gè)撇號(hào)之間('')。
下面的例子說(shuō)明了使用格式字符串生成一個(gè)字符串:
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è)例子要注意兩點(diǎn):
- 它使用yyyy來(lái)指定年分組件觉痛。一個(gè)常見(jiàn)的錯(cuò)誤是使用YYYY役衡。yyyy值得年是日歷年,而YYYY指的年是ISO的年-周(year-week)日歷薪棒。d大多數(shù)情況下手蝎,yyyy和YYYY產(chǎn)生同樣的結(jié)果榕莺,但是它們也可能會(huì)不同。通常棵介,你應(yīng)該使用日歷年钉鸯。
- 時(shí)間的表示法可能是13:00。但是在iOS中邮辽,用戶可能把24小時(shí)制關(guān)閉唠雕,那么時(shí)間的現(xiàn)實(shí)可能是1:00 pm。
顯示給用戶的日期自定義格式
想要顯示一個(gè)包含特定元素設(shè)置的日期吨述,要使用dateFormatFromTemplate:options:locale:方法岩睁。該方法生成你想使用的日期組件的格式字符串,但是要使用正確的標(biāo)點(diǎn)和恰當(dāng)?shù)捻樞颍ㄒ簿褪谴г疲槍?duì)用戶的區(qū)域和偏好定制)笙僚。然后你使用格式字符串創(chuàng)建格式化器。
例如灵再,想要使用當(dāng)前的區(qū)域創(chuàng)建格式化器來(lái)顯示今天的星期肋层、日、以及月翎迁,你可以這樣寫(xiě):
NSString *formatString = [NSDateFormatter dateFormatFromTemplate:@"EdMMM" options:0 locale:[NSLocale currentLocale]];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:formatString];
NSString *todayString = [dateFormatter stringFromDate:[NSDate date]];
NSLog(@"todayString: %@", todayString);
想要理解這種需要栋猖,你要考慮要在何處顯示星期、日汪榔、以及月蒲拉。你不能使用格式化器樣式(沒(méi)有可以忽略年的樣式)來(lái)創(chuàng)建日期的這種表達(dá)。但是痴腌,使用格式字符串可以方便的始終如一的創(chuàng)建正確的表示法雌团。雖然一開(kāi)始它看上去比較簡(jiǎn)單,但是也有復(fù)雜的地方:來(lái)自美國(guó)的用戶通常期望的日期格式是“Mon, Jan 3”士聪,然而來(lái)自英國(guó)的用戶通常期望的日期格式是“Mon 31 Jan”锦援。
下面這個(gè)例子說(shuō)明了這一點(diǎn):
NSLocale *usLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
NSString *usFormatString = [NSDateFormatter dateFormatFromTemplate:@"EdMMM" options:0 locale:usLocale];
NSLog(@"usFormatterString: %@", usFormatString);
// Output: usFormatterString: EEE, MMM d.
NSLocale *gbLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_GB"];
NSString *gbFormatString = [NSDateFormatter dateFormatFromTemplate:@"EdMMM" options:0 locale:gbLocale];
NSLog(@"gbFormatterString: %@", gbFormatString);
// Output: gbFormatterString: EEE d MMM.
解析日期字符串
除了繼承于NSFormatter的方法(例如getObjectValue:forString:errorDescription:)之外,NSDateFormatter添加了dateFromString: 和 getObjectValue:forString:range:error:方法剥悟。這兩個(gè)方法可以讓你很方便的在代碼中直接使用NSDateFormatter對(duì)象灵寺,并且可以比NSString格式化更復(fù)雜更方便的方式將日期格式化成字符串。
getObjectValue:forString:range:error:方法允許你指定字符串的子串來(lái)進(jìn)行解析区岗,它返回被真實(shí)解析了的子串(在錯(cuò)誤的情況下略板,它會(huì)指出發(fā)生錯(cuò)誤的區(qū)域)。它還返回一個(gè)NSError對(duì)象慈缔,該對(duì)象包含比getObjectValue:forString:errorDescription:(繼承子NSFormatter)的錯(cuò)誤字符串更豐富的信息叮称。
如果你是用固定格式日期,你應(yīng)該首先要設(shè)置日期格式化器的locale屬性,用以匹配你的固定格式瓤檐。大多數(shù)情況下赂韵,locale最好選擇en_US_POSIX,它專(zhuān)門(mén)設(shè)計(jì)用來(lái)產(chǎn)出美國(guó)英語(yǔ)結(jié)果距帅,無(wú)論用戶和系統(tǒng)偏好如何右锨。en_US_POSIX還不可變(如果美國(guó)在未來(lái)改變了格式日期的方式括堤,en_US會(huì)做相應(yīng)改變碌秸,但是en_US_POSIX不會(huì)),且跨平臺(tái)(en_US_POSIX在iOS悄窃、OS X讥电、以及其他平臺(tái)上的表現(xiàn)是一樣的)。
一旦你把en_US_POSIX作為日期格式化器的locale轧抗,你就可以設(shè)置格式字符串恩敌,日期格式化器為用戶提供一致的行為。
代碼清單 2 展示了如何使用NSDateFormatter解決上述兩個(gè)任務(wù)横媚。首先創(chuàng)建en_US_POSIX日期格式化器來(lái)解析RFC 3339日期字符串纠炮,使用固定日期格式字符串和UTC時(shí)區(qū)。然后灯蝴,它創(chuàng)建一個(gè)標(biāo)準(zhǔn)的日期格式化器來(lái)恢口,已將日期轉(zhuǎn)化為字符串呈現(xiàn)給用戶。
代碼清單 2 解析RFC 3339日期時(shí)間
- (NSString *)userVisibleDateTimeStringForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString {
/*
Returns a user-visible date time string that corresponds to the specified
RFC 3339 date time string. Note that this does not handle all possible
RFC 3339 date time strings, just one of the most common styles.
*/
NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
[rfc3339DateFormatter setLocale:enUSPOSIXLocale];
[rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
// Convert the RFC 3339 date time string to an NSDate.
NSDate *date = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
NSString *userVisibleDateTimeString;
if (date != nil) {
// Convert the date object to a user-visible date string.
NSDateFormatter *userVisibleDateFormatter = [[NSDateFormatter alloc] init];
assert(userVisibleDateFormatter != nil);
[userVisibleDateFormatter setDateStyle:NSDateFormatterShortStyle];
[userVisibleDateFormatter setTimeStyle:NSDateFormatterShortStyle];
userVisibleDateTimeString = [userVisibleDateFormatter stringFromDate:date];
}
return userVisibleDateTimeString;
}
為了效率緩存格式化器
創(chuàng)建日期格式化器的操作會(huì)耗費(fèi)一定資源穷躁。如果你頻繁使用格式化器耕肩,通常緩存一個(gè)單例要比創(chuàng)建和處理多個(gè)實(shí)例要更有效率。其中一種方式是使用static變量问潭。
代碼清單 3 重新實(shí)現(xiàn)了在代碼清單 2的方法猿诸,用以持有日期格式化器方便以后重用。
代碼清單 3 使用緩存的格式化器來(lái)解析RFC 3339日期時(shí)間
static NSDateFormatter *sUserVisibleDateFormatter = nil;
- (NSString *)userVisibleDateTimeStringForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString {
/*
Returns a user-visible date time string that corresponds to the specified
RFC 3339 date time string. Note that this does not handle all possible
RFC 3339 date time strings, just one of the most common styles.
*/
// If the date formatters aren't already set up, create them and cache them for reuse.
static NSDateFormatter *sRFC3339DateFormatter = nil;
if (sRFC3339DateFormatter == nil) {
sRFC3339DateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
[sRFC3339DateFormatter setLocale:enUSPOSIXLocale];
[sRFC3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
[sRFC3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
}
// Convert the RFC 3339 date time string to an NSDate.
NSDate *date = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
NSString *userVisibleDateTimeString;
if (date != nil) {
if (sUserVisibleDateFormatter == nil) {
sUserVisibleDateFormatter = [[NSDateFormatter alloc] init];
[sUserVisibleDateFormatter setDateStyle:NSDateFormatterShortStyle];
[sUserVisibleDateFormatter setTimeStyle:NSDateFormatterShortStyle];
}
// Convert the date object to a user-visible date string.
userVisibleDateTimeString = [sUserVisibleDateFormatter stringFromDate:date];
}
return userVisibleDateTimeString;
}
如果你緩存了日期格式化器(或者其他任何基于用戶當(dāng)前區(qū)域的對(duì)象)狡忙,你應(yīng)該訂閱NSCurrentLocaleDidChangeNotification通知梳虽,并在當(dāng)前區(qū)域改變的時(shí)候更新你的緩存對(duì)象。代碼清單 3中的代碼在方法之外定義了sUserVisibleDateFormatter灾茁,以便其他代碼(未顯示)可以在必要時(shí)更新它怖辆。相反,sRFC3339DateFormatterDateFormatter在方法內(nèi)被定義删顶,根據(jù)設(shè)計(jì)竖螃,它不依賴(lài)于用戶的區(qū)域設(shè)置。
注意:理論上逗余,你可以使用自動(dòng)更新區(qū)域(autoupdatingCurrentLocale)來(lái)創(chuàng)建區(qū)域特咆,該區(qū)域會(huì)根據(jù)用戶的區(qū)域設(shè)置改變而自動(dòng)改變。在實(shí)踐中,它當(dāng)前還不用于日期格式化器腻格。
考慮固定格式化和非本地化日期的Unix函數(shù)
對(duì)于在固定的画拾、非本地化格式中的日期和時(shí)間,它們總是可以使用相同的日歷菜职,有時(shí)使用標(biāo)準(zhǔn)C庫(kù)函數(shù)strptime_1 和 strftime_1或許更容易也更有效率青抛。
要注意,C庫(kù)也有當(dāng)前區(qū)域的概念酬核。要想保證固定日期格式蜜另,你應(yīng)該給這些程序的loc參數(shù)傳遞NULL。這會(huì)讓它們使用POSIX區(qū)域(也被稱(chēng)為C區(qū)域)嫡意,這與Cocoa的en_US_POSIX是等價(jià)的举瑰。下例說(shuō)明了這一點(diǎn)。
struct tm sometime;
const char *formatString = "%Y-%m-%d %H:%M:%S %z";
(void) strptime_l("2005-07-01 12:00:00 -0700", formatString, &sometime, NULL);
NSLog(@"NSDate is %@", [NSDate dateWithTimeIntervalSince1970: mktime(&sometime)]);
// Output: NSDate is 2005-07-01 12:00:00 -0700