我应该使用 NSDate 来跟踪到达时间和迟到时间吗?

Should I use NSDate to track arrival time and lateness?

我想做时间计算,不知道用什么class,我应该用NSDate吗?

我希望用户输入他每天应该上班的时间,然后该应用会显示他的平均 late/early 时间(以分钟为单位)。

更好的解释: 当用户第一次启动应用程序时,应用程序会询问他应该在什么时候上班以及什么时候应该离开。该应用程序跟踪用户 arrives/leaves 的时间(使用工作地址和地理围栏)并计算用户上班的平均迟到和平均早到。当用户打开应用程序时,他会看到两个标签,一个用于 "average late",一个用于 "average early"。

我应该使用 NSDate 还是有更好的方法?

如果您只是比较持续时间,NSTimeInterval 是适合您的类型(它实际上是 float 毫秒数,可以解释其对 reader 的意图)。

NSDate 对于需要以实际时钟时间开始或结束的计算是必需的。

编辑 描述更具体的问题:给定用户每天的工作开始时间,计算与到达工作时间的偏差,关键计算涉及NSDate .

它旨在回答以下问题:与实际时钟相比,用户现在是否准时?此计算的关键参数是当前时间 NSDate 和用户计划到达时间 NSDate,可以将其保存为简单标量并通过 NSDateComponents...[=18= 转换为 NSDate ]

// core location triggered an arrival notification and we call this
// assumes a "User" class that knows user state, like the time user is due at work
-(void)userJustArrived:(User *)myUser {
    NSDate *now = [NSDate date];
    NSDate *dueAtWork = [myUser dueAtWork];
    NSTimeInterval lateness = [now timeIntervalSinceDate:dueAtWork];

    // a negative lateness means user is early
    if (lateness > 0)
        NSLog(@"you are %f milliseconds late.  perhaps you should go into standup comedy", lateness);
}

// on the user class, due at work produces an NSDate from what you would
// have stored earlier as the time when a user is expected at work
- (NSDate *)dueAtWork {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
    // these are given as user state, the user's planned start time
    components.hour = self.dueAtWorkHour;
    components.minute = self.dueAtWorkMinute;
    return [components date];
}

通常,如果您想跟踪 "human" 时间(即您可以在日历或时钟上找到的日期和时间),您需要的工具是 NSDateComponents,再加上 NSCalendarNSDate 的目标是及时跟踪绝对时刻,(*) 自某个参考时刻以来的秒数,这通常不是人们对时间的看法。 NSDate 一旦处理时区、夏令时或任何其他扰乱 "seconds since the epoch" 和 "the clock on the wall."[=17 的良好映射的事情,NSDate 很快就会变得不正确并且常常适得其反=]

(*) 抛开相对论等等,假设我们都能就事情发生的时间达成一致,并且时间间隔可以在一个美好、简单的牛顿宇宙中以交换和传递的方式组合。还要忽略诸如闰秒或任何其他可能使事情变得复杂的问题,并且您有 NSDate.


这是一个实际的实现,应该可以处理大多数怪事(比如午夜时间)。可能无法正确处理 DST 转换,但它应该能让您走上正确的轨道。

let twelveHours: NSTimeInterval = 12 * 60 * 60

func timeLateBetweenDate(actualDate: NSDate, nominalHour: Int, nominalMinute: Int) -> NSTimeInterval {

    func timeLateOnDate(onDate: NSDate) -> NSTimeInterval {
        let calendar =  NSCalendar.currentCalendar()
        let expectedDate = calendar.dateBySettingHour(nominalHour, minute: nominalMinute, second: 0, ofDate: onDate, options: [])!

        let todayDifference = actualDate.timeIntervalSinceDate(expectedDate)

        if todayDifference > twelveHours { // Over 12 hours late; should have checked against tomorrow
            return timeLateOnDate(calendar.dateByAddingUnit(.Day, value: 1, toDate: onDate, options: [])!)
        } else if todayDifference < -twelveHours { // Over 12 hours early, should have checked against yesterday
            return timeLateOnDate(calendar.dateByAddingUnit(.Day, value: -1, toDate: onDate, options: [])!)
        }
        else {
            return todayDifference
        }
    }

    return timeLateOnDate(actualDate)
}

在有点草率的 ObjC 中(只是音译 Swift;作为一种方法,这可能会更清楚):

NSTimeInterval twelveHours = 12 * 60 * 60;

NSTimeInterval _timeLateOnDate(NSDate *onDate, NSDate *actualDate, NSUInteger nominalHour, NSUInteger nominalMinute) {
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDate *expectedDate = [calendar dateBySettingHour:nominalHour minute: nominalMinute second: 0 ofDate: onDate options: 0];

    NSTimeInterval todayDifference = [actualDate timeIntervalSinceDate:expectedDate];

    if (todayDifference > twelveHours) { // Over 12 hours late; should have checked against tomorrow
        return _timeLateOnDate([calendar dateByAddingUnit:NSCalendarUnitDay value: 1 toDate: onDate options: 0],
                               actualDate, nominalHour, nominalMinute);
    } else if (todayDifference < -twelveHours) { // Over 12 hours early, should have checked against yesterday
        return _timeLateOnDate([calendar dateByAddingUnit:NSCalendarUnitDay value: -1 toDate: onDate options: 0],
                               actualDate, nominalHour, nominalMinute);
    } else {
        return todayDifference;
    }
}


NSTimeInterval timeLateBetweenDateAndNominalHourMinute(NSDate *actualDate, NSUInteger nominalHour, NSUInteger nominalMinute) {
    return _timeLateOnDate(actualDate, actualDate, nominalHour, nominalMinute);
}

您可以使用NSDateFormatter来设置每天的实际办公时间。在 NSTimeInterval 上使用 timeIntervalSinceDate: 方法以秒为单位计算迟到/早的平均值。

您要存储的时间不是日期。 "Februrary 9th 2016 10:20:30.7 UTC" 之类的东西是一个日期。 “早上 8 点 30 分”不是 NSDate 可以表示的日期。

当用户输入时间时,将小时和分钟存储为两个数字。

当用户上班时,获取当前日期,将其转换为具有本地日历的日期组件以获得今天的日期,创建一个包含今天日期和用户输入的小时和分钟的 NSDate,然后你可以比较两个 NSDates。这样,您就可以自动处理夏令时等。您甚至可以处理用户在距您 6 小时的时区度假时输入日期的情况。

请不要索要密码。编写代码是你的工作。你一个人的工作。