日期与时间(NSDate)
NSDate对象代表日期和时间。Objective-C既提供了类方法来创建NSDate对象,也提供了大量以init开头的方法来初始化NSDate对象。关于NSDate类各个方法,可以参考NSDate的参考文档。下面通过示例程序介绍NSDate类的常见方法的用途。
#import<Foundation/Foundation.h>
int main(int argc, char * argv[])
{
@autoreleasepool{
//获取代表当前日期、时间的NSDate
NSDate* date1 = [NSDate date];
NSLog(@"%@", date1);
//获取从当前时间开始一天之后的日期
NSDate* date2 = [[NSDate alloc]initWithTimeIntervalSinceNow:3600*24];
NSLog(@"%@", date2);
//获取从当前时间开始,3天之前的日期
NSDate* date3 = [[NSDate alloc]initWithTimeIntervalSinceNow:-3*3600*24];
NSLog(@"%@", date3);
//获取从1970年1月1日开始,20年之后的日期
NSDate* date4 = [NSDate dateWithTimeIntervalSince1970:3600 * 24 * 366 * 20];
NSLog(@"%@", date4);
//获取系统当前的NSLocale
NSLocale* cn = [NSLocale currentLocale];
//获取NSDate在当前的NSLocale下对应的字符串
NSLog(@"%@", [date1 descriptionWithLocale:cn]);
//获取两个日期之间较早的日期
NSDate* earlier = [date1 earlierDate:date2];
//获取两个日期之间较晚的日期
NSDate* later = [date1 laterDate:date2];
//比较两个日期,compare: 方法返回NSComparisonResult枚举值
//该枚举类型包含NSOrderedAscending、NSOrderedSame和
//NSOrderedDescending三个值
//分别代表调用compare:的日期位于被比较日期之前、相同、之后
switch([date1 compare:date3]){
case NSOrderedAscending:
NSLog(@"date1位于date3之前");
break;
case NSOrderedSame:
NSLog(@"date1与date3日期相等");
break;
case NSOrderedDescending:
NSLog(@"date1位于date3之后");
break;
}
//获取两个时间之间的时间差
NSLog(@"date1与date3之间时间差%g秒", [date1 timeIntervalSinceDate:date3]);
//获取指定时间与现在时间差
NSLog(@"date2与现在时间差%g秒", [date2 timeIntervalSinceNow]);
}
return 0;
}
从上面的代码可以看出,创建NSDate的类方法与实例方法基本类似,只是类方法以date开头而实例方法以init开头。一旦得到NSDate对象,两个NSDate之间就可以比较大小,可以计算两个NSDate之间的时间差,也可以把NSDate转换为符合当前NSLocale的格式字符串。
编译、运行后,可看到如下输出:
2022-05-29 20:33:00.641475+0800 练习[29683:1288641] Sun May 29 20:33:00 2022
2022-05-29 20:33:00.641600+0800 练习[29683:1288641] Mon May 30 20:33:00 2022
2022-05-29 20:33:00.641620+0800 练习[29683:1288641] Thu May 26 20:33:00 2022
2022-05-29 20:33:00.641639+0800 练习[29683:1288641] Tue Jan 16 08:00:00 1990
2022-05-29 20:33:00.649731+0800 练习[29683:1288641] 2022年5月29日 星期日 中国标准时间 下午8:33:00
2022-05-29 20:33:00.649774+0800 练习[29683:1288641] date1位于date3之后
2022-05-29 20:33:00.649795+0800 练习[29683:1288641] date1与date3之间时间差259200秒
2022-05-29 20:33:00.649810+0800 练习[29683:1288641] date2与现在时间差86400秒
Program ended with exit code: 0
日期格式器(NSDateFormatter)
NSDateFormatter代表一个日期格式器,他的功能就是完成NSDate与NSString之间的转换。
使用NSDateFormatter完成NSDate与NSString之间转换的步骤如下。
- 创建一个NSDateFormatter对象。
- 调用NSDateFormatetr的setDateStyle:、setTimeStyle:方法设置格式化日期、时间的风格。其中,日期、风格支持如下几个枚举值。
- NSDateFormatterNoStyle:不显示日期、时间的风格。
- NSDateFormatterShortStyle:显示“短”的日期、时间风格。
- NSDateFormatterMediiumStyle:显示“中等”的日期、时间风格。
- NSDateFormatterLongStyle:显示“长”的日期、时间风格。
- NSDateFormatterFullStyle:显示“完整”的日期、时间风格。
如果打算使用自己的格式模板,则调用NSDateFormatter的setDFateFormat:方法设置日期、时间的模板即可。
- 如果需要将NSDate转换为NSString,则调用NSDateFormatter的stringFromDate:方法执行格式化即可;如果需要将NSString转换为NSDate,则调用NSDateFormatter的dataFromString:方法执行格式化即可。
如下程序示范了NSDateFormatter的功能和用法。
#import<Foundation/Foundation.h>
int main(int argc, char * argv[]) {
@autoreleasepool{
//获取被格式化的时间
//获取从1970年1月1日开始,20年之后的日期
NSDate* dt = [NSDate dateWithTimeIntervalSince1970:3600 * 24 * 366 * 20];
//创建两个NSLocale,分别代表中国、美国
NSLocale* locale[] = {
[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"], [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]};
NSDateFormatter* df[8];
// 为上面两个NSLocale创建8个NSDateFormatter对象
for (int i = 0; i < 2; i++) {
df[i * 4] = [[NSDateFormatter alloc] init];
// 设置NSDateFormatter的日期、时间风格
[df[i * 4] setDateStyle: NSDateFormatterShortStyle];
[df[i * 4] setTimeStyle: NSDateFormatterShortStyle];
//设置NSDateFormatter的NSLocale
[df[i * 4] setLocale: locale[i]];
df[i * 4 + 1] = [[NSDateFormatter alloc] init];
// 设置NSDateFormatter的日期、时间风格
[df[i * 4 + 1] setDateStyle: NSDateFormatterShortStyle];
[df[i * 4 + 1] setTimeStyle: NSDateFormatterShortStyle];
//设置NSDateFormatter的NSLocale
[df[i * 4 + 1] setLocale: locale[i]];
df[i * 4 + 2] = [[NSDateFormatter alloc] init];
// 设置NSDateFormatter的日期、时间风格
[df[i * 4 + 2] setDateStyle: NSDateFormatterShortStyle];
[df[i * 4 + 2] setTimeStyle: NSDateFormatterShortStyle];
//设置NSDateFormatter的NSLocale
[df[i * 4 + 2] setLocale: locale[i]];
df[i * 4 + 3] = [[NSDateFormatter alloc] init];
// 设置NSDateFormatter的日期、时间风格
[df[i * 4 + 3] setDateStyle: NSDateFormatterShortStyle];
[df[i * 4 + 3] setTimeStyle: NSDateFormatterShortStyle];
//设置NSDateFormatter的NSLocale
[df[i * 4 + 3] setLocale: locale[i]];
}
for ( int i = 0; i < 2; i++) {
switch (i) {
case 0:
NSLog(@"-------中国日期格式-------");
break;
case 1:
NSLog(@"-------美国日期格式-------");
break;
}
NSLog(@"SHORT格式的日期格式:%@", [df[i * 4] stringFromDate:dt]);
NSLog(@"MEDIUM格式的日期格式:%@", [df[i * 4 + 1]stringFromDate:dt]);
NSLog(@"LONG格式的日期格式:%@", [df[i * 4 + 2]stringFromDate:dt]);
NSLog(@"FULL格式的日期格式:%@", [df[i * 4 + 3]stringFromDate:dt]);
}
NSDateFormatter* df2 = [[NSDateFormatter alloc] init];
//设置自定义的格式模板
[df2 setDateFormat:@"公元yyyy年MM月DD日 HH时 mm分"];
//执行格式化
NSLog(@"%@", [df2 stringFromDate:dt]);
NSString* dateStr = @"2013-03-02";
NSDateFormatter* df3 = [[NSDateFormatter alloc] init];
// 根据日期字符串的格式设置格式模板
[df3 setDateFormat:@"yyyy-MM-dd"];
//将字符串转换为NSDate对象
NSDate* date2 = [df3 dateFromString: dateStr];
NSLog(@"%@", date2);
}
return 0;
}
从上面的代码可以看出,创建NSDate的类方法与实例方法基本类似,只是类方法以date开头,而实例方法以init开头。一旦得到NSDate对象,两个NSDate之间就可以比较大小,可以计算两个NSDate之间的时间差,也可以把NSDate转换为符合当前NSLocale的格式字符串。
编译运行该程序,可以看到如下输出:
2022-05-31 19:38:49.226363+0800 练习[51468:2180178] -------中国日期格式-------
2022-05-31 19:38:49.239758+0800 练习[51468:2180178] SHORT格式的日期格式:1990/1/16 上午8:00
2022-05-31 19:38:49.239914+0800 练习[51468:2180178] MEDIUM格式的日期格式:1990/1/16 上午8:00
2022-05-31 19:38:49.240017+0800 练习[51468:2180178] LONG格式的日期格式:1990/1/16 上午8:00
2022-05-31 19:38:49.240111+0800 练习[51468:2180178] FULL格式的日期格式:1990/1/16 上午8:00
2022-05-31 19:38:49.240131+0800 练习[51468:2180178] -------美国日期格式-------
2022-05-31 19:38:49.240450+0800 练习[51468:2180178] SHORT格式的日期格式:1/16/90, 8:00 AM
2022-05-31 19:38:49.240604+0800 练习[51468:2180178] MEDIUM格式的日期格式:1/16/90, 8:00 AM
2022-05-31 19:38:49.240731+0800 练习[51468:2180178] LONG格式的日期格式:1/16/90, 8:00 AM
2022-05-31 19:38:49.240856+0800 练习[51468:2180178] FULL格式的日期格式:1/16/90, 8:00 AM
2022-05-31 19:38:49.242872+0800 练习[51468:2180178] 公元1990年01月16日 08时 00分
2022-05-31 19:38:49.243773+0800 练习[51468:2180178] Sat Mar 2 00:00:00 2013
Program ended with exit code: 0
上面程序示范了如何完成NSDate与NSString之间的转换,并分别示范了在美式英语环境和简体中文环境下将NSDate转换为NSString的效果。
日历(NSCalendar)与日期组件(NSDateComponents)
前面介绍了NSDate的功能和用法,以及NSDate与NSString之间的相互转换。假如有如下场景:程序提供了3个输入框,让用户分别输入年、月、日的数值,接下来需要将年、月、日数值转换为NSDate;另一个场景:得到一个NSDate对象之后,程序需要获取该NSDate中包含的年份、月份、第几日。这些都需要将NSDate对象的各个字段数据分开提取。
为了能分开处理NSDate对象所包含的各个字段的数据,Foundation框架提供了NSCalendar对象,该对象包含如下两个常用方法。
- (NSDateComponents*)components:from:Date:从NSDate中提取年、月、日、时、分、秒各时间字段的信息。
- dateFromComponents:(NSDateComponents*)comps:使用comps:对象包含的年、月、日、时、分、秒各时间字段的信息来创建NSDate。
上面两个方法都用到一个NSDateComponents对象,该对象专门用于封装年、月、日、时、分、秒各时间字段的信息。该对象非常简单,它只包含了对year、month、date、day、hour、minute、second、week、weekday等各字段的setter和getter方法。
下面示范了如何利用NSCalendar和NSDateComponents分开处理NSDate中各时间字段的数值。
#import<Foundation/Foundation.h>
int main(int argc, char * argv[]) {
@autoreleasepool{
//获取代表公历的NSCalendar对象
NSCalendar * gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
//获取当前日期
NSDate* dt = [NSDate date];
//定义一个时间字段的旗标,指定将会获取指定年、月、日、时、分、秒的信息
unsigned unitFlags = NSCalendarUnitYear |
NSCalendarUnitMonth | NSCalendarUnitDay |
NSCalendarUnitHour | NSCalendarUnitMinute |
NSCalendarUnitSecond | NSCalendarUnitWeekday;
// 获取不同时间字段的信息
NSDateComponents* comp = [gregorian components: unitFlags fromDate: dt];
//获取不同时间字段的数值
NSLog(@"现在是%ld年", comp.year);
NSLog(@"现在是%ld月", comp.month);
NSLog(@"现在是%ld日", comp.day);
NSLog(@"现在是%ld时", comp.hour);
NSLog(@"现在是%ld分", comp.minute);
NSLog(@"现在是%ld秒", comp.second);
NSLog(@"现在是星期%ld", comp.weekday);
//再次创建一个NSDateComponts对象
NSDateComponents* comp2 = [[NSDateComponents alloc] init];
//设置各时间字段的数值
comp2.year = 2013;
comp2.month = 4;
comp2.day = 5;
comp2.hour = 18;
comp2.minute = 34;
//通过NSDateComponents所包含各时间字段的数值来恢复NSDate对象
NSDate * date = [gregorian dateFromComponents:comp2];
NSLog(@"获取的日期为:%@", date);
}
return 0;
}
在上面的程序中,第一行代码示范了从NSDate对象中获取个时间字段的数值;第二行粗体字代码示范了如何利用各时间字段的数值来初始化NSDate对象。
编译、运行该程序,可以看到如图所示结果。
2022-05-31 21:19:46.656723+0800 练习[52394:2228931] 现在是2022年
2022-05-31 21:19:46.656904+0800 练习[52394:2228931] 现在是5月
2022-05-31 21:19:46.656928+0800 练习[52394:2228931] 现在是31日
2022-05-31 21:19:46.656945+0800 练习[52394:2228931] 现在是21时
2022-05-31 21:19:46.656958+0800 练习[52394:2228931] 现在是19分
2022-05-31 21:19:46.656970+0800 练习[52394:2228931] 现在是46秒
2022-05-31 21:19:46.656983+0800 练习[52394:2228931] 现在是星期3
2022-05-31 21:19:46.662194+0800 练习[52394:2228931] 获取的日期为:2013-04-05 10:34:00 +0000
Program ended with exit code: 0
定时器(NSTimer)
如果程序需要让某个方法重复执行,则可以借助Objective-C的定时器来完成。使用Objective-C的定时器编程很简单,具体步骤如下。
- 调用NSTimer的scheduledTimerWithTimeInterval:invocation:repeats:或scheduleTimerWithTimeInterval:target:selector:userInfo:repeats:类方法来创建NSTimer对象。调用该类方法时,需要传入如下参数。
- timeInterval:指定每隔多少秒执行一次任务。
- invocation或target与selector:指定重复执行的任务。如果指定target和selector参数,则指定用某个对象的特定方法作为重复执行的任务;如果指定invocation参数,该参数需要传入英国NSInvocation对象,该NSInvocation对象也是封装target和selector的,其实也是指定某个对象的特定方法作为重复执行的任务/
userInfo: 该参数用于传入额外的附加信息。
repeats:该参数需要指定一个BOOL值,该参数控制是否需要重复执行任务
- 为第1步的任务编写方法。
- 销毁定时器。调用定时器的invalidate方法即可。
为了测定定时器的作用,此处将前面介绍的DelegateTest应用进行一些修改。在应用加载时启动一个定时器。
在该应用的委托类FKAppDelegate中applicationWillFinishLaunching:方法的最后增加如下代码。
_timer = [NSTimer scheduleTimeWithTimeInterval:0.5
target:self//指定以当前对象的info:方法作为执行任务
selector:@selector(info:)
userInfo:nil
repeats:YES];//指定重复执行
上面代码中指定以当前对象的info:方法作为任务执行体,因此,程序还需要在该类中定义一个info:方法,该方法可定义一个NSTimer参数,该参数代表调度该任务的定时器。
增加的info:方法代码如下。
- (void) info:(NSTimer*) timer {
NSLog(@"正在执行第%ld次任务", _count++);
//如果_count的值大于10,则取消定时器
if(_count > 10) {
NSLog(@"取消执行定时器");
[_timer invalidate];
}
}
上面info:方法的代码很简单,该方法只是在控制台简单的打印一行,接着程序判断如果_count的值大于10,就取消_timer对象。
上面程序中还用到了_timer、_count两个成员变量,因此还需要在FKAppDelegate.m中额外定义这两个成员变量,其代码如下:
NSInteger _count;
NSTimer* _timer;
完成上面例子之后,运行该程序,当程序加载完成后,就会启动一个定时器,该定时器就会控制程序的info:方法执行11次。运行该程序,可以看到如图7.3所示的输出。