Objective-C怎么使用日期、时间与定时器学习系列之四

Objective-C如何使用日期、时间与定时器学习系列之四

本章介绍如何通过 Foundation 框架使用 Objective-C 处理日期与定时器。本章内容:

  • ●  使用 NSDate 创建今天的日期

  • ●  使用 NSDateComponents 创建自定义日期

  • ●  比较日期

  • ●  将字符串转换为日期

  • ●  格式化日期以在用户界面上显示

  • ●  加减日期

  • ●  使用定时器调度重复与非重复的代码

    5.1 创建表示今天的日期对象

    问题

      应用中需要表示今天的日期。
    

    解决方案

    使用 NSDate 类的 date 方法创建表示当前日期的日期对象。说明

    NSDate 类通常与其他类搭配使用(接下来的攻略将会介绍这一点)NSDate 本身能获取今天的日期,可以将日期打印到控制台或是呈现给用户。要想获得今天的日期,请使用 date 方法并将结果赋给 NSDate 对象:

    NSDate *todaysDate = [NSDate date];

    参见程序清单 5-1

    代码

    程序清单5-1 main.m
    #import <Foundation/Foundation.h>

    int main (int argc, const char * argv[]){

          @autoreleasepool {
               NSDate *todaysDate = [NSDate date];
               NSLog(@"Today's date is %@", todaysDate);
    

    }
    return 0;

    }

    使用

    要想使用上述代码,请从 Xcode 构建并运行 Mac 应用。通过日志查看打印的今天的日期:Today's date is 2012-06-27 13:14:30 +0000

    5.2 通过 Component 创建自定义日期

    问题

      除了当前日期外,应用还需要引用其他日期。
    

    解决方案

    使用 NSDateComponents 定义具体日期,然后使用 NSCalendar 和日期组件返回 NSDate对象引用,在应用中可以使用 NSDate 对象引用。

    说明

    要想创建自定义日期,需要使用 3 Foundation 类:NSDateNSDateComponents NSCalendar。这里,NSDate 作为表示日期的最基本的类。

    NSDateComponents 类代表构成日期与时间的细节信息:天、月、年与小时。NSDate-Components 提供了很多关于日期与时间的细节信息,可以设置这些信息来自定义日期。NSCalendar 类表示真实世界的日历,用于管理与日历相关的复杂操作。可以指定使用的日历或是获取用户系统中采用的日历。通常情况下,可以假定使用的是 Gregorian 日历,但也可以指定其他日历,比如 Hebrew Islamic 日历。

    要想创建自定义日期,首先需要新建 NSDateComponents 实例:

    NSDateComponents *dateComponents = [[NSDateComponents alloc] init];

    接下来需要设置自定义日期中需要的所有属性。本攻略会设置用于表示美国加利福尼亚州 iPhone 发布起始日期的所有组件:

    dateComponents.year = 2007;
    dateComponents.month = 6;
    dateComponents.day = 29;
    dateComponents.hour = 12;
    dateComponents.minute = 01;
    dateComponents.second = 31;
    dateComponents.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"PDT"];

    你只需要使用点符号设置感兴趣的日期属性。上面的最后那个属性需要特殊的NSTimeZone 对象,可以指定所需的任何时区,也可以不设置该属性以使用系统时区。

    最后,要想创建 NSDate 对象,需要有指向日历的引用(通常是当前的系统日历),可通过 currentCalendar 消息[NSCalendar currentCalendar]获得日历引用。接下来,使用 date-WithComponents:函数获得与设置相对应的日期对象:

    SDate *iPhoneReleaseDate = [[NSCalendar currentCalendar]dateFromComponents:dateComponents];

    参见程序清单 5-2代码

    程序清单5-2 main.m
    #import <Foundation/Foundation.h>

    int main (int argc, const char * argv[]){

    @autoreleasepool {
    NSDateComponents *dateComponents = [[NSDateComponents alloc] init];dateComponents.year = 2007;
    dateComponents.month = 6;
    dateComponents.day = 29;
    dateComponents.hour = 12;
    dateComponents.minute = 01;
    dateComponents.second = 31;
    dateComponents.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"PDT"];

    NSDate *iPhoneReleaseDate = [[NSCalendar currentCalendar]dateFromComponents:dateComponents]; 

    NSLog(@"The original iPhone went on sale: %@", iPhoneReleaseDate);

    }

    return 0;}

    使用

    要想使用上述代码,请从 Xcode 构建并运行 Mac 应用。可以通过日志看到打印出的iPhone 发布日期:

    The original iPhone went on sale: 2007-06-29 19:01:31 +0000

    5.3 比较两个日期问题

      应用中至少有两个日期,你想知道它们之间的关系。例如,某个日期是否在另一个日
    期之前?两个日期之间相差多少天?
    

    解决方案

    对于简单的比较来说,可以使用内置的 NSDate 比较函数。要想知道从某个日期开始经过了多少天,需要有指向系统日历的引用和两个日期。

    说明

    本攻略假定你使用 5.2 节中设置的 iPhone 发布日期,并将之与今天的日期进行比较。可以通过 NSDate date 函数来获得今天的日期。

    首先比较 iPhone 发布日期是否是今天,请使用 isEqualToDate:函数并将需要比较的日期作为参数传递进来,该函数返回一个布尔值:

        NSDate *todaysDate = [NSDate date];
    

    if([todaysDate isEqualToDate:iPhoneReleaseDate])NSLog(@"The iPhone was released today!");

    else
    NSLog(@"The iPhone was released on some other date");

    要想判断某个日期是否早于另一个日期,请使用 earlierDate:函数并将另一个日期作为参数传递进来,该函数返回更早的那个日期:

    NSDate *earlierDateIs = [todaysDate earlierDate:iPhoneReleaseDate];

      还可以进行相反比较,判断哪个日期更晚:
    

    NSDate *laterDateIs = [todaysDate laterDate:iPhoneReleaseDate]; 

    要想获得两个日期之间相差的秒数,请使用 timeIntervalSinceDate:函数并将第二个日期作为参数传递进来。你会得到一个双精度浮点数,其值为两个日期间相差的秒数。这是个名为 NSTimeInterval(你会发现其他的日期方法使用了 NSTimeInterval)typedef 声明。

    可以通过系统日历与 NSDateComponents 类获得关于日期比较的更详细信息,这会以你需要的格式返回两个日期间相差的时间。因此,如果想要知道天数、小时数、分钟数、年数、月数或是其中的组合,那么可以从中受益。

      首先需要获得指向用户系统日历的引用:
    

    NSCalendar *systemCalendar = [NSCalendar currentCalendar];

    接下来,通过对 NSCalendar 常量进行按位或运算来指定采用的时间单位:unsigned int unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |

           NSDayCalendarUnit;
    

    注意:

    位操作是处理底层二进制信息的一种方式。如你所知,计算机以一系列 1 0 来表示信息(比如 00000011 表示数字 3)。位操作会比较两部分信息的二进制表示并根据比较得到结果。按位或表示如果两个信息中有一个为 1,那么结果就为 1。 

    换句话说,我想知道两个日期间相差的年数、月数与天数。表 5-1 列出了这里可以使用的常量。 

    Objective-C怎么使用日期、时间与定时器学习系列之四

    可以通过 NSCalendar components:fromDate:toDate:options:函数来返回 NSDateComponents

    对象,对象中的数据描述了根据指定的 NSCalendar 常量,计算得出的两个日期间的时间差:

    NSDateComponents *dateComparisonComponents = [systemCalendarcomponents:unitFlags

                   fromDate:iPhoneReleaseDate
                     toDate:todaysDate
    
          options:NSWrapCalendarComponents];
    

    可以访问相应的属性以获得所需的信息。例如,可以通过 dateComparisonComponents.year

    属性获得年数。参见程序清单 5-3代码

    程序清单5-3 main.m
    #import <Foundation/Foundation.h>

    int main (int argc, const char * argv[]){

    @autoreleasepool {
    NSDateComponents *dateComponents = [[NSDateComponents alloc] init];dateComponents.year = 2007;
    dateComponents.month = 6;
    dateComponents.day = 29;
    dateComponents.hour = 12;
    dateComponents.minute = 01;
    dateComponents.second = 31;
    dateComponents.timeZone = [NSTimeZone

                  timeZoneWithAbbreviation:@"PDT"];
    

    NSDate *iPhoneReleaseDate = [[NSCalendar currentCalendar]dateFromComponents:dateComponents];

    NSLog(@"The original iPhone went on sale: %@", iPhoneReleaseDate);NSDate *todaysDate = [NSDate date];
    NSLog(@"Today's date is: %@", todaysDate);

    if([todaysDate isEqualToDate:iPhoneReleaseDate])NSLog(@"The iPhone was released today!");

    else
    NSLog(@"The iPhone was released on some other date");

    NSDate *earlierDateIs = [todaysDate earlierDate:iPhoneReleaseDate];NSLog(@"The earlier date is: %@", earlierDateIs);
    NSDate *laterDateIs = [todaysDate laterDate:iPhoneReleaseDate];

    NSLog(@"The later date is: %@", laterDateIs);NSTimeInterval timeBetweenDates = [todaysDatetimeIntervalSinceDate:iPhoneReleaseDate];

    NSLog(@"The iPhone was released %f seconds ago", timeBetweenDates);NSCalendar *systemCalendar = [NSCalendar currentCalendar];unsigned int unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit|

    NSDayCalendarUnit;
    NSDateComponents *dateComparisonComponents =[systemCalendar components:unitFlags

                                 fromDate:iPhoneReleaseDate
                                   toDate:todaysDate
    
                                  options:NSWrapCalendarComponents];
    

    NSLog(@"The iPhone was released %ld years, %ld months and %ld days ago",dateComparisonComponents.year,dateComparisonComponents.month,
    dateComparisonComponents.day

    );

    return 0;}

    使用

    要想使用上述代码,请从 Xcode 构建并运行 Mac 应用。通过日志消息查看日期与它们之间的比较结果:

    The original iPhone went on sale: 2007-06-29 19:01:31 +0000Today's date is: 2012-06-27 20:54:56 +0000
    The earlier date is: 2007-06-29 19:01:31 +0000
    The later date is: 2012-06-27 20:54:56 +0000

    The iPhone was released on some other date
    The iPhone was released 143776405.074785 seconds ago
    The iPhone was released 4 years, 6 months and 20 days ago

      注意,你的输出消息与这里显示的不同,因为你运行上述代码的时间和我的不同。
    

    5.4 将字符串转换为日期

    问题

    你通过字符串文件获得了包含日期信息的字符串,想要以日期对象的形式使用日期信息。

    解决方案

    使用 NSDateFormatter 指定字符串格式并创建新的日期对象。说明

      本攻略假设你有以字符串形式存储的日期信息:
    
        NSString *dateString = @"02/14/2012";
    

    你首先需要有日期格式化器,可以使用 NSDateFormatter 类进行创建:NSDateFormatter *df = [[NSDateFormatter alloc] init];接下来,使用字符串格式设置 dateFormat 属性: 

    df.dateFormat = @"MM/dd/yyyy"; 

    要想创建日期对象,请使用 dateFromString:日期格式化函数:NSDate *valentinesDay = [df dateFromString:dateString];参见程序清单 5-4

    代码
    程序清单5-4 main.m

        import <Foundation/Foundation.h>
    

    int main (int argc, const char * argv[]){

    @autoreleasepool {
    NSString *dateString = @"02/14/2012";
    NSDateFormatter *df = [[NSDateFormatter alloc] init];df.dateFormat = @"MM/dd/yyyy";
    NSDate *valentinesDay = [df dateFromString:dateString];NSLog(@"Valentine's Day = %@", valentinesDay);

    }

    return 0;}

    使用

    要想使用上述代码,请从 Xcode 构建并运行 Mac 应用。通过日志消息查看从字符串创建的日期对象:

    Valentine's Day = 2012-02-14 05:02:00 +0000

      根据本地时区的不同,你的输出结果可能与我的也会不同。
    

    5.5 格式化日期以便显示

    问题

      你希望能以用户可以识别并且在用户界面上呈现良好的方式显示日期对象。
    

    注意:

    日期格式化器使用了 Unicode 日期格式。参见 http://unicode.org/reports/tr35/-tr35-10.html#Date_Format_Patterns 以了解可用日期格式的完整列表。 

    解决方案

    使用 NSDateFormatter 创建日期格式化器并将日期对象格式化为字符串以显示给用户。说明

    使用 5.4 节中介绍的 Unicode 日期格式指定日期格式化器。因此,如果已经有了 5.4节中的日期,但却想要以不同的格式展现给用户,那么可以像下面这样设置日期格式化器:

        df.dateFormat = @"EEEE, MMMM d";
    

    这会显示出日期所处的工作日名称、月份名称与天数。请使用 NSDateFormatter stringFromDate:函数查看结果:

    SLog(@"Another Formatted Valentine's Day = %@", [dfstringFromDate:valentinesDay]);

      这会以下面的形式显示出日期:
    
        Tuesday, February 14
    

    参见程序清单 5-5代码

    程序清单5-5 main.m
    #import <Foundation/Foundation.h>

    int main (int argc, const char * argv[]){

    @autoreleasepool {
    NSString *dateString = @"02/14/2012";
    NSDateFormatter *df = [[NSDateFormatter alloc] init];df.dateFormat = @"MM/dd/yyyy";
    NSDate *valentinesDay = [df dateFromString:dateString];NSLog(@"Unformatted Valentine's Day = %@", valentinesDay);NSLog(@"Formatted Valentine's Day = %@", [df

                  stringFromDate:valentinesDay]);
               df.dateFormat = @"EEEE, MMMM d";
               NSLog(@"Another Formatted Valentine's Day = %@",
    
                   [df stringFromDate:valentinesDay]);
    

    }

    return 0;} 

    使用

    要想使用上述代码,请从 Xcode 构建并运行 Mac 应用。通过日志消息查看格式化之后的日期对象:

    Unformatted Valentine's Day = 2012-02-14 05:00:00 +0000Formatted Valentine's Day = 02/14/2012
    Another Formatted Valentine's Day = Tuesday, February 14

    5.6 加减日期

    问题

      你想要加减应用中的日期。
    

    解决方案

    使用 NSDateComponentsNSCalendar 类以及日期对象来加减日期。NSDateComponents指定了时间长度(一天、一周或是其他时间间隔)。而借助 NSCalendar 提供的方法,我们可以通过用户的日历与创建日期组件对象时指定的信息来新建日期。

    说明

    我们继续使用 5.4 节中创建的情人节日期。对于该例来说,你想要获得情人节前一周的日期(或许作为购物时间)

    你首先需要有 NSDateComponents 对象。alloc init 构造函数用于创建这种对象:

    NSDateComponents *weekBeforeDateComponents = [[NSDateComponents alloc] init];

    要想使用时间间隔,可以设置所需的任何属性。对于该例来说,你只想减掉一周,因此将 NSDateComponents 对象的 week 属性设为-1:

        weekBeforeDateComponents.week = -1;
    

    现在可以通过用户的日历与 dateByAddingComponents:toDate:options:函数获得前一周的日期:

    NSDate *vDayShoppingDay = [[NSCalendar currentCalendar]dateByAddingComponents:weekBeforeDateComponents

                          toDate:valentinesDay
                         options:0];
    

    该函数返回代表前一周的新日期。此外,注意为了减掉日期,你在使用该函数时用到了负整数(并不存在 dateBySubtractingComponents 函数),参见程序清单 5-6。 

    代码
    程序清单5-6 main.m

        #import <Foundation/Foundation.h>
    

    int main (int argc, const char * argv[]){

    @autoreleasepool {
    NSString *dateString = @"02/14/2012";
    NSDateFormatter *df = [[NSDateFormatter alloc] init];df.dateFormat = @"MM/dd/yyyy";
    NSDate *valentinesDay = [df dateFromString:dateString];NSLog(@"Valentine's Day = %@", valentinesDay);
    NSDateComponents *weekBeforeDateComponents = [[NSDateComponents

    alloc] init];
    weekBeforeDateComponents.week = -1;
    NSDate *vDayShoppingDay = [[NSCalendar currentCalendar]dateByAddingComponents:weekBeforeDateComponents

                              toDate:valentinesDay
                              options:0];
    

    NSLog(@"Shop for Valentine's Day by %@", vDayShoppingDay);}

    return 0;}

    使用

    要想使用上述代码,请从 Xcode 构建并运行 Mac 应用。通过控制台查看日期相减之后的结果:

    Valentine's Day = 2012-02-14 05:00:00 +0000
    Shop for Valentine's Day by 2012-02-07 05:00:00 +0000

    5.7 使用定时器调度并重复执行任务

    问题

      应用需要调度代码以在特定的时间执行。此外,你还想要重复执行任务。
    

    解决方案

    使用 NSTimer 调度代码以在特定的时间执行。为了使用 NSTimer,你需要有日期对象与指向应用的运行循环的引用。 

    注意:

    NSTimer 需要有运行循环,如果想在 Mac iOS 应用中使用定时器,就必须有运行循环。本攻略需要应用带有运行循环。1.11 1.12 节分别介绍了创建 Mac iOS 应用的步骤。 


    说明

    本攻略的代码位于应用委托中。通常情况下,定时器会放在自定义类或是应用控制器中。

      定时器会从特定的日期与时间开始向对象发送消息。如果应用需要重复,那么定时器
    可能会间隔一段时间后再发送消息。你首先需要有日期对象,用来表示定时器开始向对象
    发送消息的日期与时间:
    

    NSDate *scheduledTime = [NSDate dateWithTimeIntervalSinceNow:10.0];

    上述调度时间位于上面这一行代码执行后的 10 秒钟。可以在这里使用任何日期。接下来,通过 initWithFireDate:interval:target:selector:userInfo:repeats:构造函数创建定时器:

    SString *customUserObject = @"To demo userInfo";

    NSTimer *timer = [[NSTimer alloc] initWithFireDate:scheduledTimeinterval:2

                                             target:self
                                           selector:@selector(task)
                                           userInfo:customUserObject
    
                                            repeats:YES];
    

    这里有些内容需要说明一下。第 1 个参数是日期对象,指定了定时器何时变成活动状态。接下来是间隔时间,是定时器再次发送消息前所需等待的秒数。之后是目标参数描述符,目标是方法所处的对象。selector 参数是位于圆括号中的方法名,前面是@selector 关键字。由于方法与定时器一样都位于应用委托中,因此可以使用 self 关键字。

    userInfo 是定时器使用的自定义内容。可以使用任何对象,并且可以获得正在执行的消息中的对象引用(上面的 selector 参数)。这里使用了字符串,但通常会使用字典或其他集合以支持更加复杂的活动。

    repeats 参数表示定时器是发送一次消息,还是根据第 2 个参数指定的时间间隔重复发送。

    接下来需要指向运行循环的引用。可以通过 NSRunLoop currentRunLoop 函数获得该引用:

    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

      现在,只需要将定时器添加到运行循环中即可:
    

    [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];

    10 秒钟后,定时器将会开始每隔两秒钟向应用发送 task 消息。启动之后,如果想停止定时器,可以向定时器发送 invalidate 消息。这会从运行循环中 

    删除定时器,代码如下所示:
    
        [timer invalidate];
    

    参见程序清单 5-7代码

    程序清单5-7 main.m#import "AppDelegate.h"

        @implementation AppDelegate
        @synthesize window = _window;
    

    - (void)applicationDidFinishLaunching:(NSNotification *)aNotification{NSDate *scheduledTime = [NSDate dateWithTimeIntervalSinceNow:10.0];NSString *customUserObject = @"To demo userInfo";
    NSTimer *timer = [[NSTimer alloc] initWithFireDate:scheduledTime

                                              interval:2
                                                target:self
    
                                              selector:@selector(task)
                                              userInfo:customUserObject
    

    repeats:YES];NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

    [runLoop addTimer:timer forMode:NSDefaultRunLoopMode];}

    -(void)task:(id)sender{
    NSTimer *localTimer = (NSTimer *)sender;
    NSLog(@"Schedule task has executed with this user info: %@", [localTimer

    }@end

    使用

    要想使用上述代码,请从 Xcode 构建并运行 Mac 应用。注意控制台窗口以及消息何时开始打印到日志中。此处打印出了时间戳,这样就可以看到时间间隔的运作方式了:

    2012-01-19 15:23:28.651 Timer[31067:707] Schedule task has executed withthis user info: To demo userInfo
    2012-01-19 15:23:30.651 Timer[31067:707] Schedule task has executed withthis user info: To demo userInfo

    2012-01-19 15:23:32.651 Timer[31067:707] Schedule task has executed withthis user info: To demo userInfo