關於NSDate,NSDateFormatter,NSTimeZone的一些通俗理解

Deft_MKJing宓珂璟發表於2017-05-05

基礎概念

1.GMT 0:00 格林威治標準時間; UTC +00:00 校準的全球時間; CCD
  +08:00 中國標準時間 

2.iOS中的時間類NSDate中儲存的時間,都是相對於GMT的,我們使用NSDate
時(NSDate必須是0時區的,UTC格式的),會根據App設定的當前時區設定返回
與時區對應的資料。

3.iOS中的時區表示方法:GMT+0800 GMT-0800。(+:東區 -:西區 08:小
時數 00:分鐘數)。 GMT+0830就是指比GMT早8小時外加30分鐘的時區。

4.世界標準時間,國際協調時間,簡稱UTC。不屬於任意時區

5.系統認為NSDate是0時區的,NSString是東八區的

這裡寫圖片描述


簡單說下個人的理解 根據時區可以看到,我們在東八區,時間點比國際標準時間快八小時,而LA在在西7區,時間慢7小時,因此,同一時間下,你切換手機座標,你獲取的時間戳美國比中國的大,因為時間戳的計算都是根據0時區來算的,中國的要減去,美國自然要加上了,有個簡單的例子,如果你預設NSDate TimeZone下,你切換計算時間戳是沒有問題,但是如果你用東八區的時間戳去使用,肯定比實際上多出來八小時,這個時候如果要上傳給伺服器,那就尷尬了哈。。。如果你手動切換時區,記得算好時間戳。下面會給例子,有個很重要的思想就是,顯示的時候用NSString,但是內部涉及到計算,比較,時間戳的問題,都是要NSDate,這樣保證計算出來的東西肯定不會錯

程式碼示例一:不同時區之間的時間轉換

 // 獲取usr/share/zoneinfo目錄下所有已知的時區
    NSArray *arr = [NSTimeZone knownTimeZoneNames];

    // 獲取所有時區的縮寫
    NSDictionary *dict = [NSTimeZone abbreviationDictionary];
    NSLog(@"%@",dict);

    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd"];
    NSString *timeStr = @"2017-05-10";
    NSDate *currentDate = [dateFormatter dateFromString:timeStr];
    NSLog(@"需要格式化的時區--->%@,格式後的Date--->%@",dateFormatter.timeZone,currentDate);


    [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
    NSDate *currentDate1 = [dateFormatter dateFromString:timeStr];
    NSLog(@"需要格式化的時區--->%@,格式後的Date--->%@",dateFormatter.timeZone,currentDate1);


    [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"America/Los_Angeles"]];
    NSDate *currentDate2 = [dateFormatter dateFromString:timeStr];
    NSLog(@"需要格式化的時區--->%@,格式後的Date--->%@",dateFormatter.timeZone,currentDate2);
    /*
     2017-05-05 14:32:13.893 NSData[10583:4643459] 需要格式化的時區--->Asia/Shanghai (GMT+8) offset 28800,格式後的Date--->2017-05-09 16:00:00 +0000
     個人理解:
     時間資訊都是針對於GMT標準時區來的,那麼NSDate是根據當前設定的時區來進行計算的,那麼例如上面的時間,預設是上海東八區的話就是向前移動8小時就是0時區的時間,所以列印出來的就是少8小時

     OK,我們來設定下dateFormatter的時區為GMT和PDT(美國洛杉磯西七區),也就是NSDate預設為你就在標準時區的座標下,你給出來的時間根本不需要進行時區的轉換,直接顯示出來接即可,但是PDT的情況下是西區,時間比0區小,因此要加上7小時
     2017-05-05 15:01:52.121 NSData[10996:4659886] 需要格式化的時區--->GMT (GMT) offset 0,格式後的Date--->2017-05-10 00:00:00 +0000
     2017-05-05 15:01:52.124 NSData[10996:4659886] 需要格式化的時區--->America/Los_Angeles (PDT) offset -25200 (Daylight),格式後的Date--->2017-05-10 07:00:00 +0000
     */



這一坨簡單的程式碼和上面的圖一結合,應該非常清楚的能明白
首先,NSDate是計算0時區的時間,預設情況下NSDateFormmater是獲取當前手機設定的時區(我們們的是東八區),因此,這就解釋了預設情況下你轉換的時間NSDate比字串少了八個小時,但是如果你手動表示,你就在GMT,那就不需要轉換了,直接顯示字串時間,但是如果你說你在LA,西區,自然要加上7小時了




程式碼示例二:String和Date轉換

 // 最簡單的NSDate和NSString的轉換
    NSString *timeStr1 = @"2017-5-20";
    NSDateFormatter *dateFormatter1 = [[NSDateFormatter alloc] init];
    [dateFormatter1 setDateFormat:@"yyyy-MM-dd"];
    NSDate *date1 = [dateFormatter1 dateFromString:timeStr1];
    // 加上了這句話 轉換的時候無需再算上時區,直接轉換
    // [dateFormatter1 setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
    NSString *resultStr = [dateFormatter1 stringFromDate:date1];
    NSLog(@"%@,%@",date1,resultStr);




這簡單例子告訴我們,預設不設定時區的情況下,如果你在天朝東八區,你用的時間字串,轉換成NSDate(無論如何算出來的都是GMT/UTC國際標準時間0時期的時間)的時候會減去八小時,但是轉換成NSString的時候又會轉換成GMT標準時間下的(加上了八小時當前時期東八區)字串,系統認為NSDate是0時區的,NSString是東八區的


程式碼示例三:反面教材,可以用TimeZone進行時區切換操作更好

NSDate *date3 = [NSDate date];
    NSLog(@"NSDate(0時區標準時間 所在東八區,要減去八小時)----當前時間1%@",date3);

    NSDate *date4 = [NSDate dateWithTimeIntervalSinceNow:0];
    NSLog(@"NSDate(0時區標準時間 所在東八區,要減去八小時)----當前時間2%@",date4);

    NSDate *date5 = [NSDate dateWithTimeIntervalSinceNow:8*60*60];
    NSLog(@"NSDate(0時區標準時間 所在東八區,要減去八小時)----當前時間3加上今天此刻時間%@",date5);


    NSDate *date6 = [NSDate dateWithTimeIntervalSinceNow:(8-24)*60*60];
    NSLog(@"NSDate(0時區標準時間 所在東八區,要減去八小時)----當前時間3昨天此刻時間%@",date6);


    NSDate *date7 = [NSDate dateWithTimeIntervalSinceNow:(8+24)*60*60];
    NSLog(@"NSDate(0時區標準時間 所在東八區,要減去八小時)----當前時間3明天此刻時間%@",date7);

    // 引數1比引數2小返回的是負數
    NSTimeInterval timeIntervel = [date6 timeIntervalSinceDate:date7];
    NSLog(@"NSDate(0時區標準時間 所在東八區,要減去八小時)----當前時間3昨天和明天時間間隔%.f",timeIntervel);



程式碼示例四:時間戳秒數轉換NSDate注意點

/*
     不轉換的時候NSDate出來的肯定是沒有問題的0時區的時間
     */
    NSDate *date8 = [NSDate date];
    NSTimeInterval time8 = [date8 timeIntervalSince1970];
    NSLog(@"%.f",time8);

    NSDate *date9 = [NSDate dateWithTimeIntervalSince1970:1493968897];
    NSLog(@"%@",date9);

    // 這個例子只是說明,如果你用東八區的時間,然後你又手動修改了你當前的時區,你該到了GMT 0時區,這個時候的時間戳肯定比0時區多出八小時了,
    // 你只要明白,正確的時間戳都針對GMT 0時區的國際標準時間為準的,如果前後臺需要涉及到時間上傳互動,你如果你手動切換了時區,需要注意時區
    NSDate *date10 = [NSDate date];
    NSDateFormatter *fotmatter10 = [[NSDateFormatter alloc] init];
    [fotmatter10 setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *string10 = [fotmatter10 stringFromDate:date10];
    [fotmatter10 setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
    NSDate *date100 = [fotmatter10 dateFromString:string10];
    NSTimeInterval time10 = [date100 timeIntervalSince1970];
    NSLog(@"%.f",time10);

    NSDate *date11 = [NSDate dateWithTimeIntervalSince1970:1493998268];
    NSLog(@"%@",date11);



程式碼示例五:時間轉換格式不對等相容問題

    NSString *timeStringA = @"2017-05-05";
    NSDateFormatter *formatterA = [[NSDateFormatter alloc] init];
    [formatterA setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSDate *dateA = [formatterA dateFromString:timeStringA];
    NSString *timeStringB = @"2017-05-05 00:00:00";
    NSDateFormatter *formatterB = [[NSDateFormatter alloc] init];
    [formatterB setDateFormat:@"yyyy-MM-dd"];
    NSDate *dateB = [formatterB dateFromString:timeStringB];
    NSLog(@"-----%@",dateA);---> (null)
    NSLog(@"-----%@",dateB);--->(null)

    NSDate *dateC = [formatterB dateFromString:[self dateFormatWithoutTimeWithFormatString:@"yyyy-MM-dd" timesString:timeStringB]];
    NSLog(@"%@",dateC);2017-05-04 16:00:00 +0000


// 當時間格式和formatter格式不對等的時候會出現null的情況,這個時候你對比時間計算就會出問題
- (NSString *)dateFormatWithoutTimeWithFormatString:(NSString *)formatString timesString:(NSString *)timeStr
{
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
    NSDateFormatter *dateFormatterNoYearAndSeconds = [[NSDateFormatter alloc] init];
    [dateFormatterNoYearAndSeconds setDateFormat:formatString];
    [dateFormatterNoYearAndSeconds setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
    return [dateFormatterNoYearAndSeconds stringFromDate:[dateFormatter dateFromString:timeStr]]?:timeStr;
}

很簡單的知識點記錄下而已,網上有很多關於NSDate的擴充套件,很多都包裝好了,最近處理時間的時候列印看到了一些問題,就仔細看了下,一般來說都不會有問題,只是更加了解下時間時區之前的切換問題,明白一點就夠了,NSDate就是0時區的時間點,如果你不手動設定時區,預設我們們就是東八區,時間轉換成NSDate就會小八小時,但是轉換成NSString預設又會加上時區差,還有一點,切換時間的時候,從預設時區修改成了其他時區,你小心你的時間戳會有時區上的差異,記得最後比較下你轉換前的時區和轉換後的時區,預設的時間戳也是0時區為準的

相關文章