Come determinare se un NSDate è oggi?


156

Come verificare se NSDateappartiene oggi?

Lo controllavo usando i primi 10 caratteri di [aDate description]. [[aDate description] substringToIndex:10]restituisce la stringa in questo "YYYY-MM-DD"modo, ho confrontato la stringa con la stringa restituita da [[[NSDate date] description] substringToIndex:10].

Esiste un modo più rapido e / o accurato per verificare?

Grazie.


Credo che il mio metodo sia l'unico (al momento della mia scrittura di questo) che in realtà si riferisce alla documentazione di Apple e fa quello che suggeriscono al suo interno per determinare se due giorni sono uguali. Sebbene altri metodi possano funzionare, credo che il mio metodo sia la prova più futura. stackoverflow.com/a/17641925/901641
ArtOfWarfare

@ArtOfWarfare: non sono d'accordo. Il codice deve ancora fare i conti con intervalli di tempo, ma Apple fornisce i metodi che già Factor questa via - vedi: stackoverflow.com/a/17642033/106435
vikingosegundo

1
Il metodo "descrizione" NSDate visualizza la data in formato UTC, ovvero l'ora di Greenwich, senza alcuna correzione per l'ora legale. Se sei in tarda serata negli Stati Uniti o la mattina presto in India, non verrà visualizzato ciò che è "oggi" in base al tuo calendario. È necessario un oggetto NSCalendar per tradurre NSDate in un giorno e convertire la data corrente in un giorno, quindi confrontarli.
gnasher729,

Risposte:


308

In macOS 10.9+ e iOS 8+, c'è un metodo su NSCalendar / Calendar che fa esattamente questo!

- (BOOL)isDateInToday:(NSDate *)date 

Quindi lo faresti semplicemente

Objective-C:

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

Swift 3:

let today = Calendar.current.isDateInToday(date)

1
Controlla utilizzando l'SDK di iOS 8
Catfish_Man

1
Sono confuso dal commento sopra, questo è sicuramente iOS 8: - (BOOL) isDateInToday: (NSDate *) date NS_AVAILABLE (10_9, 8_0);
powerj1984,

Sì, è quello che ho detto. Il commentatore precedente era confuso guardando l'SDK di iOS 7, presumibilmente. Aveva una dichiarazione diversa.
Catfish_Man

per "oggi" questa è la risposta perfetta :)
Daij-Djan,

Risposta eccellente!
Supertecnoboff,

192

Puoi confrontare i componenti della data:

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
}

Modificare:

Mi piace di più il metodo di stefan, penso che sia per un'affermazione più chiara e comprensibile se:

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, ho incorporato il tuo suggerimento. Ho dovuto cercare quale fosse l'epoca, quindi per chiunque non lo sapesse, distingue tra BC e DC. Questo probabilmente non è necessario per la maggior parte delle persone, ma è facile da controllare e aggiunge una certa sicurezza, quindi l'ho incluso. Se stai andando per la velocità, questo probabilmente non è comunque un buon metodo.


NOTA come con molte risposte su SO, dopo 7 anni questo è completamente obsoleto. In Swift ora basta usare.isDateInToday


1
Grazie. Penso che sia una soluzione più pulita e sicura per il futuro alla mia domanda.
Seunghoon,

1
Penso che anche tu debba includere la componente era. Penso che sia un lancio di moneta tra i due approcci; isEqualToDate sembra più bello, ma sembra un po 'dispendioso e richiede circa lo stesso numero di righe per ricostruire una data dai componenti.
Chris Page

1
Poiché sia ​​"otherDay" che "oggi" hanno comunque riempito solo quei quattro campi (è tutto ciò che hai richiesto), puoi semplicemente usare [oggi uguale a: l'altro giorno], senza saltare i cerchi nel secondo.
Glenn Maynard,

@Glenn Stai suggerendo di confrontare NSDateComponents nel primo? Non ci avevo davvero pensato, ma sembra anche una buona idea.
David Kanarek,

1
@DavidKanarek Scavare vecchi post ... Non consiglierei di confrontare direttamente i componenti della data, se ti trovi in ​​una situazione in cui devi compensare uno dei componenti, potresti ottenere qualcosa come month == 3, day == 34. La conversione in date interpreterà correttamente questo come il 3 aprile, il confronto dei componenti della data non lo vedrà come il mese == 4, il giorno == 3.
Sean Kladek,

27

Questo è un risultato alla tua domanda, ma se vuoi stampare un NSDate con "Oggi" o "Ieri", usa la funzione

- (void)setDoesRelativeDateFormatting:(BOOL)b

per NSDateFormatter


3
Idea intelligente. Ma suppongo che la ricerca della stringa "Oggi" si interrompa se si localizza.
Basil Bourque,

16

Vorrei provare a rendere la data di oggi normalizzata a mezzanotte e la seconda data, normalizzare a mezzanotte, quindi confrontare se è la stessa data NSD.

Da un esempio Apple ecco come normalizzare fino alla mezzanotte di oggi, fare lo stesso per la seconda data e confrontare:

NSCalendar * gregorian = [[NSCalendar alloc]
                               initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents * components =
    [gregorian components:
                 (NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit)
                 fromDate:[NSDate date]];
NSDate * today = [gregorian dateFromComponents:components];

Penso che anche tu debba includere l'era.
Chris Page

Una data non dovrebbe essere normalizzata impostando l'ora su 12, ovvero mezzogiorno, non mezzanotte? Poiché la data è in GMT, impostandola su mezzogiorno si assicura che le variazioni del fuso orario in entrambi i modi (e salgono fino alle 12h solo in entrambi i modi) non "salti" nel giorno prima o dopo.
Artooras,

13

Estensione di lavoro Swift 3 e 4 del suggerimento di Catfish_Man:

extension Date {

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

}

12

Non c'è bisogno di destreggiarsi con componenti, epoche e cose.

NSCalendar fornisce un metodo per ottenere l'inizio di una determinata unità di tempo per una data esistente.

Questo codice avrà inizio oggi e un'altra data e lo confronterà. Se viene valutato NSOrderedSame, entrambe le date sono nello stesso giorno, quindi oggi.

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
}

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

Ha funzionato per me su Swift 2.0


6

Versione rapida della migliore risposta:

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
}

5

Fare riferimento alla voce della documentazione di Apple intitolata "Esecuzione di calcoli del calendario" [collegamento] .

Il listato 13 in quella pagina suggerisce che per determinare il numero di mezzanotte tra i giorni, usi:

- (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;
}

È quindi possibile determinare se due giorni sono uguali utilizzando tale metodo e verificando se restituisce 0 o meno.


2
Ricorda che un'era inizia alle 00:00 GMT! Questo metodo potrebbe non funzionare correttamente se una delle date (o entrambe) non si trova nel fuso orario GMT.
Andreas Ley,

5

Puoi anche controllare l'intervallo di tempo tra la data che hai e la data corrente:

[myDate timeIntervalSinceNow]

Questo ti darà l'intervallo di tempo, in secondi, tra myDate e la data / ora corrente.

Link .

Modifica: Nota a tutti: sono ben consapevole che [myDate timeIntervalSinceNow] non determina in modo inequivocabile se myDate è oggi.

Lascio questa risposta così com'è che se qualcuno è alla ricerca di qualcosa di simile e [myDate timeIntervalSinceNow] è utile, potrebbe trovarla qui.


1
Quello che dici è corretto, ma l'intervallo non aiuta davvero a verificare se la data è oggi.
Jaanus,

3
Tieni presente che ti dice se sei entro 24 ore da ora, ma non se le date sono le stesse. Una differenza di un secondo può essere data diversa e una differenza di 86399 secondi può essere la stessa data.
David Kanarek,

@ David: True. Non ci ho pensato mentre stavo facendo altre 3 cose mentre rispondevo alla domanda, e la tua risposta non è stata pubblicata quando ho caricato la pagina.
alesplin,

1
Questo non risponde affatto alla domanda. Dovresti eliminarlo.
vikingosegundo,

1
Questa risposta non è altro che rumore. Non tenta di rispondere alla domanda né è necessario fare riferimento in timeIntervalSinceNowquanto è trattato in molti altri post. Anche la gestione del confronto dei giorni con il controllo dei secondi incoraggia la divisione soggetta a errori di 86400.
vikingosegundo,

4

Swift Extension basato sulle migliori risposte:

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

2

Se si dispone di molti di questi confronti di date, le chiamate per calendar:components:fromDateiniziare impiegano molto tempo. Secondo alcuni profili che ho fatto, sembrano essere piuttosto costosi.

Supponiamo che tu stia provando a determinare quali da una serie di date, ad esempio NSArray *datesToCompare, sono lo stesso giorno di un determinato giorno, ad esempio NSDate *baseDate, quindi puoi utilizzare qualcosa di simile al seguente (parzialmente adattato da una risposta sopra):

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

Produzione:

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

Grazie per questo. Il modo accettato era troppo dispendioso in termini di CPU per la nostra app, quindi dovevamo ottimizzarlo.
Accatyyc,

1
Questo codice fallirà per l'ora legale. Chiunque usi un codice con il famigerato numero 86.400 merita il mal di testa che può causare.
vikingosegundo,

2

C'è un modo più semplice di molte delle risposte sopra!

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

if([calendar isDateInToday:date]) {
    //do stuff
}

1
Come la risposta di Catfish_Man!
Thanh Pham,

0

Questo potrebbe probabilmente essere rielaborato come categoria NSDate, ma ho usato:

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

(Tuttavia, confrontare le due stringhe di date come nella domanda originale sembra quasi "più pulito" ..)


Perché includere ore, minuti e secondi durante la creazione compsquando li si imposta immediatamente su zero? Inoltre, penso che sia necessario includere l'era.
Chris Page

Si noti che mentre questo funzionerà in quasi tutti i casi, ci sono cose come i secondi bisestili. Non so se o come la classe NSDate di Apple li gestisca, ma c'è qualche possibilità che non funzioni. Questo è più un "lo sapevi" piuttosto che una critica al tuo metodo in quanto sono estremamente rari (24 nella storia).
David Kanarek,

che dire di date che non durano 24 ore, ma 23 o 25 a causa dell'ora legale?
vikingosegundo,

0

per iOS7 e precedenti:

//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 :-)");


0

La soluzione corretta e sicura senza forzatura, che funziona su Swift 2.2 e prima di 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
}

-1

Ecco la mia risposta da 2 centesimi basata sulla risposta accettata ma che supporta anche l'API più recente. Nota: utilizzo il calendario gregoriano poiché la maggior parte dei timestamp sono GMT ma cambio il tuo come ritieni opportuno

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
}

-3

La mia soluzione è calcolare quanti giorni sono passati dal 1970 per divisione e confrontare la parte intera

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

In realtà non è una cattiva soluzione. Non sono sicuro del motivo per cui è stato votato così in basso. L'uso della divisione intera interromperà il resto e confronterà solo il valore "giorni" del tempo lungo unix, dopo aver corretto GMT. Penso che questa sia la soluzione più pulita di tutte per essere abbastanza sincera.
devios1

1
@chaiguy: questa è una pessima soluzione, come si suppone, che ogni giorno duri 24 ore - in caso contrario. A causa dell'ora legale, i giorni possono durare 23, 24 o 25 ore.
vikingosegundo,

Hmm questo è vero. Tuttavia, la media annuale è esattamente di 24 ore, quindi sarà potenzialmente solo nel caso peggiore di un'ora, come la vedo io. Forse questo potrebbe essere verificato esplicitamente.
devios1

2
@chaiguy: no. non usare mai un codice che contiene 60 * 60 * 24. o vivere con il mal di testa.
vikingosegundo,

"secondsFromGMT" cambia bruscamente due volte all'anno nella maggior parte dei luoghi del mondo. Molto più sicuro utilizzare i metodi 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;  
}  

4
Questo non ti dice se entrambi gli NSDate hanno la stessa data, solo quella precedente, anche se sono lo stesso giorno.
David Kanarek,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.