相關資源
NSDate 、NSTimeZone 、NSCalendar 和 NSDateFormatter
NSDate 、NSTimeZone 、NSCalendar和NSDateFormatter這四個類或者說對象都是相互獨立的,他們之間有一定的關系,一定要認清。
- NSDate
// 獲取現在時間,這個時間是現在的格林威治標準時間,和時區、語言都沒有關系
[NSDate date];
- NSTimeZone
@property (class, readonly, copy) NSTimeZone *systemTimeZone;
@property (class, copy) NSTimeZone *defaultTimeZone;
@property (class, readonly, copy) NSTimeZone *localTimeZone
說明
- defaultTimeZone設置之前,和systemTimeZone共享一個timeZone對象
- defaultTimeZone設置之后,會影響NSDateFormatter和NSCalendar的默認時區(除非單獨設置)。當你獲取defaultTimeZone之后,獲取的zone就是一個獨立的對象了,即使你重新setDefaultTimeZone:也不會影響它。
- localTimeZone更像是一個單例對象(程序范圍內),這個實例變量值會跟隨defaultTimeZone的設置而變化。
-
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號。
> 
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號。
> 
> 上面兩個例子都沒有問題,關鍵是
>
> 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;
}