首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

iOS NSDateFormatter 日期格式化陷阱:MM/dd/yyyy HH:mm:ss vs hh:mm:ss a 導致時間偏移

     編輯:程式設計綜合問答

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數字日期
HH24小時制 (00-23)14無 AM/PM 的時間
hh12小時制 (01-12)02配合 a (AM/PM) 使用
mm分鐘 (00-59)30分鐘
ss秒數 (00-59)45秒
aAM/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

時區陷阱:本地時區 vs UTC


// 無時區設置時,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"];

關鍵屬性解析

  • locale = en_US_POSIX:最重要。強制格式解析不受用戶「24小時制開關」「地區語言」「日曆類型」影響。沒有它,用戶開啟 24小時制會導致 hh 解析失敗。
  • timeZone = UTC:解析固定字符串 (如 API 返回、日誌時間) 時必須固定時區,避免跨時區/夏令時導致日期漂移。
  • lenient = NO:嚴格模式,拒絕「2月30日」等非法日期。

Swift 版本


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 日期解析的「鐵律」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved