obtener NSDate hoy, ayer, esta semana, la semana pasada, este mes, el mes pasado ... variables

86

Lo que estoy tratando de hacer es obtener las variables NSDate de hoy, ayer, esta semana, la semana pasada, este mes, el mes pasado listas para la comparación de encabezados que se agregarán en el título de UITableView's titleForHeaderInSection

Lo que quiero se hace manualmente en el siguiente código para la fecha 2009-12-11

 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
fuente

Respuestas:

93

Adaptado de la Guía de programación de fecha y 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];

Si desea el número exacto correcto de días según el mes, debe usar un NSCalendar.

Ben S
fuente
77
Las constantes fijas de tiempo son MALAS MALAS MALAS. ¿Qué pasa con los días bisiestos o los segundos bisiestos? Use NSDateComponents o sufrirá algunos problemas muy desagradables y difíciles de depurar cuando sus sellos de fecha comiencen a no coincidir.
Kendall Helmstetter Gelner
11
Realmente no es tan malo si solo quieres aproximadamente el valor de las publicaciones de la última semana o lo que sea. Especifiqué que el OP debería usarse NSCalendarsi se requería exactitud.
Ben S
2
Las constantes fijas deberían estar bien para contar días: cada día son 86400 segundos. Los segundos intercalares simplemente toman el doble de tiempo: un día con un segundo intercalar todavía tiene 86400 'segundos'. En cuanto a meses ... sí, mantente alejado :)
Chris
3
@Chris excepto cuando los días no son 86400 segundos. ¿Qué pasa con el horario de verano? Suena insignificante, pero esto en realidad puede llevarte a errores súper retorcidos que surgen de la nada cuando llega el horario de verano y no te acuerdas de tenerlo en cuenta.
jpswain
1
Si solo desea regresar, digamos 7 días aproximadamente, puede restar el tiempo, si desea regresar al 'lunes' de esta semana, use NSCalendar. Hay casos de uso para ambos escenarios.
Johnny Rockex
83

Podría ser una mejor manera de escribir esto, pero aquí se me ocurrió la sugerencia de NSCalendar de Ben y trabajando desde allí hasta 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
fuente
13
Estás perdiendo memoria por todas partes con esos fromDate:[[NSDate alloc] init]bits.
Shaggy Frog
21
@Shaggy Frog: Supongo que está asumiendo que ARC está habilitado ...;)
orj
3
Este código no funciona correctamente. No tiene en cuenta las fracciones de segundo que forman parte de la fecha de entrada. Es decir, la fecha podría tener 30,1234 segundos y NSDateComponents solo tiene 30 como valor de segundos, por lo que restar 30 segundos de NSDate lo deja con 0,1234 segundos después de la medianoche. Además, algunos días tienen más (o menos) de 24 horas (horario de verano), por lo que para obtener la fecha de ayer debería usarse [components setDay: -1] en lugar de [components setHour: -24].
orj
5
@orj FYI, ARC salió años después de que se publicara esa respuesta
Shaggy Frog
3
@orj Además, en los cambios de horario de verano, restar las horas no te da medianoche. -1. Este código no vale 35 votos a favor.
Nikolai Ruhe
40

Es bueno obtener NSDateComponents hoy:

NSCalendar *cal = [NSCalendar currentCalendar];

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

Esto crea un NSDate con solo año, mes y fecha:

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

Para obtener ayer, etc., puede calcularlo usando NSDateComponents:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:today options:0];
Cerveza cristiana
fuente
¿No necesitas incluir el componente era?
Chris Page
Agradable. Usé esto para calcular una fecha de hace 8 años y funciona muy bien. Solo necesito arreglar una fuga aquí: [[NSDateComponents alloc] init];
Craig B
@CraigB gracias. No pretende ser un ejemplo completo. Dejó de lado la gestión obvia de la memoria como un desafío para el lector;)
Christian Beer
@ChrisPage ¿el componente de la era? ¿Para qué?
Christian Beer
1
@ChristianBeer Estoy bastante seguro de que se deben considerar todos los componentes más grandes que la resolución deseada. Creo que (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)debería serlo (NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit).
Chris Page
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
fuente
¿No necesitas incluir el componente era?
Chris Page
Tuve que eliminar NSDayCalendarUnit y agregar NSWeekdayCalendarUnit al calcular DateLastWeek.
jessecurry
Apple cambió la definición de NSWeekdayCalendarUnit y NSDayCalendarUnit alrededor de iOS 5.0. La nueva forma es utilizar NSWeekdayCalendarUnit. Pero en versiones anteriores debe usar NSDayCalendarUnit. Es un desastre ...
Dustin
@Dustin No parece funcionar, incluso cuando se actualiza a nuevas unidades ... Por ejemplo DateThisWeek, la configuración del día de la semana no tiene impacto en NSDate ...
AnthoPak
11

Rápido 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)

Tenga en cuenta que los componentes de la fecha son opcionales.

gokhanakkurt
fuente
1
@fengd bueno, hoy 27 de junio de 2016, a la 1 a.m., la fecha será el 28 de junio de 2016, y si resto 24 horas, la fecha será el 27 de junio a la 1 a.m. ¿Cuál es el punto que me perdí?
gokhanakkurt
1
lo siento, @gokhanakkurt. Creo que hubo algunos contratiempos en mi mente. su respuesta es correcta
fengd
4

Las otras respuestas simplemente no funcionaron para mí (tal vez debido a mi zona horaria). Así es como lo hago:

- (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
fuente
4

Si está utilizando iOS 10+ o MacOS 10.12+, puede utilizar estos dos Calendarmétodos para hacerlo correctamente.

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

Aquí hay un ejemplo de cómo usar estos métodos en Swift 3, junto con la salida de parques infantiles en mi zona horaria.

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

Bonificación: puede usar DateIntervals para probar si una fecha cae en ese rango. Continuando desde arriba:

thisWeekInterval!.contains(now)
// => true
lastMonthInterval!.contains(now)
// => false
yood
fuente
¿Por qué yesterdaydice 29 de abril y now28 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
fuente
0

Realmente me gusta el objeto THCalendarInfo contenido en este proyecto:

http://github.com/jaredholdcroft/kcalendar

No puedo encontrar el original. Con este objeto puede pasar a un día anterior, al comienzo de una semana, al comienzo de un mes, obtener el día de una semana, el día de un mes ... etc.etc.

Kendall Helmstetter Gelner
fuente
0
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
Tischel
fuente
14
¿Por qué no [NSDate date]?
joshpaul
Dado que ambos son válidos para obtener la fecha actual, ambos incluyen la hora. ¡Entonces no es la solución correcta para la pregunta!
Christian Beer
0

Esto es para comprobar si la fecha es este mes o no

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
}

Y el segundo solo cambia el tipo de calendarioUnidad para débiles 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 esto sea de ayuda para alguien Gracias.

Ilesh P
fuente
0

Respondí a una pregunta similar ya y he aquí por qué mi respuesta es mejor:

  • Swift 3 !
  • Utiliza "Yesterday" y "Today" de DateFormatter. ¡Esto ya está traducido por Apple, lo que le ahorra trabajo!
  • Utiliza la cadena "1 semana" ya traducida de DateComponentsFormatter. (De nuevo, menos trabajo para ti, cortesía de Apple). Todo lo que tienes que hacer es traducir la cadena "% @ ago". 🙂
  • Las otras respuestas calculan incorrectamente la hora en la que el día cambia de "hoy" a "ayer", etc. Las constantes fijas son un gran NO-NO por razones . Además, las otras respuestas usan la fecha / hora actual cuando deberían usar el final de la fecha / hora del día actual .
  • Utiliza autoupdatingCurrent para Calendar & Locale, lo que garantiza que su aplicación esté actualizada inmediatamente con el calendario del usuario y las preferencias de idioma en Settings.app
ChrisJF
fuente