iOS NSDateFormatter 格式化字符串區分大小寫,HH (24小時制) vs hh (12小時制) + a (AM/PM) 是最常見錯誤源。原問題中輸入 7/25/2013 12:00:00 AM 但格式用 HH:mm:ss 導致解析偏移。
// 輸入: "7/25/2013 12:00:00 AM" (12小時制帶 AM/PM)
// ❌ 格式錯誤:HH 是 24小時制,不識別 AM/PM
[formatter setDateFormat:@"MM/dd/yyyy HH:mm:ss"];
// 結果偏移 12小時或解析失敗
| 符號 | 含義 | 範例 | 適用場景 |
|---|---|---|---|
| yyyy | 年份 (4位) | 2024 | 標準年份 |
| MM | 月份 (01-12) | 07 | 數字月份 |
| dd | 日期 (01-31) | 25 | 數字日期 |
| HH | 24小時制 (00-23) | 14 | 無 AM/PM 的時間 |
| hh | 12小時制 (01-12) | 02 | 配合 a (AM/PM) 使用 |
| mm | 分鐘 (00-59) | 30 | 分鐘 |
| ss | 秒數 (00-59) | 45 | 秒 |
| a | AM/PM 標記 | PM | 必須配合 hh 使用 |
| SSS | 毫秒 (000-999) | 123 | 高精度時間戳 |
NSString *dateStr = @"7/25/2013 12:00:00 AM";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
// ✅ 正確:12小時制 hh + AM/PM 標記 a
[formatter setDateFormat:@"MM/dd/yyyy hh:mm:ss a"];
// 關鍵:設置時區為 GMT/UTC,避免本地時區偏移導致日期變化
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDate *date = [formatter dateFromString:dateStr];
NSLog(@"date: %@", date); // 正確輸出 2013-07-25 00:00:00 +0000
// 無時區設置時,formatter 默認使用設備本地時區
// 輸入 "7/25/2013 12:00:00 AM" 被視為本地時間
// 輸出 NSDate 內部存儲 UTC 時間 → 顯示時轉換回本地 → 可能出現「前一天 18:30」現象
// 最佳實踐:解析固定格式字符串時,強制 UTC
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
// 或
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
- (NSDate *)parseDateString:(NSString *)dateString format:(NSString *)format {
static NSDateFormatter *formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; // 關鍵:強制 24小時制/英文月份,避免用戶地區設置干擾
formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; // 固定 UTC
});
formatter.dateFormat = format;
return [formatter dateFromString:dateString];
}
// 使用
NSDate *date = [self parseDateString:@"7/25/2013 12:00:00 AM" format:@"MM/dd/yyyy hh:mm:ss a"];
hh 解析失敗。
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "MM/dd/yyyy hh:mm:ss a"
if let date = formatter.date(from: "7/25/2013 12:00:00 AM") {
print(date) // 2013-07-25 00:00:00 +0000
}
總結:輸入含 AM/PM 必用 hh:mm:ss a,固定格式解析必加 en_US_POSIX locale + UTC 時區。這是 iOS 日期解析的「鐵律」。