NS Fecha de inicio y finalización del día

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];

}

Cuando llamo: [self endOfDay: [NSDate date]]; Me sale el primero del mes ... ¿Por qué? Utilizo estos dos métodos porque necesito un intervalo que va desde el primer segundo de la primera fecha (beginOfDay: date1) hasta el último segundo de la segunda fecha (endOfDay: Date2) ...

usuario1028028
fuente
2
Si desea establecer las horas en cero UTC, una forma más fácil de obtener el timeIntervalSince ..., truncar las horas y luego convertir de nuevo a NSDate. Y esto funcionará para otra zona horaria si primero ajusta el intervalo de tiempo por los segundos de la zona horaria de GMT, luego ajusta de la manera opuesta después de truncar. (El final del día es obviamente a las 23: 59: 59.999 más tarde, que se puede obtener con una simple suma mientras tenga el intervalo de tiempo).
Hot Licks
Especificar "fin del día" como un tiempo arbitrario antes de su final real (un segundo en este caso) parece una receta para un software con fallas. Si usa 1 ms, los fallos serán menos frecuentes. O puede usar 23 horas para que sea más probable que se detecten los errores. :-)
Edward Brey

Respuestas:

33

Te falta NSDayCalendarUniten

NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
JaanusSiim
fuente
1
Tengo un comportamiento extraño en el que si agrego el componente de día obtengo las 23:00 del día anterior.
Mike M
3
@Mike use timezone: components.timeZone = [NSTimeZone timeZoneWithName: @ "GMT"];
dimaxyu
@dimaxyu tiene razón. En rápido es: components.timeZone = NSTimeZone (nombre: "GMT")
Tom Bevelander
220

Inicio / Fin del día - 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
fuente
1
NSYearCalendarUnit, NSMonthCalendarUnitY NSDayCalendarUnitse han de utilizarse a partir de iOS 8. Uso NSCalendarUnitYear, NSCalendarUnitMonthy NSCalendarUnitDayen su lugar!
T Blank
6
Para comenzar el día, use [[NSCalendar currentCalendar] startOfDayForDate: ...] para iOS8 +
GingerBreadMane
5
¡Esto devuelve startOfDay en la zona horaria actual! Se espera que NSDate esté en UTC, pero este convertidor devuelve la hora corregida para la zona horaria predeterminada.
Tom Bevelander
3
¿Por qué endOfDay es opcional? ¿Hay situaciones en las que startOfDay no sea nil, mientras que endOfDay podría ser nil?
Mansurov Ruslan
1
Entonces, dependiendo de cómo lo uses, te importarán los espacios de un segundo entre los fines y comienzos de los días developer.apple.com/documentation/foundation/nsdate/…
atlex2
29

Swift 5 Respuesta simple y más precisa.

Hora de inicio: 00:00:00

Hora de finalización: 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
fuente
2
¿En qué se diferencia esto de let dateAtEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())?
Andrés Canella
1
Ninguna diferencia .
August Lin
21

En iOS 8+, esto es realmente conveniente; tu puedes hacer:

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

Para obtener el final del día, simplemente use los métodos NSCalendar durante 23 horas, 59 minutos, 59 segundos, dependiendo de cómo defina el final del día.

// 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))

Matemáticas de fecha

Documentación de NSCalendar de Apple iOS . (Consulte la sección: Cálculos calendáricos )

Métodos NSCalendar discutidos por NSHipster .

Bryan Bryce
fuente
17

Mis extensiones 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
fuente
Puede establecer el segundo en -1 y obtener lo mismo sin usar dateByAddingTimeInterval
Ben Sinclair
para evitar problemas con la zona horaria, agregue: components.timeZone = NSTimeZone (nombre: "GMT")
Tom Bevelander
12

Swift 5.1 - XCode 11 con Dateclase en lugar de NSDatey en Calenderlugar 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
fuente
Recibo el tipo de error no declarado Datecuando lo intento en el patio de recreo
John Doe
1
Me está funcionando aquí. ¿Importaste UIKit con import UIKit?
Ben
2
@Ben Fecha y calendario no está en UIKit, está en Fundación, sin embargo, UIKit importa Fundación
Arbitur
4

No tiene que configurar los componentes a cero, simplemente ignórelos:

-(NSDate *)beginningOfDay:(NSDate *)date
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
    return [calendar dateFromComponents:components];
}
Maxim Lavrov
fuente
2
"Recientemente" disponible:, [calendar startOfDayForDate:date]pero no hay -endOfDayForDate:, desafortunadamente ...
Julian F. Weinert
4

Para mí, ninguna de las respuestas aquí y en otros lugares en stackoverflow funcionó. Para empezar hoy hice esto.

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];

Tenga en cuenta esto [gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];y [components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];.

Cuando se crea un calendario, se inicializa con la zona horaria actual y cuando se extrae la fecha de sus componentes, dado que NSDate no tiene zona horaria, la fecha de la zona horaria actual se considera como zona horaria UTC. Por lo tanto, debemos establecer la zona horaria antes de extraer los componentes y más tarde al extraer la fecha de estos componentes.

Vikram Rao
fuente
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
fuente
4

Swift3 Uso * XCode8
de Apple es la eliminación de la NSdel nombre de la clase de manera que NSDatese pueden intercambiar a Date. Es posible que reciba una advertencia del compilador si intenta lanzarlos diciendo que siempre fallarán, pero funcionan bien cuando los ejecuta en el patio de recreo.

Reemplacé mi NSDatemodelo de datos centrales generados con Datey todavía funcionan.

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
fuente
4

En Swift 3 y 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
fuente
2

Una forma más de obtener 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
fuente
2

Falta NSDayCalendarUniten los componentes.

LombaX
fuente
2

C objetivo

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

Para rápido 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
fuente
2

Creo que la forma más sucinta de hacer esto en Swift es la siguiente:

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)
    }
}
armarioCoder
fuente
¡Eso es perfecto! ¡Gracias!
Baran Emre
1

Dado que iOS 8.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+hay una función incorporada en la Fundación, que puede usar de inmediato. No es necesario implementar funciones propias.

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

Entonces puedes usarlo de esta manera:

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

Vale la pena recordar que esto toma en consideración la zona horaria del dispositivo. Se puede establecer .timeZoneen calendarsi usted quiere tener por ejemplo la zona UTC.

Enlace a las páginas de referencia de Apple: https://developer.apple.com/reference/foundation/nscalendar/1417161-startofday .

Vive
fuente
1

Sólo otra manera el uso dateInterval(of:start:interval:for:)deCalendar

A la vuelta startDatecontiene el inicio del día y intervalel número de segundos del día.

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)
vadian
fuente
1

Esto es lo que uso para 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 un encanto para mí. Puede agregar esto a una extensión para las fechas de inicio y finalización Date, sin embargo, tenga en cuenta que agregar una extensión aumenta el tiempo de compilación (a menos que esté en el mismo archivo que la clase), por lo que si solo lo necesita en un lugar o en una clase. .. no use una extensión.

Paul Peelen
fuente
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
fuente
0

Solo como referencia, una forma sencilla de configurar el inicio y el final del día en 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
fuente
-1

Las unidades de calendario deben considerarse intervalos. A partir de iOS 10 Calendartiene algunos buenos métodos para esto

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

Puede usar el mismo método para obtener el inicio / final de cualquier componente del calendario (semana / mes / año, etc.)

David Arve
fuente
Esto es incorrecto. day?.endevalúa a las 12:00 a. m. de mañana, no a las 11:59 p. m. de esta noche.
Casey Wagner