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.