Comment déterminer si un NSDate est aujourd'hui?

156

Comment vérifier si un NSDate appartient à aujourd'hui?

J'avais l'habitude de le vérifier en utilisant les 10 premiers caractères de [aDate description]. [[aDate description] substringToIndex:10]renvoie une chaîne comme "YYYY-MM-DD"celle-ci, j'ai comparé la chaîne avec la chaîne renvoyée par [[[NSDate date] description] substringToIndex:10].

Existe-t-il un moyen plus rapide et / ou plus efficace de vérifier?

Merci.

Seunghoon
la source
Je crois que ma méthode est la seule (au moment où j'écris ceci) qui fait réellement référence à la documentation d'Apple et fait ce qu'elle suggère pour déterminer si deux jours sont identiques. Bien que d'autres méthodes puissent fonctionner, je pense que ma méthode est la plus à l'épreuve du temps. stackoverflow.com/a/17641925/901641
ArtOfWarfare
@ArtOfWarfare: Je ne suis pas d'accord. Votre code doit toujours gérer les intervalles de temps, mais Apple fournit des méthodes qui
tiennent
1
La méthode de «description» NSDate affiche la date au format UTC, c'est-à-dire l'heure de Greenwich, sans correction pour l'heure d'été. Si vous êtes en retard dans la soirée aux États-Unis, ou tôt le matin en Inde, il n'affichera pas ce qui est «aujourd'hui» selon votre calendrier. Vous avez besoin d'un objet NSCalendar pour traduire le NSDate en un jour et pour convertir la date actuelle en un jour, puis les comparer.
gnasher729

Réponses:

308

Dans macOS 10.9+ et iOS 8+, il existe une méthode sur NSCalendar / Calendar qui fait exactement cela!

- (BOOL)isDateInToday:(NSDate *)date 

Alors tu ferais simplement

Objectif c:

BOOL today = [[NSCalendar currentCalendar] isDateInToday:date];

Swift 3:

let today = Calendar.current.isDateInToday(date)
Catfish_Man
la source
1
Vérifiez à l'aide du SDK iOS 8
Catfish_Man
1
Je suis confus par le commentaire ci-dessus, c'est définitivement iOS 8: - (BOOL) isDateInToday: (NSDate *) date NS_AVAILABLE (10_9, 8_0);
powerj1984
Oui, c'est ce que j'ai dit. Le commentateur précédent était probablement confus en regardant le SDK iOS 7, probablement. Il y avait une déclaration différente.
Catfish_Man
pour 'aujourd'hui' c'est la réponse parfaite :)
Daij-Djan
Excellente réponse!
Supertecnoboff
192

Vous pouvez comparer les composants de date:

NSDateComponents *otherDay = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:aDate];
NSDateComponents *today = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]];
if([today day] == [otherDay day] &&
   [today month] == [otherDay month] &&
   [today year] == [otherDay year] &&
   [today era] == [otherDay era]) {
    //do stuff
}

Éditer:

J'aime plus la méthode de Stefan, je pense que cela donne une déclaration if plus propre et plus compréhensible:

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
NSDate *today = [cal dateFromComponents:components];
components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:aDate];
NSDate *otherDate = [cal dateFromComponents:components];

if([today isEqualToDate:otherDate]) {
    //do stuff
}

Chris, j'ai incorporé votre suggestion. J'ai dû chercher quelle était l'époque, donc pour tous ceux qui ne le savent pas, cela fait la distinction entre la Colombie-Britannique et l'AD. Ceci est probablement inutile pour la plupart des gens, mais c'est facile à vérifier et ajoute une certaine certitude, donc je l'ai inclus. Si vous optez pour la vitesse, ce n'est probablement pas une bonne méthode de toute façon.


NOTEZ comme pour de nombreuses réponses sur SO, après 7 ans, c'est totalement dépassé. Dans Swift, utilisez simplement.isDateInToday

David Kanarek
la source
1
Merci. Je pense que c'est une solution plus soignée et plus sûre pour l'avenir à ma question.
Seunghoon
1
Je pense que vous devez également inclure le composant d'ère. Je pense que c'est un tirage au sort entre les deux approches; isEqualToDate semble plus agréable, mais cela semble un peu inutile et prend environ le même nombre de lignes pour reconstruire une date à partir des composants.
Chris Page
1
Puisque les champs "otherDay" et "today" n'ont de toute façon rempli que ces quatre champs (c'est tout ce que vous avez demandé), vous pouvez simplement utiliser [today equalTo: otherDay], sans sauter les cerceaux dans le second.
Glenn Maynard
@Glenn Proposez-vous de comparer NSDateComponents dans le premier? Je n'avais pas vraiment pensé à ça, mais cela me semble aussi être une bonne idée.
David Kanarek
1
@DavidKanarek Déterrer d'anciens messages ... Je ne recommanderais pas de comparer directement les composants de date, si vous vous trouvez dans une situation où vous devez compenser l'un des composants, vous pouvez obtenir quelque chose comme mois == 3, jour == 34. La conversion en dates interprétera correctement cela comme le 3 avril, la comparaison des composants de date ne verra pas cela comme étant le même que mois == 4, jour == 3.
Sean Kladek
27

Ceci est une émanation de votre question, mais si vous souhaitez imprimer un NSDate avec "Aujourd'hui" ou "Hier", utilisez la fonction

- (void)setDoesRelativeDateFormatting:(BOOL)b

pour NSDateFormatter

Jon
la source
3
Idée brillante. Mais je suppose que la recherche de la chaîne "Aujourd'hui" se briserait si vous localisez.
Basil Bourque
16

J'essaierais de normaliser la date du jour à minuit et la deuxième date, de normaliser à minuit, puis de comparer s'il s'agit du même NSDate.

À partir d' un exemple Apple, voici comment vous normalisez la date d'aujourd'hui à minuit, faites de même pour la deuxième date et comparez:

NSCalendar * gregorian = [[NSCalendar alloc]
                               initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents * components =
    [gregorian components:
                 (NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit)
                 fromDate:[NSDate date]];
NSDate * today = [gregorian dateFromComponents:components];
stefanB
la source
Je pense que vous devez également inclure l'époque.
Chris Page
Une date ne devrait-elle pas être normalisée en mettant l'heure à 12, c'est-à-dire midi et non minuit? Étant donné que la date est en GMT, la régler à midi garantit que les variations de fuseau horaire dans les deux sens (et elles ne vont jusqu'à 12h que dans les deux sens) ne «sautent» pas dans la journée avant ou après.
artooras
13

Travail Swift 3 et 4 l' extension de la suggestion faite par Catfish_Man:

extension Date {

    func isToday() -> Bool {
        return Calendar.current.isDateInToday(self)
    }

}
Benno Kress
la source
12

Pas besoin de jongler avec les composants, les époques et tout.

NSCalendar fournit une méthode pour obtenir le début d'une certaine unité de temps pour une date existante.

Ce code obtiendra le début d'aujourd'hui et une autre date et comparera cela. S'il évalue à NSOrderedSame, les deux dates sont le même jour - donc aujourd'hui.

NSDate *today = nil;
NSDate *beginningOfOtherDate = nil;

NSDate *now = [NSDate date];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&today interval:NULL forDate:now];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&beginningOfOtherDate interval:NULL forDate:beginningOfOtherDate];

if([today compare:beginningOfOtherDate] == NSOrderedSame) {
    //otherDate is a date in the current day
}
vikingosegundo
la source
9
extension NSDate {
  func isToday() -> Bool {
    let cal = NSCalendar.currentCalendar()
    var components = cal.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
    let today = cal.dateFromComponents(components)!

    components = cal.components([.Era, .Year, .Month, .Day], fromDate:self)
    let otherDate = cal.dateFromComponents(components)!

    return today.isEqualToDate(otherDate)
}

A travaillé pour moi sur Swift 2.0

Omar Albeik
la source
6

Version Swift de la meilleure réponse:

let cal = NSCalendar.currentCalendar()
var components = cal.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
let today = cal.dateFromComponents(components)!

components = cal.components([.Era, .Year, .Month, .Day], fromDate:aDate);
let otherDate = cal.dateFromComponents(components)!

if(today.isEqualToDate(otherDate)) {
    //do stuff
}
Vojtech Vrbka
la source
5

Reportez-vous à l'entrée de documentation d'Apple intitulée «Effectuer des calculs de calendrier» [lien] .

Le listing 13 sur cette page suggère que pour déterminer le nombre de minuit entre les jours, vous utilisez:

- (NSInteger)midnightsFromDate:(NSDate *)startDate toDate:(NSDate *)endDate
{
    NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
    NSInteger startDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                             inUnit:NSEraCalendarUnit
                                            forDate:startDate];
    NSInteger endDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                           inUnit:NSEraCalendarUnit
                                          forDate:endDate];
    return endDay - startDay;
}

Vous pouvez ensuite déterminer si deux jours sont identiques en utilisant cette méthode et voir si elle renvoie 0 ou non.

ArtOfWarfare
la source
2
Gardez à l'esprit qu'une ère commence à 00h00 GMT! Cette méthode peut ne pas fonctionner correctement si l'une des dates (ou les deux) n'est pas dans le fuseau horaire GMT.
Andreas Ley
5

Vous pouvez également vérifier l'intervalle de temps entre la date que vous avez et la date actuelle:

[myDate timeIntervalSinceNow]

Cela vous donnera l'intervalle de temps, en secondes, entre maDate et la date / heure actuelle.

Lien .

Edit: Note à tout le monde: je suis bien conscient que [myDate timeIntervalSinceNow] ne détermine pas sans ambiguïté si myDate est aujourd'hui.

Je laisse cette réponse telle quelle pour que si quelqu'un cherche quelque chose de similaire et que [myDate timeIntervalSinceNow] est utile, il peut le trouver ici.

alespline
la source
1
Ce que vous dites est correct, mais l'intervalle n'aide pas vraiment à vérifier si la date est aujourd'hui.
Jaanus
3
Gardez simplement à l'esprit que cela vous indique si vous êtes dans les 24 heures depuis maintenant, mais pas si les dates sont les mêmes. Une différence d'une seconde peut être des dates différentes et une différence de 86399 secondes peut être la même date.
David Kanarek
@David: Vrai. Je n'ai pas pensé à cela car je faisais 3 autres choses en répondant à la question, et votre réponse n'a pas été publiée lorsque j'ai chargé la page.
alesplin
1
Cela ne répond pas du tout à la question. Vous devriez le supprimer.
vikingosegundo
1
Cette réponse n'est rien d'autre que du bruit. Il n'essaie pas de répondre à la question et il n'est pas nécessaire de s'y référer timeIntervalSinceNowcar il est couvert dans de nombreux autres articles. La gestion de la comparaison des jours avec la vérification des secondes encourage également la division sujette aux erreurs par 86400.
vikingosegundo
4

Extension Swift basée sur les meilleures réponses:

extension NSDate {
    func isToday() -> Bool {
        let cal = NSCalendar.currentCalendar()
        if cal.respondsToSelector("isDateInToday:") {
            return cal.isDateInToday(self)
        }
        var components = cal.components((.CalendarUnitEra | .CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay), fromDate:NSDate())
        let today = cal.dateFromComponents(components)!

        components = cal.components((.CalendarUnitEra | .CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay), fromDate:self);
        let otherDate = cal.dateFromComponents(components)!
        return today.isEqualToDate(otherDate)
    }
}
ZiggyST
la source
2

Si vous avez beaucoup de ces comparaisons de dates, les appels à calendar:components:fromDatecommencer prennent beaucoup de temps. Selon certains profils que j'ai réalisés, ils semblent être assez chers.

Supposons que vous essayez de déterminer lesquelles parmi une série de dates, disons NSArray *datesToCompare, sont le même jour qu'un jour donné, par exemple NSDate *baseDate, vous pouvez utiliser quelque chose comme ce qui suit (partiellement adapté d'une réponse ci-dessus):

NSDate *baseDate = [NSDate date];

NSArray *datesToCompare = [NSArray arrayWithObjects:[NSDate date], 
                           [NSDate dateWithTimeIntervalSinceNow:100],
                           [NSDate dateWithTimeIntervalSinceNow:1000],
                           [NSDate dateWithTimeIntervalSinceNow:-10000],
                           [NSDate dateWithTimeIntervalSinceNow:100000],
                           [NSDate dateWithTimeIntervalSinceNow:1000000],
                           [NSDate dateWithTimeIntervalSinceNow:50],
                           nil];

// determine the NSDate for midnight of the base date:
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* comps = [calendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) 
                                       fromDate:baseDate];
NSDate* theMidnightHour = [calendar dateFromComponents:comps];

// set up a localized date formatter so we can see the answers are right!
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];

// determine which dates in an array are on the same day as the base date:
for (NSDate *date in datesToCompare) {
    NSTimeInterval interval = [date timeIntervalSinceDate:theMidnightHour];
    if (interval >= 0 && interval < 60*60*24) {
        NSLog(@"%@ is on the same day as %@", [dateFormatter stringFromDate:date], [dateFormatter stringFromDate:baseDate]);
    }
    else {
        NSLog(@"%@ is NOT on the same day as %@", [dateFormatter stringFromDate:date], [dateFormatter stringFromDate:baseDate]);
    }
}

Production:

Nov 23, 2011 1:32:00 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:33:40 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:48:40 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 10:45:20 AM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 24, 2011 5:18:40 PM is NOT on the same day as Nov 23, 2011 1:32:00 PM
Dec 5, 2011 3:18:40 AM is NOT on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:32:50 PM is on the same day as Nov 23, 2011 1:32:00 PM
Julian Richardson
la source
Merci pour cela. La méthode acceptée était beaucoup trop gourmande en ressources processeur pour notre application, nous devions donc l'optimiser.
Accatyyc
1
Ce code échouera pour les heures d'été. Tous ceux qui utilisent du code avec le tristement célèbre numéro 86 400 méritent le mal de tête qu'il peut causer.
vikingosegundo
2

Il existe un moyen plus simple que la plupart des réponses ci-dessus!

NSDate *date = ... // The date you wish to test
NSCalendar *calendar = [NSCalendar currentCalendar];

if([calendar isDateInToday:date]) {
    //do stuff
}
aryland
la source
1
Identique à la réponse de Catfish_Man!
Thanh Pham
0

Cela pourrait probablement être retravaillé en tant que catégorie NSDate, mais j'ai utilisé:

// Seconds per day (24h * 60m * 60s)
#define kSecondsPerDay 86400.0f

+ (BOOL) dateIsToday:(NSDate*)dateToCheck
{
    // Split today into components
    NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents* comps = [gregorian components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit) 
                                        fromDate:[NSDate date]];

    // Set to this morning 00:00:00
    [comps setHour:0];
    [comps setMinute:0];
    [comps setSecond:0];
    NSDate* theMidnightHour = [gregorian dateFromComponents:comps];
    [gregorian release];

    // Get time difference (in seconds) between date and then
    NSTimeInterval diff = [dateToCheck timeIntervalSinceDate:theMidnightHour];
    return ( diff>=0.0f && diff<kSecondsPerDay );
}

(Cependant, comparer les deux chaînes de date comme dans la question d'origine semble presque `` plus propre ''.)

LeoN
la source
Pourquoi inclure les heures, les minutes et les secondes lors de la création compslorsque vous les mettez immédiatement à zéro? De plus, je pense que vous devez inclure l'époque.
Chris Page
Veuillez noter que même si cela fonctionnera dans presque tous les cas, il existe des choses telles que les secondes intercalaires. Je ne sais pas si ou comment la classe NSDate d'Apple les gère, mais il y a des chances que cela ne fonctionne pas. Il s'agit davantage d'un "saviez-vous" que d'une critique de votre méthode car ils sont extrêmement rares (24 dans l'histoire).
David Kanarek
Qu'en est-il des dates qui ne durent pas 24 heures, mais 23 ou 25 en raison de l'heure d'été?
vikingosegundo
0

pour iOS7 et versions antérieures:

//this is now => need that for the current date
NSDate * now = [NSDate date];

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone systemTimeZone]];

NSDateComponents * components = [calendar components:( NSYearCalendarUnit|    NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate: now];

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

//this is Today's Midnight
NSDate *todaysMidnight = [calendar dateFromComponents: components];



//now timeIntervals since Midnight => in seconds
NSTimeInterval todayTimeInterval = [now timeIntervalSinceDate: todaysMidnight];

//now timeIntervals since OtherDate => in seconds
NSTimeInterval otherDateTimeInterval = [now timeIntervalSinceDate: otherDate];

if(otherDateTimeInterval > todayTimeInterval) //otherDate is not in today
{
    if((otherDateTimeInterval - todayTimeInterval) <= 86400) //86400 == a day total seconds
    {
        @"yesterday";
    }
    else
    {
        @"earlier";
    }
}
else
{
    @"today";
}


now = nil;
calendar = nil;
components = nil;
todaysMidnight = nil;

NSLog("Thank you :-)");
user3744424
la source
0

La solution correcte et sûre sans déballage forcé, fonctionnant sur Swift 2.2 et avant iOS 8:

func isToday() -> Bool {
    let calendar = NSCalendar.currentCalendar()
    if #available(iOS 8.0, *) {
        return calendar.isDateInToday(self)
    }

    let todayComponents = calendar.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
    let dayComponents = calendar.components([.Era, .Year, .Month, .Day], fromDate:self)

    guard let today = calendar.dateFromComponents(todayComponents),
        day = calendar.dateFromComponents(dayComponents) else {
        return false
    }

    return today.compare(day) == .OrderedSame
}
fpg1503
la source
-1

Voici ma réponse de 2 centimes basée sur la réponse acceptée mais prenant également en charge la nouvelle API. Remarque: j'utilise le calendrier grégorien car la plupart des horodatages sont GMT mais modifiez le vôtre comme bon vous semble

func isDateToday(date: NSDate) -> Bool {
    let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
    if calendar.respondsToSelector("isDateInToday:") {
        return calendar.isDateInToday(date)
    }
    let dateComponents = NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay
    let today = calendar.dateFromComponents(calendar.components(dateComponents, fromDate: NSDate()))!
    let dateToCompare = calendar.dateFromComponents(calendar.components(dateComponents, fromDate: date))!

    return dateToCompare == today
}
Daniel Galasko
la source
-3

Ma solution est de calculer le nombre de jours écoulés depuis 1970 par division et de comparer la partie entière

#define kOneDay (60*60*24)
- (BOOL)isToday {
  NSInteger offset = [[NSTimeZone defaultTimeZone] secondsFromGMT];

  NSInteger days =[self timeIntervalSince1970] + offset;
  NSInteger currentDays = [[NSDate date] timeIntervalSince1970] + offset;
  return (days / kOneDay == currentDays / kOneDay);
}
stcui
la source
Ce n'est en fait pas une mauvaise solution. Je ne sais pas pourquoi il a été si défavorisé. L'utilisation de la division entière coupera le reste et ne comparera que la valeur "jours" du temps long unix, après correction pour GMT. Je pense que c'est la solution la plus propre de toutes pour être tout à fait franc.
devios1
1
@chaiguy: c'est une mauvaise solution car elle suppose, que chaque jour dure 24 heures - ce qui n'est pas le cas. En raison de l'heure d'été, les jours peuvent durer 23, 24 ou 25 heures.
vikingosegundo
Hmm c'est vrai. Cependant, la moyenne par an est exactement de 24 heures, donc il ne sera potentiellement que d'une heure dans le pire des cas, comme je le vois. Peut-être que cela pourrait être vérifié explicitement.
devios1
2
@chaiguy: non. n'utilisez jamais un code contenant 60 * 60 * 24. ou vivez avec votre mal de tête.
vikingosegundo
"secondsFromGMT" change brusquement deux fois par an dans la plupart des endroits du monde. Il est beaucoup plus sûr d'utiliser les méthodes NSCalendar.
gnasher729
-4
NSDate *dateOne = yourDate;
NSDate *dateTwo = [NSDate date];  

switch ([dateOne compare:dateTwo])
{  
    case NSOrderedAscending:  
        NSLog(@”NSOrderedAscending”);  
        break;  

    case NSOrderedSame: 
        NSLog(@”NSOrderedSame”);  
        break;

    case NSOrderedDescending:  
        NSLog(@”NSOrderedDescending”);  
        break;  
}  
Rajesh
la source
4
Cela ne vous dit pas si les deux NSDates sont la même date, juste laquelle est la plus ancienne, même si elles sont le même jour.
David Kanarek