Усечение NSDate (время обнуления)

Я хочу создать новый NSDate с 0 часами, 0 минутами и 0 секундами для времени. Исходная дата может быть любой случайной NSDate.

Есть ли способ добиться этого? Документация мне в этом не помогла.


Пример

Есть: 2010-10-30 10:14:13 GMT

Хочу: 2010-10-30 00:00:00 GMT


person Benjamin    schedule 15.11.2010    source источник
comment
См. мой ответ, который предлагает до четырех способов решения вашей проблемы с Swift 3.   -  person Imanou Petit    schedule 31.03.2017


Ответы (6)


unsigned int flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:flags fromDate:date];
NSDate* dateOnly = [calendar dateFromComponents:components];

date — это дата, с которой вы хотите удалить время.

Это разделяет дату и время и создает новую дату со временем по умолчанию (00:00:00).

ИЗМЕНИТЬ

Чтобы учесть часовой пояс:

NSDate* dateOnly = [[calendar dateFromComponents:components] dateByAddingTimeInterval:[[NSTimeZone localTimeZone]secondsFromGMT]];
person Evan Mulawski    schedule 15.11.2010
comment
он сохранил свое часовое значение: o я получаю 19:00:00 с 19:27:23 - person Benjamin; 15.11.2010
comment
Отличный ответ. Это действительно глупо, что такие часто необходимые вещи не включены в Foundation. - person Jonathan Sterling; 15.11.2010
comment
Это работает хорошо, но причина, по которой я предпочитаю использовать rangeOfUnit:startDate:interval:forDate:, заключается в том, что проще обобщить, чтобы найти другие границы даты, такие как год, минута, час и т. д. NSDateComponents требует, чтобы вы разработали правильные флаги для каждого случая. . - person Rob Napier; 15.11.2010
comment
почему 22:00:00 час по умолчанию? это зависит от часового пояса? - person Benjamin; 15.11.2010
comment
Да, тут дело в часовом поясе. Если вы GMT, он вернет 00:00:00. Смотрите мою правку. - person Evan Mulawski; 15.11.2010
comment
мой реальный часовой пояс — CET (центральноевропейское время) DebugLog(@Today: %@, [дата NSDate]); сказал GMT (написал в консоли) - person Benjamin; 15.11.2010
comment
Я проверил случайные даты и время. Все результаты имеют исходную дату и время 00:00:00. - person Evan Mulawski; 15.11.2010
comment
да, хорошо, большое спасибо, теперь я могу сделать это самостоятельно, это было очень полезно, я благодарен. - person Benjamin; 15.11.2010
comment
Я получаю 01:00:00. Я предполагаю, что это та же проблема, что и у Бенджамина - также необходимо настроить летнее время. - person Jonathan Moffatt; 22.10.2011
comment
Ни один из ответов на усечение компонентов даты/времени не работал у меня без установки [calendarInstance setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; перед извлечением компонентов. Я ответил здесь stackoverflow.com/a/31278150/3056278 на аналогичный вопрос, почему это так. - person Vikram Rao; 07.07.2015

Используйте rangeOfUnit:startDate:interval:forDate: NSCalendar. Этот код выберет границу дня на основе текущего часового пояса. Если вам нужен определенный часовой пояс, вам нужно создать NSCalendar и соответствующим образом установить его часовой пояс.

- (NSDate*)boundaryForCalendarUnit:(NSCalendarUnit)calendarUnit
{
    NSDate *boundary;
    [[NSCalendar currentCalendar] rangeOfUnit:calendarUnit startDate:&boundary interval:NULL forDate:self];
    return boundary;
}

- (NSDate*)dayBoundary
{
    return [self boundaryForCalendarUnit:NSDayCalendarUnit];
}
person Rob Napier    schedule 15.11.2010
comment
+1 Мне это нравится больше, чем принятый ответ. Он не сталкивается с проблемами перехода на летнее время и не требует хака для учета часового пояса. - person Jonathan Moffatt; 22.10.2011
comment
+1 @rob отличное решение. Принятый ответ Эвана замедлил работу моего приложения (около 1642,0 мс процессорного времени в 'Instruments' 'Time Profiler' для каждого вызова метода), тогда как решение Роба ускорилось примерно до 100,0 мс. - person anneblue; 20.06.2013
comment
Интересный ответ, но я не думаю, что он полностью решает вопрос. Вы можете получить правильный вывод, установив часовой пояс на gmt. Один из способов сделать это: NSCalendar *calendar = [NSCalendar currentCalendar]; [календарь setTimeZone: [NSTimeZone timeZoneForSecondsFromGMT: 0]; - person smileBot; 05.11.2013
comment
Если вы используете это и передадите [NSDate date] в качестве аргумента, вы получите дату в часовом поясе по Гринвичу, а НЕ в вашем местном часовом поясе. Если вам нужна только дата в местном/системном часовом поясе, используйте принятый ответ. Также обратите внимание, что отладчик покажет что-то вроде этого: __NSTaggedDate * 2015-09-10 00:00:00 UTC, что немного сбивает с толку, поскольку показывает UTC, но поскольку время равно нулю, UTC не имеет значения. - person Mark Edington; 11.09.2015
comment
@MarkEdington UTC = GMT, и да, это актуально :) NSDate не имеет пользовательского часового пояса, поскольку он всегда сохраняется в формате UTC / GMT. Хотя принятый ответ и этот ответ поддерживают часовой пояс пользователей из коробки, этот ответ намного чище. - person marsbear; 04.04.2016

В Swift 3 вы можете выбрать один из четырех следующих шаблонов для решения своей проблемы.


№1. Использование Calendar startOfDay(for:)

startOfDay(for:) имеет следующее объявление:

func startOfDay(for date: Date) -> Date

Возвращает первый момент заданного Date как Date.

В приведенном ниже коде Playground показано, как использовать этот метод:

import Foundation

let date = Date()

// Get new date
let calendar = Calendar.current
let newDate = calendar.startOfDay(for: date)

// Format dates
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK")
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .long

let formattedDate = dateFormatter.string(from: date)
let formattedNewDate = dateFormatter.string(from: newDate)

// Print formatted dates
print(formattedDate) // Prints: 30/03/2017, 15:14:41 CEST
print(formattedNewDate) // Prints: 30/03/2017, 00:00:00 CEST

№ 2. Использование Calendar date(bySettingHour:minute:second:of:matchingPolicy:repeatedTimePolicy:direction:)

date(bySettingHour:minute:second:of:matchingPolicy:repeatedTimePolicy:direction:) имеет следующее объявление:

func date(bySettingHour hour: Int, minute: Int, second: Int, of date: Date, matchingPolicy: Calendar.MatchingPolicy = default, repeatedTimePolicy: Calendar.RepeatedTimePolicy = default, direction: Calendar.SearchDirection = default) -> Date?

Возвращает новый Date, представляющий дату, рассчитанную путем установки часов, минут и секунд на заданное время в указанном Date.

В приведенном ниже коде Playground показано, как использовать этот метод:

import Foundation

let date = Date()

// Get new date
let calendar = Calendar.current
let newDate = calendar.date(bySettingHour: 0, minute: 0, second: 0, of: date)

// Format dates
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK")
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .long

let formattedDate = dateFormatter.string(from: date)
let formattedNewDate = dateFormatter.string(from: newDate!)

// Print formatted dates
print(formattedDate) // Prints: 30/03/2017, 15:14:41 CEST
print(formattedNewDate) // Prints: 30/03/2017, 00:00:00 CEST

№3. Использование методов Calendar dateComponents(_:from:) и date(from:)

dateComponents(_:from:) имеет следующее объявление:

func dateComponents(_ components: Set<Calendar.Component>, from date: Date) -> DateComponents

Возвращает все компоненты даты, используя часовой пояс календаря.

date(from:) имеет следующее объявление:

func date(from components: DateComponents) -> Date?

Возвращает дату, созданную из указанных компонентов.

В приведенном ниже коде Playground показано, как использовать эти методы:

import Foundation

let date = Date()

// Get new date
let calendar = Calendar.current
let components = calendar.dateComponents([.day, .month, .year], from: date)
let newDate = calendar.date(from: components)

// Format dates
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK")
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .long

let formattedDate = dateFormatter.string(from: date)
let formattedNewDate = dateFormatter.string(from: newDate!)

// Print formatted dates
print(formattedDate) // Prints: 30/03/2017, 15:14:41 CEST
print(formattedNewDate) // Prints: 30/03/2017, 00:00:00 CEST

№ 4. Использование NSCalendar range(of:start:interval:for:)

range(of:start:interval:for:) имеет следующее объявление:

func range(of unit: NSCalendar.Unit, start datep: AutoreleasingUnsafeMutablePointer<NSDate?>?, interval tip: UnsafeMutablePointer<TimeInterval>?, for date: Date) -> Bool

Возвращает по ссылке время начала и продолжительность данной единицы календаря, содержащей заданную дату.

В приведенном ниже коде Playground показано, как использовать этот метод:

import Foundation

let date = Date()

// Get new date
let calendar = Calendar.current as NSCalendar
var newDate: NSDate?
calendar.range(of: .day, start: &newDate, interval: nil, for: date)

// Format dates
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_UK")
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .long

let formattedDate = dateFormatter.string(from: date)
let formattedNewDate = dateFormatter.string(from: newDate as! Date)

// Print formatted dates
print(formattedDate) // Prints: 30/03/2017, 15:14:41 CEST
print(formattedNewDate) // Prints: 30/03/2017, 00:00:00 CEST
person Imanou Petit    schedule 30.03.2017

Я знаю, что уже поздно, но теперь есть лучшие методы: почему бы вам просто не использовать

Свифт 2

NSCalendar.currentCalendar().dateBySettingHour(0, minute: 0, second: 0, ofDate: yourDateToZeroOutTime, options: [])

Свифт 5

var yourDate = Date() //Or any Date
yourDate  = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: yourDate) ?? yourDate
person smat88dd    schedule 10.11.2016

Свифт 3

extension Date {
    func trimTime() -> Date {
        var boundary = Date()
        var interval: TimeInterval = 0
        _ = Calendar.current.dateInterval(of: .day, start: &boundary, interval: &interval, for: self)

        return Date(timeInterval: TimeInterval(NSTimeZone.system.secondsFromGMT()), since: boundary)
    }
}
person Chris Byatt    schedule 07.10.2016

Я бы использовал метод описания, чтобы получить заданную дату в виде строки, затем изменить строку и создать новую дату с помощью initWithString.

initWithString: возвращает объект NSDate, инициализированный значением даты и времени, заданным заданной строкой в ​​международном формате представления строк.

  • (id)initWithString:(NSString *)description Описание параметров Строка, указывающая значение даты и времени в международном строковом формате представления — ГГГГ-ММ-ДД ЧЧ:ММ:СС ±ЧЧММ, где ±ЧЧММ — смещение часового пояса в часы и минуты по Гринвичу (например, «2001-03-24 10:45:32 +0600»). Необходимо указать все поля строки формата, включая смещение часового пояса, которое должно иметь префикс со знаком плюс или минус. Возвращаемое значение Объект NSDate, инициализированный со значением даты и времени, указанным в aString.
person jakev    schedule 15.11.2010
comment
нм, решение Эвана лучше - person jakev; 15.11.2010
comment
Это плохая идея. Если вы действительно хотите изменить дату по компонентам, для этого есть решение Эвана. Я не хочу тянуть карту производительности по такой маленькой теме, но что ж... обработка строк сравнительно дорогая. Если вы просто хотите получить начало дня (или базовую дату дня) для расчетов, фильтрации и т. д., используйте версию Роба, так как она еще чище. - person marsbear; 04.04.2016