NSDate、NSTimeZone、NSCalendar和 NSDateFormatter

相關資源

cocoachina日歷
本篇日歷簡單代碼

NSDate 、NSTimeZone 、NSCalendar 和 NSDateFormatter

NSDate 、NSTimeZone 、NSCalendar和NSDateFormatter這四個類或者說對象都是相互獨立的,他們之間有一定的關系,一定要認清。

  1. NSDate
// 獲取現在時間,這個時間是現在的格林威治標準時間,和時區、語言都沒有關系
[NSDate date];
  1. NSTimeZone
@property (class, readonly, copy) NSTimeZone *systemTimeZone;
@property (class, copy) NSTimeZone *defaultTimeZone;
@property (class, readonly, copy) NSTimeZone *localTimeZone

說明

  1. defaultTimeZone設置之前,和systemTimeZone共享一個timeZone對象
  2. defaultTimeZone設置之后,會影響NSDateFormatter和NSCalendar的默認時區(除非單獨設置)。當你獲取defaultTimeZone之后,獲取的zone就是一個獨立的對象了,即使你重新setDefaultTimeZone:也不會影響它。
  3. localTimeZone更像是一個單例對象(程序范圍內),這個實例變量值會跟隨defaultTimeZone的設置而變化。
  1. NSDateFormatter

// 時間戳轉換成時間的方法(返回字符串)

  • (NSString *)timeStrWithInterval:(NSString *)interval {
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:[interval doubleValue]/1000.0];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *timeString = [formatter stringFromDate:date];
    return timeString;
    }
 > 這里可以看到date格式化會受到時區影響,反向格式化也一樣。

4.  NSCalendar
> 1. 日歷,歷法,一般歷法都是遵循固定的規則的,具有周期性。日歷都是已知的或可預測的。

> 2. NSDate是獨立與任何歷法的,它只是時間相對于某個時間點的時間差;NSDate是進行日歷計算的基礎。

> 3. 下面的兩個例子(在**獲取日歷一周索引值**標題下),已經清晰反映了TimeZone和日歷的關系。
> 通過[[NSCalendar currentCalendar] setTimeZone:[NSTimeZone localTimeZone]]對日歷設置時區;


**其他重要API**

// 當前時間對應的星期幾(索引)
[[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfMonth forDate:[NSDate date]];

// 當前時間對應的周是當前月中的第幾周
[[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitWeekOfMonth inUnit:NSCalendarUnitYear forDate:[NSDate date]];
[[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitWeekOfMonth inUnit:NSCalendarUnitMonth forDate:[NSDate date]];

// 當前時間對應的周是當前年中的第幾周
[[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitWeekOfYear inUnit:NSCalendarUnitYear forDate:[NSDate date]];

#### 獲取日歷一周索引值

NSCalendar *calendar = [NSCalendar currentCalendar]; // 
NSLog(@"FirstWeekday = %ld",calendar.firstWeekday);//默認值 是 1。
[calendar setFirstWeekday:1]; //  設定日歷每周的第一天從星期幾開始,比如:如需設定從星期日開始(“開始”意味著日歷的第一天(索引為1)對應的是星期天),則value傳入1 ,如需設定從星期一開始,則value傳入2 ,以此類推。


// 這里到天day和hour
NSDateComponents *comp = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour fromDate:date];
NSLog(@"date = %@",date); // 2017-05-18 11:27:37 +0000
NSLog(@"dayComponet = %ld",comp.day);// 打印結果是18
NSLog(@"HourComponet = %ld",comp.hour);// 14H
// 從打印結果可以看出,calendar先把date加了3個小時,再處理component

// newDate
NSDate *newDate = [calendar dateFromComponents:comp];
NSLog(@"newDate = %@", newDate);// 2017-05-18 11:00:00 +0000 ,也就是說從calendar取出來的date是受NSTimeZone限制的。
// 從打印結果分析,calendar先components取出數值,再減去3小時。

// 設置為這個月的第一天
[comp setDay:1]; // 把comp的day屬性設置為一號,即comp是5月1號14H
NSLog(@"dateComponet = %ld",comp.day);// 打印結果是1
NSDate *firstDayOfMonthDate = [calendar dateFromComponents:comp];
NSLog(@"firstDayOfMonthDate = %@",firstDayOfMonthDate);// 2017-05-01 11:00:00 +0000
NSDateFormatter *fommatter = [[NSDateFormatter alloc] init];
[fommatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString *timeString = [fommatter stringFromDate:firstDayOfMonthDate];
NSLog(@"timeStr = %@",timeString);// 2017-05-01 14:00:00,這里可以看出fommatter也受時區影響。

// 經測試firstDayOfMonthDate 取值在2017-04-40 21:00:00 +0000 ~ 2017-05-01 21:00:00 +0000(不包括)之間,下面方法返回結果都一樣,所以Calendar 受TimeZone影響,會給date自動加3小時。
NSUInteger firstWeekday = [calendar ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfMonth forDate:firstDayOfMonthDate]; // 這個月第一天在當前日歷的順序
// 返回某個特定時間(date)其對應的小的時間單元(smaller)在大的時間單元(larger)中的順序位置(索引值)

// 如果這里值是2,因為前面設置的周日是第一位置,所以這里的索引2,對應的應該是周一。
NSLog(@"firstWeekday = %ld",firstWeekday);
> 本次代碼分析的是component保留hour的情況,下面gif圖是本人在第二天的運行的截圖,上面代碼分析是在18號。
> ![Calendar1.gif](http://upload-images.jianshu.io/upload_images/3448645-af67f4a9d587bd54.gif?imageMogr2/auto-orient/strip)


NSCalendar *calendar = [NSCalendar currentCalendar];
NSLog(@"FirstWeekday = %ld",calendar.firstWeekday);//默認值 是 1。
[calendar setFirstWeekday:2];

NSLog(@"date = %@",date); // 如果是5月31號21:00到6.1號21點(不包括)
// 這里只到天day
NSDateComponents *comp = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:date];
NSLog(@"dateComponet = %ld",comp.day);// 打印結果是1,component 是6.1

// newDate
NSDate *newDate = [calendar dateFromComponents:comp];
NSLog(@"newDate = %@",newDate);// 這里的結果會是5月31號 21:00:00 +0000

// 設置為這個月的第一天
[comp setDay:1]; // 把comp的day屬性設置為一號,即comp是6月1號
NSLog(@"dateComponet = %ld",comp.day);// 打印結果是1
NSDate *firstDayOfMonthDate = [calendar dateFromComponents:comp];
NSLog(@"date = %@",firstDayOfMonthDate);// 05-31 21:00:00 +0000

// 經測試firstDayOfMonthDate 取值在2017-04-40 21:00:00 +0000 ~ 2017-05-01 21:00:00 +0000(不包括)之間,下面方法返回結果都一樣。date會加3個小時,變成6月份的日歷。
NSUInteger firstWeekday = [calendar ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfMonth forDate:firstDayOfMonthDate]; // 這個月第一天在當前日歷的順序
// 返回某個特定時間(date)其對應的小的時間單元(smaller)在大的時間單元(larger)中的順序位置(索引值)

// 這里值是1,因為前面設置的周一是第一位置,所以這里的索引1,對應的就是周一。
NSLog(@"firstWeekday = %ld",firstWeekday);

> 本次代碼分析的是component只保留到day的情況 。下面gif圖是本人在第二天的運行的截圖,上面代碼分析是在18號。
> ![Calendar.gif](http://upload-images.jianshu.io/upload_images/3448645-e90a6a4e3710ebb9.gif?imageMogr2/auto-orient/strip)

> 上面兩個例子都沒有問題,關鍵是
> 
> 1. 涉及到TimeZone,對TimeZone對象的分析很關鍵。[NSTimeZone setDefaultTimeZone:[NSTimeZone timeZoneWithName:@"Europe/Vilnius"]]的設置沒在上面代碼中顯示,這是歐洲東時區三區的一個設置,也可以通過單獨設置日歷的時區對象屬性。
> 2. 第二個關鍵部分是:找到對應月份和當前月對應的第一天,只要確保月份沒有找錯(component對應的月份就是最關鍵的月份),邏輯上就是對的。 如果date(格林威治標準時間GMT([NSDate date]))是5月31號21:00,component對應的數字就是6(month)和1(day),日歷內部對應的“日期”就是6月1號00:00(以東三時區為例);component設置第一天對應的相關數值也是6(month)和1(day),然后用日歷轉化為date就是5月31號21:00,日歷內部對應的“日期”就是6月1號00:00。

> 3. 第三個關鍵部分是:找到第一個“日期”對應的星期數,用的時間(NSDate)可能是上個月的時間,比如:date(格林威治標準時間GMT([NSDate date]))是5月31號21:00,然而時間對應“日期”(6月1號00:00)才是日歷要查詢的依據(日歷會根據TimeZone自動調整date成“日期”)。

**找到日期對應月的第一天日期(更直接的方法)**
> 下面方法更直接,但是通過上面的代碼,我們可以更好地分析本月第一天會是上個月的日期的情況。

  • (NSDate *)firstDayOfCurrentMonthForDate:(NSDate *)date {
    NSDate *startDate = nil;
    BOOL ok = [[NSCalendar currentCalendar] rangeOfUnit:NSMonthCalendarUnit startDate:&startDate interval:NULL forDate:date];
    return startDate;
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容