NSDate início e fim do dia

104
    -(NSDate *)beginningOfDay:(NSDate *)date
{
    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];

    [components setHour:0];
    [components setMinute:0];
    [components setSecond:0];

    return [cal dateFromComponents:components];

}

-(NSDate *)endOfDay:(NSDate *)date
{
    NSCalendar *cal = [NSCalendar currentCalendar];
    NSDateComponents *components = [cal components:(  NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];

    [components setHour:23];
    [components setMinute:59];
    [components setSecond:59];

    return [cal dateFromComponents:components];

}

Quando eu ligo: [self endOfDay: [NSDate date]]; Recebo o primeiro dia do mês ... Por que isso? Eu uso esses dois métodos porque preciso de um intervalo que vai do primeiro segundo da primeira data (startOfDay: date1) até o último segundo da segunda data (endOfDay: Date2) ...

user1028028
fonte
2
Se você quiser definir as horas como zero UTC, uma maneira mais fácil de obter o timeIntervalSince ..., truncar as horas e depois converter de volta para NSDate. E isso funcionará para outro fuso horário se você primeiro ajustar o intervalo de tempo pelos segundosFromGMT do fuso horário e, em seguida, ajustar o caminho oposto após o truncamento. (O final do dia é obviamente 23:59: 59,999 mais tarde, o que pode ser obtido com uma simples adição enquanto você tem o intervalo de tempo.)
Hot Licks de
Especificar "final do dia" como um tempo arbitrário antes do seu final real (um segundo neste caso) parece uma receita para software com problemas. Se você usar 1 ms, os glitches serão menos frequentes. Ou você pode usar 23 horas para que qualquer bug seja mais provável de ser detectado. :-)
Edward Brey

Respostas:

33

Você está faltando NSDayCalendarUnitem

NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
JaanusSiim
fonte
1
Eu tenho um comportamento estranho onde se eu adicionar o componente do dia eu obtenho 23:00 do dia anterior.
Mike M
3
@Mike usa fuso horário: components.timeZone = [NSTimeZone timeZoneWithName: @ "GMT"];
dimaxyu
@dimaxyu está certo. Rapidamente é: components.timeZone = NSTimeZone (nome: "GMT")
Tom Bevelander
220

Início do dia / fim do dia - Swift 4

  // Extension

extension Date {
    var startOfDay: Date {
        return Calendar.current.startOfDay(for: self)
    }

    var endOfDay: Date {
        var components = DateComponents()
        components.day = 1
        components.second = -1
        return Calendar.current.date(byAdding: components, to: startOfDay)!
    }

    var startOfMonth: Date {
        let components = Calendar.current.dateComponents([.year, .month], from: startOfDay)
        return Calendar.current.date(from: components)!
    }

    var endOfMonth: Date {
        var components = DateComponents()
        components.month = 1
        components.second = -1
        return Calendar.current.date(byAdding: components, to: startOfMonth)!
    }
}

// End of day = Start of tomorrow minus 1 second
// End of month = Start of next month minus 1 second
Zelko
fonte
1
NSYearCalendarUnit, NSMonthCalendarUnitE NSDayCalendarUnitforam obsoleto a partir do iOS 8. Use NSCalendarUnitYear, NSCalendarUnitMonthe NSCalendarUnitDayem vez disso!
T Blank
6
Para o início do dia, use [[NSCalendar currentCalendar] startOfDayForDate: ...] para iOS8 +
GingerBreadMane
5
Isso retorna o startOfDay no fuso horário atual! Espera-se que NSDate esteja em UTC, mas este conversor retorna a hora corrigida para o fuso horário padrão.
Tom Bevelander,
3
Por que endOfDay é opcional? Existem situações em que startOfDay não é nulo, enquanto endOfDay pode ser nulo?
Mansurov Ruslan
1
Então, dependendo de como você usa isso, você se preocupa com intervalos de um segundo entre o fim e o início dos dias developer.apple.com/documentation/foundation/nsdate/…
atlex2
29

Swift 5 Resposta simples e mais precisa.

Horário de início: 00:00:00

Horário de término: 23: 59: 59,5

let date = Date() // current date or replace with a specific date
let calendar = Calendar.current
let startTime = calendar.startOfDay(for: date)
let endTime = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: date)

Extra

let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: noon)!
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: noon)!
let specificDate = Date("2020-01-01")

extension Date {
    init(_ dateString:String) {
        let dateStringFormatter = DateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
        let date = dateStringFormatter.date(from: dateString)!
        self.init(timeInterval:0, since:date)
    }
}
August Lin
fonte
2
Como isso é diferente de let dateAtEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())?
Andres Canella
1
Sem diferença.
Agosto Lin
21

No iOS 8+, isso é muito conveniente; você pode fazer:

let startOfDay = NSCalendar.currentCalendar().startOfDayForDate(date)

Para obter o final do dia, basta usar os métodos NSCalendar por 23 horas, 59 minutos e 59 segundos, dependendo de como você define o final do dia.

// Swift 2.0
let components = NSDateComponents()
components.hour = 23
components.minute = 59
components.second = 59
let endOfDay = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: startOfDay, options: NSCalendarOptions(rawValue: 0))

Date Math

Documentação do Apple iOS NSCalendar . (Consulte a Seção: Cálculos Calendáricos )

Métodos NSCalendar discutidos por NSHipster .

Bryan Bryce
fonte
17

Minhas extensões Swift para NSDate:

Swift 1.2

extension NSDate {

    func beginningOfDay() -> NSDate {
        var calendar = NSCalendar.currentCalendar()
        var components = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: self)
        return calendar.dateFromComponents(components)!
    }

    func endOfDay() -> NSDate {
        var components = NSDateComponents()
        components.day = 1
        var date = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: self.beginningOfDay(), options: .allZeros)!
        date = date.dateByAddingTimeInterval(-1)!
        return date
    }
}

Swift 2.0

extension NSDate {

    func beginningOfDay() -> NSDate {
        let calendar = NSCalendar.currentCalendar()
        let components = calendar.components([.Year, .Month, .Day], fromDate: self)
        return calendar.dateFromComponents(components)!
    }

    func endOfDay() -> NSDate {
        let components = NSDateComponents()
        components.day = 1
        var date = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: self.beginningOfDay(), options: [])!
        date = date.dateByAddingTimeInterval(-1)
        return date
    }
}
Libor Zapletal
fonte
Você pode apenas definir o segundo como -1 e obter o mesmo sem usar dateByAddingTimeInterval
Ben Sinclair de
para evitar problemas de fuso horário, adicione: components.timeZone = NSTimeZone (nome: "GMT")
Tom Bevelander
12

Swift 5.1 - XCode 11 com Dateclasse em vez de NSDatee em Calendervez deNSCalender

extension Date {

    var startOfDay : Date {
        let calendar = Calendar.current
        let unitFlags = Set<Calendar.Component>([.year, .month, .day])
        let components = calendar.dateComponents(unitFlags, from: self)
        return calendar.date(from: components)!
   }

    var endOfDay : Date {
        var components = DateComponents()
        components.day = 1
        let date = Calendar.current.date(byAdding: components, to: self.startOfDay)
        return (date?.addingTimeInterval(-1))!
    }
}

Uso:

    let myDate = Date()
    let startOfDate = myDate.startOfDay
    let endOfDate = myDate.endOfDay
Ben
fonte
Estou recebendo o tipo de erro não declarado Datequando tento no playground
John Doe
1
Está funcionando para mim aqui. Você importou o UIKit com import UIKit?
Ben
2
@Ben Date and Calendar não está no UIKit, está no Foundation, no entanto, o UIKit importa Foundation
Arbitur
4

Você não precisa configurar os componentes para zero, apenas ignore-os:

-(NSDate *)beginningOfDay:(NSDate *)date
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
    return [calendar dateFromComponents:components];
}
Maxim Lavrov
fonte
2
"Recentemente" disponível:, [calendar startOfDayForDate:date]mas não há -endOfDayForDate:, infelizmente ...
Julian F. Weinert
4

Para mim, nenhuma das respostas aqui e em outros lugares onde o stackoverflow funcionou. Para começar hoje eu fiz isso.

NSCalendar * gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; 
[gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];    
NSDateComponents *components = [gregorian components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:[NSDate date]]; 
[components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; 
NSDate *beginningOfToday = [gregorian dateFromComponents:components];

Observe isso [gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];e [components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];.

Quando um calendário é criado, ele é inicializado com o fuso horário atual e quando a data é extraída de seus componentes, uma vez que NSDate não tem fuso horário, a data do fuso horário atual é considerada como fuso horário UTC. Portanto, precisamos definir o fuso horário antes de extrair os componentes e posteriormente ao extrair a data desses componentes.

Vikram Rao
fonte
4

Swift 3

  class func today() -> NSDate {
        return NSDate()
    }

    class func dayStart() -> NSDate {
          return NSCalendar.current.startOfDay(for: NSDate() as Date) as NSDate
    }

    class func dayEnd() -> NSDate {
        let components = NSDateComponents()
        components.day = 1
        components.second = -1
        return NSCalendar.current.date(byAdding: components as DateComponents, to: self.dayStart() as Date)
    }
Jaseem Abbas
fonte
4

Swift3 usando * XCode8 A
Apple está removendo o NSdo nome da classe para que NSDatepossa ser trocado para Date. Você pode receber um aviso do compilador se tentar lançá-los dizendo que eles sempre falharão, mas eles funcionam bem quando você os executa no playground.

Substituí meu NSDatemodelo de dados gerado no núcleo por Datee eles ainda funcionam.

extension Date {
  func startTime() -> Date {
    return Calendar.current.startOfDay(for: self)
  }

  func endTime() -> Date {
    var components = DateComponents()
    components.day = 1
    components.second = -1
    return Calendar.current.date(byAdding: components, to: startTime())!
  }
}
John Doe
fonte
4

Em Swift 3 e superior

extension Date {
    var startOfDayDate: Date {
        return Calendar.current.startOfDay(for: self)
    }

    var endOfDayDate: Date {
        let nextDayDate = Calendar.current.date(byAdding: .day, value: 1, to: self.startOfDayDate)!
        return nextDayDate.addingTimeInterval(-1)
    }
}

Uso:

var currentDayStart = Date().startOfDayDate
var currentDayEnd = Date().endOfDayDate
Gurjit Singh
fonte
2

Mais uma maneira de obter resultados:

NSDate *date = [NSDate date];

NSDateComponents *components = [[NSDateComponents alloc] init];
components.day = [[NSCalendar currentCalendar] ordinalityOfUnit:(NSCalendarUnitDay) inUnit:(NSCalendarUnitEra) forDate:date];
NSDate *dayBegin = [[NSCalendar currentCalendar] dateFromComponents:components];

components.day += 1;
NSDate *dayEnd = [[NSCalendar currentCalendar] dateFromComponents:components];
k06a
fonte
2

Você está faltando NSDayCalendarUnitnos componentes.

LombaX
fonte
2

Objective-C

NSCalendar * calendar = [NSCalendar currentCalendar];
NSDate * startDate = [calendar startOfDayForDate:[NSDate date]];
NSLog(@"start date is %@", startDate);
Johnny Rockex
fonte
2

Para Swift 4

    var calendar = Calendar.current
    calendar.timeZone = NSTimeZone(abbreviation: "UTC")! as TimeZone
    let dateAtMidnight = calendar.startOfDay(for: Date())

    //For End Date
    var components = DateComponents()
    components.day = 1
    components.second = -1

    let dateAtEnd = calendar.date(byAdding: components, to: dateAtMidnight)

    print("dateAtMidnight :: \(dateAtMidnight)")
    print("dateAtEnd :: \(dateAtEnd!)")
Hardik Thakkar
fonte
2

Acho que a maneira mais sucinta de fazer isso no Swift é a seguinte:

extension Date {
    func startOfDay() -> Date {
        return Calendar.current.startOfDay(for: self)
    }
    func endOfDay() -> Date {
        return Calendar.current.date(bySettingHour: 23, minute: 59, second 59, of: self)
    }
}
closetCoder
fonte
Perfeito! THX!
Baran Emre
1

Uma vez que iOS 8.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+existe uma função incorporada na Fundação, que você pode usar fora da caixa. Não há necessidade de implementar funções próprias.

public func startOfDay(for date: Date) -> Date

Então, você pode usá-lo desta forma:

let midnightDate = Calendar(identifier: .gregorian).startOfDay(for: Date())

Vale lembrar que isso leva em consideração o fuso horário do dispositivo. Você pode definir .timeZonesobre calendarse você quer ter por exemplo zona UTC.

Link para as páginas de referência da Apple: https://developer.apple.com/reference/foundation/nscalendar/1417161-startofday .

Vive
fonte
1

Apenas uma outra maneira utilizando dateInterval(of:start:interval:for:)deCalendar

No retorno, startDatecontém o início do dia e intervalo número de segundos do dia.

func startAndEnd(of date : Date) -> (start : Date, end : Date) {
    var startDate = Date()
    var interval : TimeInterval = 0.0
    Calendar.current.dateInterval(of: .day, start: &startDate, interval: &interval, for: date)
    var endDate = startDate.addingTimeInterval(interval-1)
    return (start : startDate, end : endDate)
}

let (start, end) = startAndEnd(of: Date())
print(start, end)
vadiana
fonte
1

Isso é o que eu uso para o Swift 4.2:

    let calendar = Calendar.current
    let fromDate = calendar.startOfDay(for: Date())
    let endDate = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: Date())

Funciona como um encanto para mim. Você pode adicionar isso a uma extensão para as datas de início e término em Date, no entanto, lembre-se de que adicionar uma extensão aumenta o tempo de compilação (a menos que no mesmo arquivo da classe), portanto, se você só precisar dela em um lugar ou em uma classe. .. não use uma extensão.

Paul Peelen
fonte
0
extension Date {
    func stringFrom(dateFormat: String) -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = dateFormat
        return formatter.string(from: self)
    }

    func firstSecondInDay() -> Date {
        let dayStr = self.stringFrom(dateFormat: "yyyy-MM-dd")
        let firstSecondStr = "\(dayStr) 00:00:00"
        let format = DateFormatter()
        format.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return format.date(from: firstSecondStr)!
    }

    func lastSecondInDay() -> Date {
        let dayStr = self.stringFrom(dateFormat: "yyyy-MM-dd")
        let laseSecondStr = "\(dayStr) 23:59:59"
        let format = DateFormatter()
        format.dateFormat = "yyyy-MM-dd HH:mm:ss"
        return format.date(from: laseSecondStr)!
    }
}
Benjamin Wen
fonte
0

Apenas para referência, uma maneira simples de definir o início e o fim do dia no Swift 4 ,

var comp: DateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: Date())
comp.hour = 0
comp.minute = 0
comp.second = 0
comp.timeZone = TimeZone(abbreviation: "UTC")!


//Set Start of Day
let startDate : Date = Calendar.current.date(from: comp)!
print(“Day of Start : \(startDate)")


//Set End of Day
comp.hour = 23
comp.minute = 59
comp.second = 59

let endDate : Date = Calendar.current.date(from:comp)!
print("Day of End : \(endDate)")
Renish Dadhaniya
fonte
-1

As unidades de calendário devem ser consideradas intervalos. A partir do iOS 10 Calendartem alguns métodos interessantes para isso

let day = Calendar.autoupdatingCurrent.dateInterval(of: .day, for: Date())
day?.end
day?.start

Você pode usar o mesmo método para obter o início / fim de qualquer componente do calendário (semana / mês / ano etc.)

David Arve
fonte
Isso está incorreto. day?.endavalia para 0h de amanhã, não para 23h59 hoje à noite.
Casey Wagner