Como determinar se um NSDate é hoje?

156

Como verificar se um NSDatepertence a hoje?

Eu costumava checá-lo usando os 10 primeiros caracteres de [aDate description]. [[aDate description] substringToIndex:10]retorna string "YYYY-MM-DD"assim, eu comparei a string com a string retornada por [[[NSDate date] description] substringToIndex:10].

Existe uma maneira mais rápida e / ou limpa de verificar?

Obrigado.

Seunghoon
fonte
Acredito que meu método é o único (até o momento em que escrevi isso) que realmente se refere à documentação da Apple e faz o que eles sugerem para determinar se dois dias são iguais. Embora outros métodos possam funcionar, acredito que meu método seja a prova mais futura. stackoverflow.com/a/17641925/901641
ArtOfWarfare
@ArtOfWarfare: eu discordo. Seu código ainda precisa lidar com intervalos de tempo, mas a Apple fornece métodos que já este factor de distância - ver: stackoverflow.com/a/17642033/106435
vikingosegundo
1
O método de "descrição" do NSDate exibe a data no formato UTC, que é o horário médio de Greenwich, sem correção para o horário de verão. Se você estiver atrasado à noite nos EUA ou no início da manhã na Índia, ele não exibirá o que é "hoje" de acordo com o seu calendário. Você precisa de um objeto NSCalendar para converter o NSDate em um dia, converter a data atual em um dia e compará-los.
gnasher729

Respostas:

308

No macOS 10.9+ e iOS 8+, existe um método no NSCalendar / Calendar que faz exatamente isso!

- (BOOL)isDateInToday:(NSDate *)date 

Então você simplesmente faria

Objetivo-C:

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

Swift 3:

let today = Calendar.current.isDateInToday(date)
Catfish_Man
fonte
1
Verifique usando o iOS 8 SDK
Catfish_Man
1
Estou confuso com o comentário acima, este é definitivamente o iOS 8: - (BOOL) isDateInToday: (NSDate *) date NS_AVAILABLE (10_9, 8_0);
powerj1984
Sim, foi o que eu disse. O comentarista anterior ficou confuso olhando para o iOS 7 SDK, presumivelmente. Tinha uma declaração diferente.
Catfish_Man
para 'hoje' esta é a resposta perfeita :)
Daij-Djan
Excelente resposta!
Supertecnoboff
192

Você pode comparar os componentes da 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
}

Editar:

Eu gosto mais do método de stefan, acho que torna uma declaração if mais limpa e compreensível:

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, eu incorporei sua sugestão. Eu tive que procurar o que era era, então para quem não sabe, ele distingue entre BC e AD. Provavelmente, isso é desnecessário para a maioria das pessoas, mas é fácil verificar e acrescenta alguma certeza, então eu a incluí. Se você está buscando velocidade, esse provavelmente não é um bom método.


OBSERVAÇÃO Como em muitas respostas sobre SO, após 7 anos, isso está totalmente desatualizado. No Swift agora basta usar.isDateInToday

David Kanarek
fonte
1
Obrigado. Eu acho que é uma solução mais limpa e segura para o futuro da minha pergunta.
Seunghoon
1
Eu acho que você precisa incluir o componente era também. Eu acho que é um sorteio entre as duas abordagens; isEqualToDate parece mais agradável, mas parece um pouco inútil e leva aproximadamente o mesmo número de linhas para reconstruir uma data dos componentes.
Chris Página
1
Como "otherDay" e "today" têm apenas esses quatro campos preenchidos de qualquer maneira (é tudo o que você solicitou), você pode usar [today equalTo: otherDay], sem pular os bastidores no segundo.
Glenn Maynard
@ Glenn Você está sugerindo que eu compare o NSDateComponents no primeiro? Eu realmente não tinha pensado nisso, mas também parece uma boa ideia.
precisa saber é o seguinte
1
@DavidKanarek Desenterrando postagens antigas ... Eu não recomendaria comparar diretamente os componentes de data. Se você se deparar com uma situação em que precisa compensar um dos componentes, poderá obter algo como mês == 3, dia == 34. A conversão para datas o interpretará corretamente como 3 de abril, comparando os componentes da data com o mesmo mês == 4, dia == 3. #
Sean Kladek
27

Esta é uma ramificação para sua pergunta, mas se você deseja imprimir um NSDate com "Hoje" ou "Ontem", use a função

- (void)setDoesRelativeDateFormatting:(BOOL)b

para NSDateFormatter

Jon
fonte
3
Ideia inteligente. Mas suponho que procurar a string "Hoje" seja interrompida se você localizar.
Basil Bourque
16

Eu tentaria obter a data de hoje normalizada para meia-noite e a segunda data, normalizar para meia-noite e comparar se é a mesma NSDate.

De um exemplo da Apple, veja como você normaliza para a meia-noite da data de hoje, faça o mesmo para a segunda data e compare:

NSCalendar * gregorian = [[NSCalendar alloc]
                               initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents * components =
    [gregorian components:
                 (NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit)
                 fromDate:[NSDate date]];
NSDate * today = [gregorian dateFromComponents:components];
stefanB
fonte
Eu acho que você precisa incluir a época também.
Chris Página
Uma data não deve ser normalizada definindo a hora para 12, ou seja, meio-dia, não meia-noite? Como a data está no GMT, configurá-lo para o meio-dia garante que as variações de fuso horário nos dois sentidos (e elas durem até 12h apenas nos dois sentidos) não "saltem" para o dia anterior ou posterior.
Artooras 6/06/2014
13

Working Swift 3 e 4 extensão da sugestão de Catfish_Man:

extension Date {

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

}
Benno Kress
fonte
12

Não há necessidade de fazer malabarismos com componentes, eras e outras coisas.

O NSCalendar fornece um método para obter o início de uma determinada unidade de tempo para uma data existente.

Este código terá o início de hoje e outra data e comparará isso. Se for avaliado NSOrderedSame, as duas datas são no mesmo dia - hoje.

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

Trabalhou para mim no Swift 2.0

Omar Albeik
fonte
6

Versão rápida da melhor resposta:

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
fonte
5

Consulte a entrada de documentação da Apple intitulada "Executando cálculos de calendário" [link] .

A Listagem 13 dessa página sugere que, para determinar o número de meia-noite entre os dias, use:

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

Você pode determinar se dois dias são iguais usando esse método e verificando se ele retorna 0 ou não.

ArtOfWarfare
fonte
2
Lembre-se de que uma Era começa às 00:00 GMT! Esse método pode não funcionar corretamente se uma das datas (ou ambas) não estiver no fuso horário GMT.
Andreas Ley
5

Você também pode verificar o intervalo de tempo entre a data que você tem e a data atual:

[myDate timeIntervalSinceNow]

Isso fornecerá o intervalo de tempo, em segundos, entre myDate e a data / hora atual.

Link .

Edit: Note to everyone: Estou ciente de que [myDate timeIntervalSinceNow] não determina inequivocamente se myDate é hoje.

Estou deixando esta resposta como está, para que, se alguém estiver procurando por algo semelhante e [myDate timeIntervalSinceNow] seja útil, eles poderão encontrá-la aqui.

alesplin
fonte
1
O que você diz está correto, mas o intervalo não ajuda realmente a verificar se a data é hoje.
Jaanus
3
Lembre-se de que é isso que indica se você está dentro de 24 horas desde agora, mas não se as datas forem as mesmas. Uma diferença de um segundo pode ter datas diferentes e uma diferença de 86399 segundos pode ter a mesma data.
David Kanarek
@ David: True. Não pensei nisso, pois eu estava fazendo três outras coisas enquanto respondia à pergunta, e sua resposta não foi publicada quando eu carreguei a página.
Alsplin
1
Isso não responde à pergunta. Você deve excluí-lo.
vikingosegundo
1
Essa resposta não passa de barulho. Ele não tenta responder à pergunta, nem é necessário se referir à timeIntervalSinceNowmedida que ela é abordada em muitos outros posts. A manipulação da comparação do dia com a verificação de segundos incentiva a divisão propensa a erros em 86400.
vikingosegundo
4

Extensão Swift com base nas melhores respostas:

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
fonte
2

Se você tiver muitas dessas comparações de datas, as chamadas para calendar:components:fromDate começarão a demorar muito tempo. De acordo com alguns perfis que fiz, eles parecem bastante caros.

Digamos que você esteja tentando determinar quais de algumas datas, digamos NSArray *datesToCompare, são o mesmo dia que um determinado dia, digamos NSDate *baseDate, então você pode usar algo como o seguinte (parcialmente adaptado de uma resposta acima):

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

Resultado:

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
fonte
Obrigado por isso. A maneira aceita era muito intensiva da CPU para o nosso aplicativo, por isso precisávamos otimizar isso.
Accatyyc
1
Este código falhará no horário de verão. Todo mundo que usa código com o número infame 86.400 merece a dor de cabeça que isso pode causar.
Vikingosegundo
2

Existe uma maneira mais fácil do que muitas das respostas acima!

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

if([calendar isDateInToday:date]) {
    //do stuff
}
Aryland
fonte
1
O mesmo que a resposta de Catfish_Man!
Thanh Pham
0

Provavelmente isso poderia ser reformulado como uma categoria NSDate, mas eu usei:

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

(No entanto, comparar as duas cadeias de datas como na pergunta original quase parece "mais limpo" ..)

LeoN
fonte
Por que incluir horas, minutos e segundos ao criar compsquando você os define imediatamente como zero? Além disso, acho que você precisa incluir a época.
Chris Page
Observe que, embora isso funcione em quase todos os casos, existem coisas como segundos bissextos. Não sei se ou como a classe NSDate da Apple lida com eles, mas há alguma chance de isso não funcionar. Isso é mais um "você sabia" do que uma crítica ao seu método, pois eles são extremamente raros (24 na história).
David Kanarek
e as datas que não duram 24 horas, mas 23 ou 25 devido ao horário de verão?
Vikingosegundo 4/10
0

para iOS7 e versões anteriores:

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

A solução correta e segura sem forçar a quebra, trabalhando no Swift 2.2 e antes do 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
fonte
-1

Aqui está a minha resposta de 2 centavos com base na resposta aceita, mas também com suporte à API mais recente. Nota: Eu uso o calendário gregoriano, pois a maioria dos carimbos de hora é GMT, mas altere a sua como achar melhor

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
fonte
-3

Minha solução é calcular quantos dias passaram desde 1970 por divisão e comparar a parte inteira

#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
fonte
Na verdade, essa não é uma solução ruim. Não sei por que foi tão votado. O uso da divisão inteira cortará o restante e comparará apenas o valor "dias" do unix por muito tempo, depois de corrigir o GMT. Penso que esta é a solução mais limpa de todas, para ser bastante franca.
devios1
1
@chaiguy: esta é uma solução ruim, pois supõe que cada dia dura 24 horas - o que não é o caso. Devido ao horário de verão, os dias podem durar 23, 24 ou 25 horas.
Vikingosegundo 4/10
Hmm isso é verdade. No entanto, a média por ano é exatamente de 24 horas, portanto, ela só poderá ser desativada em uma hora no pior caso, a meu ver. Talvez isso possa ser verificado explicitamente.
precisa saber é o seguinte
2
@chaiguy: não. apenas nunca use um código que contenha 60 * 60 * 24. ou viva com sua dor de cabeça.
vikingosegundo
"secondsFromGMT" muda abruptamente duas vezes por ano na maioria dos lugares do mundo. Muito mais seguro usar os métodos 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
fonte
4
Isso não informa se os dois NSDates são da mesma data, apenas qual é anterior, mesmo que sejam no mesmo dia.
David Kanarek