本文主要是介绍了iOS中关于时间转化和处理。
文章分六部分介绍:
- NSDate -- 表示一个绝对的时间点
- NSTimeZone -- 时区信息
- NSLocale -- 本地化信息
- NSDateComponents -- 一个封装了具体年月日、时秒分、周、季度等的类
- NSCalendar -- 日历类,它提供了大部分的日期计算接口,并且允许您在NSDate和NSDateComponents之间转换
- NSDateFormatter -- 用来在日期和字符串之间转换
一、NSDate
用来表示公历的GMT时间(格林威治时间)
GMT 时间基准是1970-01-01 08:00:00, 而标准时间基准是2021-01-01 08:00:00
1.1 初始化
- 默认初始化,返回当前时间
- (instancetype)init;
+ (instancetype)date;
- 以当前时间的偏移秒数来初始化 eg:当前时间是2021-06-25 16:55:07 +0000,初始化是secs传20, 得到的时间就是2021-06-25 16:55:27 +0000
- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
+ (instancetype)dateWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- 以GMT时间的偏移秒数来初始化, 得到的时间是格林威治时间secs秒 eg:初始化时secs = 20,得到的时间就是1970-1-1 07:59:40 +0000
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
+ (instancetype)dateWithTimeIntervalSince1970:(NSTimeInterval)secs;
- 以基准时间2001-1-1 08:00:00的偏移秒数来初始化 eg:初始化时ti = 80, 得到的时间就是Mon Jan 1 08:01:20 2001
- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti;
+ (instancetype)dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti;
- 以sinceDate为基准时间的偏移秒数来初始化
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
+ (instancetype)dateWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
1.2 常用属性和方法
- 两个只读属性, 分别用来返回一个极早的时间点和一个极晚的时间点
// 极晚的时间 4001-01-01 08:00:00 +0000
@property (class, readonly, copy) NSDate *distantFuture;
// 极早的时间 0000-12-30 08:05:43 +0000
@property (class, readonly, copy) NSDate *distantPast;
- 返回以当前NSDate对象为基准,偏移多少秒后得到的新NSDate对象
- (instancetype)dateByAddingTimeInterval:(NSTimeInterval)ti;
- 将当前对象与参数传递的对象进行比较,根据是否相同返回BOOL值
- (BOOL)isEqualToDate:(NSDate *)otherDate;
- 比较两个NSDate对象,返回较早/较晚的时间点,并以新NSDate对象的形式返回
- (NSDate *)earlierDate:(NSDate *)anotherDate;
- (NSDate *)laterDate:(NSDate *)anotherDate;
- 将当前对象与参数传递的对象进行比较,如果相同,返回0(NSOrderedSame);对象时间早于参数时间,返回-1(NSOrderedAscending);对象时间晚于参数时间,返回1(NSOrderedDescending)
- (NSComparisonResult)compare:(NSDate *)other;
eg:output: 1 , 也就是降序, date9 值比date10 大, 即date9 比date10 更晚
NSDate *date9 = [NSDate dateWithTimeIntervalSince1970:0];
NSDate *date10 = [NSDate dateWithTimeInterval:-50 sinceDate:date9];
NSInteger result = [date9 compare:date10];
NSLog(@"%ld",(long)result);
- 返回当前对象时间与1970-1-1 08:00:00的相隔秒数,也可以这样理解:从1970-1-1 08:00:00开始,经过多少秒到达对象指定时间
@property (readonly) NSTimeInterval timeIntervalSince1970;
- 返回当前对象时间与客户端时间的相隔秒数,也可以这样理解:从客户端当前时间开始,经过多少秒到达对象指定时间
@property (readonly) NSTimeInterval timeIntervalSinceNow;
- 返回当前对象时间与2001-1-1 08:00:00的相隔秒数,也可以这样理解:从2001-1-1 08:00:00开始,经过多少秒到达对象指定时间
@property (class, readonly) NSTimeInterval timeIntervalSinceReferenceDate;
- 返回当前对象时间与参数传递的对象时间的相隔秒数,也可以这样理解:从参数时间开始,经过多少秒到达对象执行时间
- (NSTimeInterval)timeIntervalSinceDate:(NSDate *)anotherDate;
二、NSTimeZone
表示时区信息
2.1 初始化
- 根据时区名称初始化
- (nullable instancetype)initWithName:(NSString *)tzName;
+ (nullable instancetype)timeZoneWithName:(NSString *)tzName;
eg:output: America/Chicago (GMT-5) offset -18000 (Daylight) (时差5个小时, 56060)
NSTimeZone *zone = [[NSTimeZone alloc] initWithName:@"America/Chicago"];
NSLog(@"%@",zone);
- 根据时区缩写初始化。例如:EST(美国东部标准时间)、HKT(香港标准时间)
+ (nullable instancetype)timeZoneWithAbbreviation:(NSString *)abbreviation;
eg:output: America/New_York (GMT-4) offset -14400 (Daylight)
NSTimeZone *zone2 = [NSTimeZone timeZoneWithAbbreviation:@"EST"]; NSLog(@"%@",zone2);
- 返回系统时区
@property (class, readonly, copy) NSTimeZone *systemTimeZone;
eg;假如时区是上海,打印出的时区信息将会是:Asia/Shanghai (CST (China)) offset 28800,28800代表相对于GMT时间偏移的秒数,即8个小时。(86060)
NSTimeZone *zone3 = [NSTimeZone systemTimeZone];
NSLog(@"%@",zone3);
- 返回本地时区: 与systemTimeZone的区别在于本地时区可以被修改,而系统时区不能修改
@property (class, readonly, copy) NSTimeZone *localTimeZone;
- 根据零时区的秒数偏移返回一个新时区对象
+ (instancetype)timeZoneForSecondsFromGMT:(NSInteger)seconds;
2.2 常用属性和方法
- 以数组的形式返回所有已知的时区名称
@property (class, readonly, copy) NSArray<NSString *> *knownTimeZoneNames;
NSArray *zoneArray = [NSTimeZone knownTimeZoneNames];
for(NSString *str in zoneArray) {
NSLog(@"%@",str);
}
- 返回时区对象的名称或缩写
@property (readonly, copy) NSString *name;
@property (nullable, readonly, copy) NSString *abbreviation;
NSTimeZone *zone = [NSTimeZone localTimeZone];
NSString *strZoneName = [zone name];
NSString *strZoneAbbreviation = [zone abbreviation];
NSLog(@"name is %@",strZoneName);
NSLog(@"abbreviation is %@",strZoneAbbreviation);
output: name is Asia/Shanghai - abbreviation is GMT+8
- 得到当前时区与零时区的间隔秒数
@property (readonly) NSInteger secondsFromGMT;
三、NSLoale
返回本地化信息,主要体现在"语言"和"区域格式"这两个设置项
3.1 初始化
- 返回系统初始本地化信息
@property (class, readonly, copy) NSLocale *systemLocale;
eg:output: gregorian, 代表当前日历 NSCalendarIdentifierGregorian 公历
NSLocale *locale = [NSLocale systemLocale];
NSLog(@"%@",[[locale objectForKey:NSLocaleCalendar] calendarIdentifier]);
- 返回当前客户端的本地化信息
@property (class, readonly, copy) NSLocale *currentLocale;
@property (class, readonly, strong) NSLocale *autoupdatingCurrentLocale
注:这两个类方法都将返回当前客户端的本地化信息,区别在于:currentLocale取得的值会一直保持在cache中,第一次用此方法实例化对象后,即使修改了本地化设定,这个对象也不会改变。而使用autoupdatingCurrentLocale,当每次修改本地化设定,其实例化的对象也会随之改变。 eg:下面的代码演示了区别所在,假设初始本地化信息为en_CN,先用这两个函数分别初始化两个对象,然后修改本地化设定语言为台湾繁体中文,再重新打印这两个对象的信息:
NSLocale *locale1 = [NSLocale currentLocale];
NSLocale *locale2 = [NSLocale autoupdatingCurrentLocale];;
NSLog(@"before:%@",locale1.localeIdentifier);
NSLog(@"before:%@",locale2.localeIdentifier);
// output: before:en_CN
// output: before:en_CN
// --> 支持多语言且手机修改语言后
// output: before:en_CN
// output: before:zh_TW
- 用标示符初始化本地化信息
- (instancetype)initWithLocaleIdentifier:(NSString *)string;
eg:output: ¥ , 代码用"zh_CN"来初始化对象,然后再打印出对象的货币符号,得到的结果是人民币符号¥
NSLocale *locale3 = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
NSString *strSymbol = [locale3 objectForKey:NSLocaleCurrencySymbol];
NSLog(@"%@",strSymbol);
3.2 常用对象方法与类方法
- 根据不同的key返回各种本地化信息
- (nullable id)objectForKey:(NSLocaleKey)key;
// 返回了当前货币符号
NSString *strSymbol = [locale objectForKey:NSLocaleCurrencySymbol];
// 返回了当前日历
NSCalendar *calendar = [[NSLocale currentLocale] objectForKey:NSLocaleCalendar];
- 显示特定地区代号下相应键的显示名称
- (nullable NSString *)displayNameForKey:(NSLocaleKey)key value:(id)value;
NSLocale *locale1 = [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"];
NSString *str = [locale1 displayNameForKey:NSLocaleIdentifier value:@"en_US"];
NSLog(@"%@",str);
// output: 英语(美国)
// 第一句代码代表以中文来实例化对象,然后得到"en_US"的NSLocaleIdentifier键的显示名称。最后输出的结果是"英文(美国)"
四、NSDateComponents
NSDateComponents封装了具体年月日、时秒分、周、季度等, 可以通过设置这些属性来制指定一个具体的时间
NSDateComponents *compt = [[NSDateComponents alloc] init];
[compt setEra:1];
[compt setYear:2013];
[compt setMonth:3];
[compt setDay:15];
[compt setHour:11];
[compt setMinute:20];
[compt setSecond:55];
[compt setQuarter:2];
[compt setTimeZone:[NSTimeZone systemTimeZone]];
[compt setWeek:3];
[compt setWeekday:4];
[compt setWeekOfMonth:3];
[compt setWeekOfYear:2];
[compt setCalendar:[NSCalendar currentCalendar]];
五、NSCalendar
5.1 初始化
-
返回当前客户端的逻辑日历, 区别在于:
currentCalendar取得的值会一直保持在cache中,第一次用此方法实例化对象后,即使修改了系统日历设定,这个对象也不会改变
而使用autoupdatingCurrentCalendar,当每次修改系统日历设定,其实例化的对象也会随之改变
@property (class, readonly, copy) NSCalendar *currentCalendar;
@property (class, readonly, strong) NSCalendar *autoupdatingCurrentCalendar
- 根据提供的日历标示符初始化
- (nullable id)initWithCalendarIdentifier:(NSCalendarIdentifier)ident;
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierChinese];
NSLog(@"%@",calendar.calendarIdentifier);
// output: chinese
- 设置本地化信息
@property (nullable, copy) NSLocale *locale;
- 设置时区信息
@property (copy) NSTimeZone *timeZone;
5.2 NSCalendar & NSDateComponents
- 取得一个NSDate对象的1个或多个部分,用NSDateComponents来封装
- (NSDateComponents *)components:(NSCalendarUnit)unitFlags fromDate:(NSDate *)date;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *date = [NSDate date];
NSDateComponents *compt = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:date];
NSLog(@"%ld, %@",(long)[compt year], date);
NSLog(@"%ld, %@",(long)[compt month], date);
NSLog(@"%ld, %@",(long)[compt day], date);
// output: 2021, Mon Jun 28 11:33:50 2021
// output: 6, Mon Jun 28 11:33:50 2021
// output: 28, Mon Jun 28 11:33:50 2021
//需要注意的是,只有明确指定了unitFlags,NSDateComponents相应的那一部分才有值
NSCalendarUnit包含的值有:
NSEraCalendarUnit -- 纪元单位。对于NSGregorianCalendar(公历)来说,只有公元前(BC)和公元(AD);而对于其它历法可能有很多,例如日本和历是以每一代君王统治来做计算。
NSYearCalendarUnit -- 年单位。值很大,相当于经历了多少年,未来多少年。
NSMonthCalendarUnit -- 月单位。范围为1-12
NSDayCalendarUnit -- 天单位。范围为1-31
NSHourCalendarUnit -- 小时单位。范围为0-24
NSMinuteCalendarUnit -- 分钟单位。范围为0-60
NSSecondCalendarUnit -- 秒单位。范围为0-60
NSWeekCalendarUnit -- 周单位。范围为1-53
NSWeekdayCalendarUnit -- 星期单位,每周的7天。范围为1-7
NSWeekdayOrdinalCalendarUnit -- 没完全搞清楚
NSQuarterCalendarUnit -- 几刻钟,也就是15分钟。范围为1-4
NSWeekOfMonthCalendarUnit -- 月包含的周数。最多为6个周
NSWeekOfYearCalendarUnit -- 年包含的周数。最多为53个周
NSYearForWeekOfYearCalendarUnit -- 没完全搞清楚
NSTimeZoneCalendarUnit -- 没完全搞清楚
- 取得两个NSDate对象的时间间隔,用NSDateComponents来封装
- (NSDateComponents *)components:(NSCalendarUnit)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSCalendarOptions)opts;
eg:
NSCalendar *calendar2 = [NSCalendar currentCalendar];
NSDate *date1 = [NSDate date];
NSDate *date2 = [NSDate dateWithTimeInterval:5*60*60+75 sinceDate:date];
NSDateComponents *compt2 = [calendar2 components:(NSCalendarUnitMinute | NSCalendarUnitSecond) fromDate:date1 toDate:date2 options:0];
NSLog(@"%ld",(long)[compt2 minute]); // 301
NSLog(@"%ld",(long)[compt2 second]); // 14 ??? 可能在比较的时候消耗了1s
有两点需要注意:
① 得到的NSDateComponents对象可能会包含负数。例如:当toDate比fromDate晚10秒,second部分返回10;当toDate比fromDate早10秒,second部分返回-10
② 当指定unitFlags返回多个部分时,相隔的时间由多个部分共同组成。例如:上面的例子时间相差5小时1分15秒,如果指定只返回second部分,将得到18075秒;如果指定返回minute和second部分,将得到301分15秒;如果指定返回hour、minute和second,将得到5小时1分15秒。
- 根据NSDateComponents对象得到一个NSDate对象
- (nullable NSDate *)dateFromComponents:(NSDateComponents *)comps;
NSDateComponents *compt3 = [[NSDateComponents alloc] init];
[compt3 setYear:2021];
[compt3 setMonth:7];
[compt3 setDay:11];
NSCalendar *calendar3 = [NSCalendar currentCalendar];
NSDate *date3 = [calendar3 dateFromComponents:compt3];
// 2021-07-10 16:00:00 +0000
// 得到本地时区
NSTimeZone *zone3 = [NSTimeZone systemTimeZone];
// 和GMT时间相差多少秒 28800 = 60 * 60 * 8
NSInteger interval3 = [zone3 secondsFromGMTForDate:date3];
NSDate *localeDate3 = [date3 dateByAddingTimeInterval:interval3];
NSLog(@"%@",localeDate3);
// output: 2021-07-11 00:00:00 +0000
- 在参数date基础上,增加一个NSDateComponents类型的时间增量
- (nullable NSDate *)dateByAddingComponents:(NSDateComponents *)comps toDate:(NSDate *)date options:(NSCalendarOptions)opts;
5.3 NSCalendar 其他方法
系统日历为6行 * 7 列, 星期排列为:1.Sun. 2.Mon. 3.Thes. 4.Wed. 5.Thur. 6.Fri. 7.Sat.
- 设置每周的第一天从星期几开始,比如:1代表星期日开始,2代表星期一开始,以此类推。默认值是1
@property NSUInteger firstWeekday;
- 设置每年及每月第一周必须包含的最少天数,比如:设定第一周最少包括3天,则value传入3
@property NSUInteger minimumDaysInFirstWeek;
- 目标date 所在大的时间单位的的第几个小的时间单位
- (NSUInteger)ordinalityOfUnit:(NSCalendarUnit)smaller inUnit:(NSCalendarUnit)larger forDate:(NSDate *)date;
// 目标date 所在月份的第几周
NSInteger count1 = [calendar ordinalityOfUnit:NSCalendarUnitWeekOfMonth inUnit:NSCalendarUnitMonth forDate:[NSDate date]];
// 目标date 所在年份的第几周
NSInteger count2 = [calendar ordinalityOfUnit:NSCalendarUnitWeekOfYear inUnit:NSCalendarUnitYear forDate:[NSDate date]];
// 目标date 所在周内的第几天
NSInteger count3 = [calendar ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfMonth forDate:[NSDate date]];
NSInteger count4 = [calendar ordinalityOfUnit:NSCalendarUnitWeekday inUnit:NSCalendarUnitWeekOfYear forDate:[NSDate date]];
// 取"天"(Day)这个单位在这一年(Year)的取值范围 (1 -- 365)
NSRange range1 = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitYear forDate:[NSDate date]];
// "天"(Day)的取值范围。根据参数时间的月份不同,值也不同。例如2月是1--28、3月是1--31、4月是1--30。
NSRange range2 = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:[NSDate date]];
// "周"(Week)的取值范围。需要注意的是结果会受到minimumDaysInFirstWeek属性的影响。在默认minimumDaysInFirstWeek情况下,取得的范围值一般是"1--5",从日历上可以看出来这个月包含5排,即5个周。
NSRange range3 = [calendar rangeOfUnit:NSCalendarUnitWeekOfMonth inUnit:NSCalendarUnitMonth forDate:[NSDate date]];
- 返回该日期月数第一周开始的第一天, 也就是日历的本月左上角第一天
- (NSDate *)beginningOfMonth:(NSDate *)date{
NSCalendar *calendar =[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *componentsCurrentDate =[calendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay|NSCalendarUnitHour|NSCalendarUnitMinute fromDate:date];
NSDateComponents *componentsNewDate = [NSDateComponents new];
componentsNewDate.year = componentsCurrentDate.year;
componentsNewDate.month = componentsCurrentDate.month;
componentsNewDate.weekOfMonth = 1;//本月第一周
componentsNewDate.weekday = calendar.firstWeekday;//本月第一周开始的日期
componentsNewDate.hour = componentsCurrentDate.hour;
componentsNewDate.minute = componentsCurrentDate.minute;
return [calendar dateFromComponents:componentsNewDate];
}
eg: 使用如下:
NSDateComponents *dayComponent = [NSDateComponents new];
dayComponent.month = -1;
NSDate *monthDate = [calendar dateByAddingComponents:dayComponent toDate:[NSDate date] options:0];
NSLog(@"%@,%ld", monthDate, (long)dayComponent.month); // Fri May 28 18:12:16 2021,-1
NSLog(@"月初:%@", [self beginningOfMonth:monthDate]); // 月初:2021-04-25 10:12:00 +0000
dayComponent.month = 0;
NSDate *monthDate2 = [calendar dateByAddingComponents:dayComponent toDate:[NSDate date] options:0];
NSLog(@"%@,%ld", monthDate2, (long)dayComponent.month); // Mon Jun 28 18:12:16 2021,0
NSLog(@"月初:%@", [self beginningOfMonth:monthDate2]); // 月初:2021-05-30 10:12:00 +0000
dayComponent.month = 1;
NSDate *monthDate3 = [calendar dateByAddingComponents:dayComponent toDate:[NSDate date] options:0];
NSLog(@"%@,%ld", monthDate3, (long)dayComponent.month); // Wed Jul 28 18:12:16 2021,1
NSLog(@"月初:%@", [self beginningOfMonth:monthDate3]); // 月初:2021-06-27 10:12:00 +0000
- date所在当前月日历的第几周
- (void)testWeekOfMonth {
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:60 * 60 * 24 * -20];
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSDateComponents *comps = [calendar components:NSCalendarUnitWeekOfMonth fromDate:date];
NSLog(@"%ld", (long)comps.weekOfMonth);
}
iOS 系统日历就是一个月6周 (横7天 * 竖6周)
注意: 周数不同于编程思想, 不是从零开始, 是从1开始, 也就是第一行就是第一周.
weekOfMonth 代表当前时间所在当前月分日历的第几周, 通俗来讲就是第几行.
六、NSDateFormatter
用来在日期和字符串之间转换
6.1 NSDateFormatter的日期格式
G -- 纪元
一般会显示公元前(BC)和公元(AD)
y -- 年
假如是2013年,那么yyyy=2013,yy=13
M -- 月
假如是3月,那么M=3,MM=03,MMM=Mar,MMMM=March
假如是11月,那么M=11,MM=11,MMM=Nov,MMMM=November
w -- 年包含的周
假如是1月8日,那么w=2(这一年的第二个周)
W -- 月份包含的周(与日历排列有关)
假如是2013年4月21日,那么W=4(这个月的第四个周)
F -- 月份包含的周(与日历排列无关)
和上面的W不一样,F只是单纯以7天为一个单位来统计周,例如7号一定是第一个周,15号一定是第三个周,与日历排列无关。
D -- 年包含的天数
假如是1月20日,那么D=20(这一年的第20天)
假如是2月25日,那么D=31+25=56(这一年的第56天)
d -- 月份包含的天数
假如是5号,那么d=5,dd=05
假如是15号,那么d=15,dd=15
E -- 星期
假如是星期五,那么E=Fri,EEEE=Friday
a -- 上午(AM)/下午(PM)
H -- 24小时制,显示为0--23
假如是午夜00:40,那么H=0:40,HH=00:40
h -- 12小时制,显示为1--12
假如是午夜00:40,那么h=12:40
K -- 12小时制,显示为0--11
假如是午夜00:40,那么K=0:40,KK=00:40
k -- 24小时制,显示为1--24
假如是午夜00:40,那么k=24:40
m -- 分钟
假如是5分钟,那么m=5,mm=05
假如是45分钟,那么m=45,mm=45
s -- 秒
假如是5秒钟,那么s=5,ss=05
假如是45秒钟,那么s=45,ss=45
S -- 毫秒
一般用SSS来显示
z -- 时区
表现形式为GMT+08:00
Z -- 时区
表现形式为+0800
NSDateFormatter的两个最实用的方法是dateFromString和stringFromDate,前者将一个字符串经过格式化后变成NSDate对象,后者将NSDate对象格式化成字符串
- (NSString *)stringFromDate:(NSDate *)date;
- (nullable NSDate *)dateFromString:(NSString *)string;
eg: 在调用setDateFormat设置格式化字符串时,可以加入一些别的字符串,用单引号来引入
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd 'some ''special'' string' HH:mm:ss"];
七、结语
路漫漫其修远兮,吾将上下而求索~
.End