NSDate début et fin de journée

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

}

Quand j'appelle: [self endOfDay: [NSDate date]]; Je reçois le premier du mois ... Pourquoi ça? J'utilise ces deux méthodes car j'ai besoin d'un intervalle qui va de la première seconde de la première date (beginOfDay: date1) à la dernière seconde de la deuxième date (endOfDay: Date2) ...

user1028028
la source
2
Si vous souhaitez définir les heures sur zéro heure UTC, un moyen plus simple d'obtenir le timeIntervalSince ..., tronquer les heures, puis reconvertir en NSDate. Et cela fonctionnera pour un autre fuseau horaire si vous ajustez d'abord l'intervalle de temps par secondesFromGMT du fuseau horaire, puis ajustez la manière opposée après la troncature. (La fin de la journée est évidemment 23: 59: 59.999 plus tard, ce qui peut être obtenu avec un simple ajout pendant que vous avez l'intervalle de temps.)
Hot Licks
Spécifier «fin de journée» comme un temps arbitraire avant sa fin réelle (une seconde dans ce cas) semble être une recette pour un logiciel glitch. Si vous utilisez 1 ms, les problèmes seront moins fréquents. Ou vous pouvez utiliser 23 heures pour que les bogues soient plus susceptibles d'être détectés. :-)
Edward Brey

Réponses:

33

Vous manquez NSDayCalendarUnitdans

NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
JaanusSiim
la source
1
J'ai un comportement étrange où si j'ajoute le composant du jour, j'obtiens 23h00 du jour précédent.
Mike M
3
@Mike utilise le fuseau horaire: components.timeZone = [NSTimeZone timeZoneWithName: @ "GMT"];
dimaxyu
@dimaxyu a raison. Dans swift son: components.timeZone = NSTimeZone (nom: "GMT")
Tom Bevelander
220

Début / Fin de journée - 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
la source
1
NSYearCalendarUnit, NSMonthCalendarUnitEt NSDayCalendarUnitont été OBSOLETE iOS 8. Utilisez NSCalendarUnitYear, NSCalendarUnitMonthet à la NSCalendarUnitDayplace!
T Blank
6
Pour le début de la journée, utilisez [[NSCalendar currentCalendar] startOfDayForDate: ...] pour iOS8 +
GingerBreadMane
5
Cela renvoie le startOfDay dans le fuseau horaire actuel! NSDate devrait être en UTC, mais ce convertisseur renvoie une heure corrigée pour le fuseau horaire par défaut.
Tom Bevelander
3
Pourquoi endOfDay est-il facultatif? Existe-t-il des situations où startOfDay n'est pas nul, alors que endOfDay pourrait être nul?
Mansurov Ruslan
1
Donc, selon la façon dont vous l'utilisez, vous faites attention aux écarts d'une seconde entre les fins et les débuts des jours developer.apple.com/documentation/foundation/nsdate/…
atlex2
29

Swift 5 Réponse simple et plus précise.

Heure de début: 00:00:00

Heure de fin: 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)

Supplémentaire

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)
    }
}
Août Lin
la source
2
En quoi est-ce différent de let dateAtEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())?
Andres Canella
1
Aucune différence .
Août Lin
21

Dans iOS 8+, c'est vraiment pratique; tu peux faire:

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

Pour obtenir la fin de la journée, utilisez simplement les méthodes NSCalendar pendant 23 heures, 59 minutes, 59 secondes, selon la façon dont vous définissez la fin de la journée.

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

Documentation Apple iOS NSCalendar . (Voir la section: Calculs calendaires )

Méthodes NSCalendar discutées par NSHipster .

Bryan Bryce
la source
17

Mes extensions Swift pour 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
la source
Vous pouvez simplement définir la seconde à -1 et obtenir la même chose sans utiliser dateByAddingTimeInterval
Ben Sinclair
pour éviter les problèmes de fuseau horaire, ajoutez: components.timeZone = NSTimeZone (nom: "GMT")
Tom Bevelander
12

Swift 5.1 - XCode 11 avec Dateclasse au lieu de NSDateet Calenderau lieu 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))!
    }
}

Usage:

    let myDate = Date()
    let startOfDate = myDate.startOfDay
    let endOfDate = myDate.endOfDay
Ben
la source
J'obtiens l'erreur de type non déclarée Datelorsque je l'essaye dans la cour de récréation
John Doe
1
Cela fonctionne pour moi ici. Avez-vous importé UIKit avec import UIKit?
Ben
2
@Ben Date and Calendar n'est pas dans UIKit, c'est dans Foundation, mais UIKit importe Foundation
Arbitur
4

Vous n'avez pas besoin de mettre les composants à zéro, ignorez-les simplement:

-(NSDate *)beginningOfDay:(NSDate *)date
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
    return [calendar dateFromComponents:components];
}
Maxim Lavrov
la source
2
"Nouvellement" disponible:, [calendar startOfDayForDate:date]mais il n'y -endOfDayForDate:en a malheureusement pas ...
Julian F. Weinert
4

Pour moi, aucune des réponses ici et ailleurs sur stackoverflow n'a fonctionné. Pour commencer aujourd'hui, je l'ai fait.

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

Notez ceci [gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];et [components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];.

Lorsqu'un calendrier est créé, il est initialisé avec le fuseau horaire actuel et lorsque la date est extraite de ses composants, puisque NSDate n'a pas de fuseau horaire, la date du fuseau horaire actuel est considérée comme le fuseau horaire UTC. Nous devons donc définir le fuseau horaire avant d'extraire les composants et plus tard lors de l'extraction de la date de ces composants.

Vikram Rao
la source
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
la source
4

Swift3 Utilisation de * XCode8
Apple supprime le NSdu nom de classe afin qu'il NSDatepuisse être remplacé par Date. Vous pouvez recevoir un avertissement du compilateur si vous essayez de les lancer en disant qu'ils échoueront toujours, mais ils fonctionnent bien lorsque vous les exécutez dans la cour de récréation.

J'ai remplacé mon NSDatemodèle de données de base généré par Dateet ils fonctionnent toujours.

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
la source
4

Dans Swift 3 et supérieur

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

Usage:

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

Une autre façon d'obtenir le résultat:

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
la source
2

Vous manquez NSDayCalendarUnitdans les composants.

LombaX
la source
2

Objectif c

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

Pour 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
la source
2

Je pense que la façon la plus succincte de le faire dans Swift est la suivante:

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)
    }
}
placardCoder
la source
C'est parfait! THX!
Baran Emre
1

Puisqu'il iOS 8.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+y a une fonction intégrée dans la Fondation, que vous pouvez utiliser directement. Pas besoin d'implémenter ses propres fonctions.

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

Vous pouvez donc l'utiliser de cette façon:

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

Il convient de se rappeler que cela prend en compte le fuseau horaire de l'appareil. Vous pouvez régler .timeZonesur calendarsi vous voulez avoir par exemple la zone UTC.

Lien vers les pages de référence Apple: https://developer.apple.com/reference/foundation/nscalendar/1417161-startofday .

Vive
la source
1

Juste une autre façon à l' aide dateInterval(of:start:interval:for:)deCalendar

Au retour startDatecontient le début de la journée et intervalle nombre de secondes dans la journée.

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
la source
1

Voici ce que j'utilise pour 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())

Fonctionne comme un charme pour moi. Vous pouvez l'ajouter à une extension pour les dates de début et de fin le Date, mais gardez à l'esprit que l'ajout d'une extension augmente le temps de compilation (sauf dans le même fichier que la classe), donc si vous n'en avez besoin qu'à un seul endroit ou dans une classe. .. n'utilisez pas d'extension.

Paul Peelen
la source
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
la source
0

Juste pour référence, un moyen simple de définir le début et la fin de la journée dans 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
la source
-1

Les unités de calendrier doivent être considérées comme des intervalles. À partir d'iOS 10, il Calendarexiste de bonnes méthodes pour cela

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

Vous pouvez utiliser la même méthode, pour obtenir le début / la fin de n'importe quel composant du calendrier (semaine / mois / année, etc.)

David Arve
la source
Ceci est une erreur. day?.endévalue à 12:00 AM demain, pas 23:59 PM ce soir.
Casey Wagner du