obter NSDate hoje, ontem, esta semana, semana passada, este mês, mês passado ... variáveis

86

Estou tentando fazer é obter NSDate hoje, ontem, esta semana, semana passada, este mês, último mês variáveis ​​prontas para comparação para cabeçalhos a serem adicionados em titleForHeaderInSection de UITableView

O que desejo é feito manualmente no código abaixo para a data 11/12/2009

 NSDate *today = [NSDate dateWithString:@"2009-12-11 00:00:00 +0000"];
 NSDate *yesterday = [NSDate dateWithString:@"2009-12-10 00:00:00 +0000"];
 NSDate *thisWeek = [NSDate dateWithString:@"2009-12-06 00:00:00 +0000"];
 NSDate *lastWeek = [NSDate dateWithString:@"2009-11-30 00:00:00 +0000"];
 NSDate *thisMonth = [NSDate dateWithString:@"2009-12-01 00:00:00 +0000"];
 NSDate *lastMonth = [NSDate dateWithString:@"2009-11-01 00:00:00 +0000"];
hasnat
fonte

Respostas:

93

Adaptado do Guia de Programação de Data e Hora :

// Right now, you can remove the seconds into the day if you want
NSDate *today = [NSDate date];

// All intervals taken from Google
NSDate *yesterday = [today dateByAddingTimeInterval: -86400.0];
NSDate *thisWeek  = [today dateByAddingTimeInterval: -604800.0];
NSDate *lastWeek  = [today dateByAddingTimeInterval: -1209600.0];

// To get the correct number of seconds in each month use NSCalendar
NSDate *thisMonth = [today dateByAddingTimeInterval: -2629743.83];
NSDate *lastMonth = [today dateByAddingTimeInterval: -5259487.66];

Se quiser o número exato de dias correto, dependendo do mês, você deve usar um NSCalendar.

Ben S
fonte
77
Constantes fixas para o tempo são BAD BAD BAD. E quanto aos dias bissextos ou segundos bissextos? Use NSDateComponents ou você IRÁ sofrer alguns problemas muito desagradáveis ​​e difíceis de depurar quando seus carimbos de data começarem a divergir.
Kendall Helmstetter Gelner
11
Realmente não é tão ruim se você quer apenas aproximadamente o valor das postagens da semana passada ou algo assim. Especifiquei que o OP deve ser usado NSCalendarse a exatidão for necessária.
Ben S
2
Constantes fixas devem ser adequadas para a contagem de dias: todo dia corresponde a 86400 segundos. Os segundos bissextos simplesmente demoram o dobro - um dia com um segundo bissexto ainda tem 86400 'segundos'. Quanto aos meses ... sim, fique longe :)
Chris,
3
@Chris, exceto quando os dias não são 86400 segundos. E o horário de verão? Parece insignificante, mas isso pode realmente levar você a bugs extremamente desagradáveis ​​que surgem do nada quando chega o horário de verão e você não se lembra de levar isso em consideração.
jpswain
1
Se você quiser voltar, digamos 7 dias aproximadamente, você pode apenas subtrair o tempo, se quiser voltar para a 'segunda-feira' desta semana, use NSCalendar. Existem casos de uso para ambos os cenários.
Johnny Rockex
83

Pode ser uma maneira melhor de escrever isso, mas aqui o que eu descobri na sugestão de Ben's NSCalendar e trabalhando a partir daí para NSDateComponents

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond ) fromDate:[[NSDate alloc] init]];

[components setHour:-[components hour]];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
NSDate *today = [cal dateByAddingComponents:components toDate:[[NSDate alloc] init] options:0]; //This variable should now be pointing at a date object that is the start of today (midnight);

[components setHour:-24];
[components setMinute:0];
[components setSecond:0];
NSDate *yesterday = [cal dateByAddingComponents:components toDate: today options:0];

components = [cal components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:[[NSDate alloc] init]];

[components setDay:([components day] - ([components weekday] - 1))]; 
NSDate *thisWeek  = [cal dateFromComponents:components];

[components setDay:([components day] - 7)];
NSDate *lastWeek  = [cal dateFromComponents:components];

[components setDay:([components day] - ([components day] -1))]; 
NSDate *thisMonth = [cal dateFromComponents:components];

[components setMonth:([components month] - 1)]; 
NSDate *lastMonth = [cal dateFromComponents:components];

NSLog(@"today=%@",today);
NSLog(@"yesterday=%@",yesterday);
NSLog(@"thisWeek=%@",thisWeek);
NSLog(@"lastWeek=%@",lastWeek);
NSLog(@"thisMonth=%@",thisMonth);
NSLog(@"lastMonth=%@",lastMonth);
hasnat
fonte
13
Você está perdendo memória com esses fromDate:[[NSDate alloc] init]bits.
Shaggy Frog
21
@Shaggy Frog: Acho que ele está assumindo que o ARC está habilitado ...;)
orj
3
Este código não funciona corretamente. Não leva em consideração as frações de segundo que fazem parte da data de entrada. Ou seja, a data poderia ter 30,1234 segundos e os NSDateComponents apenas 30 como o valor de segundos, portanto, subtrair 30 segundos do NSDate deixa você com 0,1234 segundos após a meia-noite. Além disso, alguns dias têm mais (ou menos) de 24 horas (horário de verão), portanto, obter a data de ontem deve realmente usar [components setDay: -1] em vez de [components setHour: -24].
orj
5
@orj Para sua informação, ARC saiu anos depois que a resposta foi postada
Sapo Salsicha,
3
@orj Além disso, nas mudanças de horário de verão, subtrair as horas não resulta em meia-noite. -1. Este código não vale 35 votos positivos.
Nikolai Ruhe
40

É bom ter NSDateComponents hoje:

NSCalendar *cal = [NSCalendar currentCalendar];

NSDate *date = [NSDate date];
NSDateComponents *comps = [cal components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) 
                                                          fromDate:date];
NSDate *today = [cal dateFromComponents:comps];

Isso cria um NSDate com apenas ano, mês e data:

(gdb) po today
2010-06-22 00:00:00 +0200

Para obter ontem, etc., você pode calculá-lo usando NSDateComponents:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:today options:0];
Cerveja cristã
fonte
Você não precisa incluir o componente era?
Chris Página
Agradável. Usei isso para calcular uma data de 8 anos atrás e funciona bem. Só precisa consertar um vazamento aqui: [[NSDateComponents alloc] init];
Craig B
@CraigB obrigado. Não pretende ser um exemplo completo. Deixou de fora o óbvio gerenciamento de memória como um desafio para o leitor;)
Christian Beer
@ChrisPage o componente era? Pelo que?
Christian Beer de
1
@ChristianBeer Tenho certeza de que todos os componentes maiores do que a resolução desejada devem ser considerados. Eu acredito que (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)deveria ser (NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit).
Chris Página
19
+ (NSDate*)dateFor:(enum DateType)dateType {

    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDateComponents *comps =
    [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
                fromDate:[NSDate date]];

    if(dateType == DateYesterday) {

        comps.day--;
    }
    else if(dateType == DateThisWeek) {

        comps.weekday = 1;
    }
    else if(dateType == DateLastWeek) {

        comps.weekday = 1;
        comps.week--;
    }
    else if(dateType == DateThisMonth) {

        comps.day = 1;
    }
    else if(dateType == DateLastMonth) {

        comps.day = 1;
        comps.month--;
    }
    else if(dateType != DateToday)
        return nil;

    return [calendar dateFromComponents:comps];
}
Dustin
fonte
Você não precisa incluir o componente era?
Chris Página
Tive que remover NSDayCalendarUnit e adicionar NSWeekdayCalendarUnit ao calcular DateLastWeek.
jessecurry
A Apple mudou a definição de NSWeekdayCalendarUnit e NSDayCalendarUnit em torno do iOS 5.0. A nova maneira é usar NSWeekdayCalendarUnit. Mas em versões mais antigas, você deve usar NSDayCalendarUnit. É uma bagunça ...
Dustin
@Dustin Parece não funcionar, mesmo ao atualizar para novas unidades ... Por exemplo DateThisWeek, para definir o dia da semana não tem impacto sobre o NSDate ...
AnthoPak
11

Swift 4.2

let today = Date()
let yesterday = today.addingTimeInterval(-86400.0)
let thisWeek = today.addingTimeInterval(-604800.0)
let lastWeek = today.addingTimeInterval(-1209600.0)

let thisMonth = today.addingTimeInterval(-2629743.83)
let lastMonth = today.addingTimeInterval(-5259487.66)

// components of the date
let calendar = Calendar(identifier: Calendar.Identifier.gregorian)
let components = calendar.dateComponents([.year, .month, .day], from: today)
let (year, month, day) = (components.year, components.month, components.day)

Observe que os componentes de data são opcionais.

gokhanakkurt
fonte
1
@fengd bem, hoje, 27 de junho de 2016, à 1h da manhã será 28 de junho de 2016, e se eu subtrair 24 horas, a data será 27 de junho à 1h. Qual é o ponto que eu perdi?
gokhanakkurt
1
desculpe, @gokhanakkurt. Acho que houve alguns soluços em minha mente. sua resposta está correta
fengd
4

As outras respostas simplesmente não funcionaram para mim (talvez por causa do meu fuso horário). É assim que eu faço:

- (BOOL)isOnThisWeek:(NSDate *)dateToCompare
{
    NSCalendar * calendar = [NSCalendar currentCalendar];
    NSDate     * today    = [NSDate date];

    int todaysWeek        = [[calendar components: NSWeekCalendarUnit fromDate:today] week];
    int dateToCompareWeek = [[calendar components: NSWeekCalendarUnit fromDate:dateToCompare] week];

    int todaysYear         = [[calendar components:NSYearCalendarUnit fromDate:today] year];
    int dateToCompareYear  = [[calendar components:NSYearCalendarUnit fromDate:dateToCompare] year];

    if (todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear) {
        return YES;
    }

    return NO;
}
Lucas
fonte
4

Se estiver usando iOS 10+ ou MacOS 10.12+, você pode usar esses dois Calendarmétodos para fazer isso corretamente.

  • func date(byAdding component: Calendar.Component, value: Int, to date: Date, wrappingComponents: Bool = default) -> Date?( docs )
  • func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval?( docs )

Aqui está um exemplo de como usar esses métodos no Swift 3, junto com a saída de playgrounds em meu fuso horário.

let calendar = Calendar.current
let now = Date()
// => "Apr 28, 2017, 3:33 PM"

let yesterday = calendar.date(byAdding: .day, value: -1, to: now)
// => "Apr 29, 2017, 3:33 PM"
let yesterdayStartOfDay = calendar.startOfDay(for: yesterday!)
// => ""Apr 29, 2017, 12:00 AM"

let thisWeekInterval = calendar.dateInterval(of: .weekOfYear, for: now)
// => 2017-04-23 04:00:00 +0000 to 2017-04-30 04:00:00 +0000

let thisMonthInterval = calendar.dateInterval(of: .month, for: now)
// => 2017-04-01 04:00:00 +0000 to 2017-05-01 04:00:00 +0000

let aDateInLastWeek = calendar.date(byAdding: .weekOfYear, value: -1, to: now)
let lastWeekInterval = calendar.dateInterval(of: .weekOfYear, for: aDateInLastWeek!)
// => 2017-04-16 04:00:00 +0000 to 2017-04-23 04:00:00 +0000

let aDateInLastMonth = calendar.date(byAdding: .month, value: -1, to: now)
let lastMonthInterval = calendar.dateInterval(of: .weekOfYear, for: aDateInLastMonth!)
// => 2017-03-26 04:00:00 +0000 to 2017-04-02 04:00:00 +0000

Bônus: você pode usar o DateIntervals para testar se uma data se enquadra nesse intervalo. Continuando de cima:

thisWeekInterval!.contains(now)
// => true
lastMonthInterval!.contains(now)
// => false
yood
fonte
Por que yesterdaydiz 29 de abril e nowdiz 28 de abril?
Juan Boero
1
NSDate *today = [NSDate date]; // Today's date
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents =[gregorian componentsNSDayCalendarUnit | NSWeekdayCalendarUnit) fromDate:today];
NSInteger day = [weekdayComponents day];
Hayden
fonte
0

Eu realmente gosto do objeto THCalendarInfo contido neste projeto:

http://github.com/jaredholdcroft/kcalendar

Não consigo encontrar o original. Usando este objeto, você pode ir para um dia anterior, o início de uma semana, o início de um mês, obter o dia da semana, o dia do mês ... etc.

Kendall Helmstetter Gelner
fonte
0
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
Tischel
fonte
14
Porque não [NSDate date]?
Joshpaul
Como ambos são válidos para obter a data atual, ambos incluem a hora. Portanto, não é a solução correta para a questão!
Christian Beer de
0

Isto é para verificar se a data é este mês ou não

func isOnThisMonth(dateToCompare: NSDate) -> Bool {
    let calendar: NSCalendar = NSCalendar.currentCalendar()
    let today: NSDate = NSDate()
    let todaysWeek: Int = calendar.components(NSCalendarUnit.Month, fromDate: today).month
    let dateToCompareWeek: Int = calendar.components(.Month, fromDate: dateToCompare).month
    let todaysYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: today).year
    let dateToCompareYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: dateToCompare).year
    if todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear {
        return true
    }
    return false
}

E o segundo só muda o tipo de unidade de calendário para fracos como este

func isOnThisWeek(dateToCompare: NSDate) -> Bool {
    let calendar: NSCalendar = NSCalendar.currentCalendar()
    let today: NSDate = NSDate()
    let todaysWeek: Int = calendar.components(NSCalendarUnit.Weekday, fromDate: today).weekday
    let dateToCompareWeek: Int = calendar.components(.Weekday, fromDate: dateToCompare).weekday
    let todaysYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: today).year
    let dateToCompareYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: dateToCompare).year
    if todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear {
        return true
    }
    return false
}

Espero que isso seja útil para alguém, obrigado.

Ilesh P
fonte
0

Eu respondi uma pergunta semelhante e por isso aqui a minha resposta é melhor:

  • Swift 3 !
  • Utiliza "Yesterday" e "Today" de DateFormatter. Isso já foi traduzido pela Apple, o que economiza seu trabalho!
  • Usa a string de "1 semana" já traduzida de DateComponentsFormatter. (Novamente, menos trabalho para você, cortesia da Apple.) Tudo o que você precisa fazer é traduzir a string "% @ ago". 🙂
  • As outras respostas incorretamente calcular o tempo quando o dia muda de "hoje" para "ontem" para etc. constantes fixas são um grande não-não, porque razões . Além disso, as outras respostas usam a data / hora atual quando deveriam usar a data / hora do final do dia atual .
  • Usa autoupdatingCurrent para Calendar & Locale, que garante que seu aplicativo seja imediatamente atualizado com o calendário do usuário e as preferências de idioma em Settings.app
ChrisJF
fonte