Как установить секунды на ноль для NSDate

Я пытаюсь получить NSDate от UIDatePicker, но он постоянно возвращает мне дату и трейлинг 20 секунд. Как я могу установить вручную NSDateвторой с нуля?

6 ответов

Решение

NSDate является неизменным, поэтому вы не можете изменить его время. Но вы можете создать новый объект даты, привязанный к ближайшей минуте:

NSTimeInterval time = floor([date timeIntervalSinceReferenceDate] / 60.0) * 60.0;
NSDate *minute = [NSDate dateWithTimeIntervalSinceReferenceDate:time];

Изменить, чтобы ответить на комментарий Ули

Базовая дата для NSDate 1 января 2001 года, 0:00 по Гринвичу. С тех пор было добавлено две дополнительные секунды: 2005 и 2010, поэтому значение, возвращаемое [NSDate timeIntervalSinceReferenceDate] должен быть выключен на две секунды.

Это не вариант: timeIntervalSinceReferenceDate точно синхронно с настенным временем.

Отвечая на вопрос, я не убедился, что это действительно так. Я просто предположил, что Mac OS будет вести себя так же, как время UNIX (эпоха 1970 года): POSIX гарантирует, что каждый день начинается с кратности 86 400 секунд.

Глядя на значения, возвращенные из NSDate это предположение кажется правильным, но было бы неплохо найти определенное (задокументированное) утверждение об этом.

Вы не можете напрямую манипулировать NSTimeInterval, так как это расстояние в секундах с исходной даты, которое не гарантируется как 00-секундное время при делении на 60. В конце концов, могут быть добавлены дополнительные секунды для настройки для различия между солнечным временем и UTC. Каждая секунда прыжка отбрасывает вас на 1. Что я делаю, чтобы установить секунды моей даты на 0:

NSDate              *   startDateTime = [NSDate date];
NSDateComponents    *   startSeconds = [[NSCalendar currentCalendar] components: NSSecondCalendarUnit fromDate: startDateTime];

startDateTime = [NSDate dateWithTimeIntervalSinceReferenceDate: [startDateTime timeIntervalSinceReferenceDate] -[startSeconds second]];

Это заботится о високосных секундах. Я предполагаю, что еще более чистый способ будет использовать -dateByAddingComponents:

NSDate              *   startDateTime = [NSDate date];
NSDateComponents    *   startSeconds = [[NSCalendar currentCalendar] components: NSSecondCalendarUnit fromDate: startDateTime];
[startSeconds setSecond: -[startSeconds second]];

startDateTime = [[NSCalendar currentCalendar] dateByAddingComponents: startSeconds toDate: startDateTime options: 0];

Таким образом, вы гарантированно учитываете любые особые вещи, которые -dateByAddingComponents: заботятся.

Вот расширение Swift для тех, кто заинтересован:

extension Date {
    public mutating func floorSeconds() {
        let calendar = Calendar.current
        let components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: self)
        self = calendar.date(from: components) ?? self // you can handle nil however you choose, probably safe to force unwrap in most cases anyway
    }
}

Пример использования:

let date = Date()
date.floorSeconds()

С помощью DateComponents гораздо надежнее, чем добавление временного интервала к дате.

Хотя это старый вопрос, и Ули дал "правильный" ответ, самое простое решение IMHO - просто вычесть секунды из даты, полученной из календаря. Имейте в виду, что это может оставить миллисекунды на месте.

NSDate *date = [NSDate date];
NSDateComponents *comp = [[NSCalendar currentCalendar] components:NSCalendarUnitSecond
                                                         fromDate:date];
date = [date dateByAddingTimeInterval:-comp.second];

NSDate записывает один момент времени. Если вы хотите сохранить определенный день, не используйте NSDate, Вы получите много неожиданных головных болей, связанных с часовыми поясами, летнее время и т. Д.

Одним из альтернативных решений является сохранение дня в виде целого числа в стандартном формате квази-ISO, например 20110915 от 15 сентября 2011 года. Это гарантированно сортируется так же, как NSDate бы отсортировать

Вот расширение, чтобы сделать это в Swift:

extension NSDate {
    func truncateSeconds() -> NSDate {
        let roundedTime = floor(self.timeIntervalSinceReferenceDate / 60) * 60
        return NSDate(timeIntervalSinceReferenceDate: roundedTime)
    }
}

Также Swift ...

extension Date {

  func floored() -> Date {
    let flooredSeconds = DateComponents(second: 0, nanosecond: 0)
    return Calendar.current.nextDate(after: self, 
                                     matching: flooredSeconds,
                                     matchingPolicy: .strict, 
                                     direction: Calendar.SearchDirection.backward)!
  }

}
Другие вопросы по тегам