2011-07-30 14 views
11

Parece que no puedo entender por qué el día no cambia cuando llego al 6 de noviembre de 2011. Todo lo que hago es repetir los días. Cualquier ayuda sería apreciada. Aquí está mi código:NSDateComponents - método del día que devuelve el día incorrecto

NSCalendar* calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
NSDateComponents* comp = [[NSDateComponents alloc] init]; 

[comp setDay:2]; 
[comp setMonth:11]; 
[comp setYear:2011]; 

NSDate* date = [calendar dateFromComponents:comp]; 

for (int i = 0; i < 7; i++) { 
    NSDate* d = [date dateByAddingTimeInterval:((3600 * 24) * i)]; 
    NSDateComponents* dComponents = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:d]; 

    int day = [dComponents day]; 
    NSLog(@"\nDate: %@\nDay: %i", [d description], day); 
} 

Este es mi resultado:

Date: 2011-11-02 06:00:00 +0000 
Day: 2 

Date: 2011-11-03 06:00:00 +0000 
Day: 3 

Date: 2011-11-04 06:00:00 +0000 
Day: 4 

Date: 2011-11-05 06:00:00 +0000 
Day: 5 

Date: 2011-11-06 06:00:00 +0000 
Day: 6 

Date: 2011-11-07 06:00:00 +0000 
Day: 6 

Date: 2011-11-08 06:00:00 +0000 
Day: 7 

Gracias!

+0

FYI una copia y pega la versión de su código en mi máquina muestra el resultado esperado (es decir días 2-8). Tal vez su computadora sabe algo que nosotros no? =) – Rog

+0

@Rog ¿Ustedes tienen Horario de verano bajo? :) –

Respuesta

20

Noviembre, 6 es un día cuando el tiempo cambió debido a la hora de guardado, por lo que ese día realmente dura 25 horas y el resultado incorrecto probablemente proviene de eso (en general, agregar intervalos de tiempo a una fecha no es confiable debido a irregularidades el comportamiento puede depender de muchos parámetros: calendario actual, zona horaria, configuración regional, etc.).

La forma más correcta para iterar a través de días (por vídeo wwdc11 "Performing Calendar Calculations" (enlace iTunes)) es añadir un número apropiado de componentes de la fecha a una fecha de inicio en cada iteración:

... 
NSDateComponents *addComponents = [[NSDateComponents alloc] init]; 
for (int i = 0; i < 7; i++) { 
    [addComponents setDay: i]; 
    NSDate* d = [calendar dateByAddingComponents:addComponents toDate:date options:0];   
    NSDateComponents* dComponents = [calendar components:(NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) fromDate:d]; 

    int day = [dComponents day]; 
    NSLog(@"\nDate: %@\nDay: %i", [d description], day); 
} 
+7

+1 Absolutamente correcto. Esto también se conoce como el error No todos los días tiene 86.400 segundos en el error –

32

Bienvenido al maravilloso mundo de cálculos de fecha!

@Vladimir es correcto. El 6 de noviembre tiene ~ 90,000 segundos porque ese es el día (en los EE. UU. En el calendario gregoriano) que el horario de verano entra en vigencia. Acepta su respuesta; es el correcto.

Este uno es para educar a usted un poco más, porque este es un tema difícil.

Un NSDate representa un punto absoluto en el tiempo. Este punto es inmutable. Existirá independientemente de si los humanos y sus medidas de tiempo existen.

Un "día" es un valor relativo. Es relativo a muchas cosas, como:

  1. El planeta tierra. Un día marciano no es lo mismo que un día de la tierra
  2. El calendario de la persona que realiza el cálculo. No todos los calendarios miden el tiempo de la misma manera.

lo tanto, es imposible de tomar un valor relativo y añadirlo a un valor absoluto sin más contexto. No tiene idea de cuál es el valor relativo relativo a. Usted asume que todos están utilizando el mismo punto de referencia que usted, y esa suposición es incorrecta. Por ejemplo:

  • El año actual es 2011. ¿Pero es así? "2011" es relativo al calendario gregoriano. ¿Qué pasa si no estás usando el calendario gregoriano?
  • El año actual es 23. Pero eso es solo si estás usando el calendario japonés y específicamente usando los nombres de la era japonesa.
  • El año actual es 5771. Pero eso solo si usa el calendario hebreo. Y la facturación para el próximo año (5772) no es la misma facturación que el próximo año gregoriano.
  • El año actual es (probablemente) 1432.Si eres musulmán
  • El año actual es 4344, si eres coreano.

Y así sucesivamente.

Como regla general, la mayoría de los calendarios tienen base solar, lo que significa que una órbita alrededor del sol equivale a un "año" en ese calendario. Pero esto hace que las cosas sean REALMENTE complicadas, porque el número de rotaciones que hace la Tierra (un "día") en su órbita alrededor del Sol no es un número integral (completo). Es algo así como 365.24 rotaciones. ¡Ingresa los días bisiestos! ¡Pero solo en ciertos calendarios! En otros calendarios, ¡tenemos meses bisiestos! O un salto de segundos. O un salto de horas. O saltar-lo que sea que sea el tiempo-unidad-lo que quieras. Y ni siquiera me inicie en las zonas horarias.

Entonces, ¿cómo lidiar con esto?

La respuesta simple: NO HACE. Personas mucho más inteligentes que tú o yo ya hemos escrito el código para manejar todo esto por nosotros.

mira:

Un NSCalendar encapsula todas las reglas y regulaciones para calcular el tiempo (en la Tierra) de acuerdo con ese sistema de medición. NSDateComponents encapsula la naturaleza relativa de del tiempo de medición.

Tiene un punto absoluto en el tiempo (un NSDate) y desea saber a qué día de la semana corresponde. Esto es lo que debe hacer:

  • Obtenga el objeto de calendario adecuado. Para casi todo lo que harás, probablemente sea [NSCalendar currentCalendar].
  • Usando el calendario, se dice "quiero tales y tales 'componentes de la fecha' de este punto absoluto en el tiempo"

del calendario va a batir y quemar y le devolverá una NSDateComponents que significan lo información que estás buscando.

O digamos que tiene un punto absoluto en el tiempo y desea saber "¿cuál es el punto absoluto en el tiempo 1 semana después?". ¿Quién sabe cuánto dura una semana? Yo no. Es generalmente 7 días, pero no tiene que ser así. Eso es exactamente con lo que estoy familiarizado. Pero estoy seguro de que hay calendarios que se basan en semanas que no son de 7 días. Entonces crea un objeto NSDateComponents, lo ataca para significar "1 semana" y dice "calendario: tengo esta fecha y esta cantidad relativa. Añádalos y dígame la nueva fecha" y lo hace.

< actualización >

Sin embargo, la adición de componentes de la fecha de fechas puede también ser problemático. Por ejemplo, ¿qué pasa si quiere encontrar el último día de cada mes de este año? (Asumiremos el calendario gregoriano aquí) Entonces comienza con un NSDate que coincide con el 31 de enero y le agrega "1 mes". ¿Qué obtienes? 28 de febrero! Entonces, agrega un mes a que. ¿Qué obtienes? 28 de marzo! ¡Ya no es el final del mes!

La forma de corregir que es calcular siempre la nueva fecha a partir de la fecha original. Entonces, en lugar de agregar 1 mes a cada fecha sucesiva, agrega "n" meses a la fecha original. Y eso funcionará correctamente.

</actualización >

Se puede ver cómo este es un tema difícil. En caso de que no tiene, esto es una última pista:

ESTE ES un tema difícil

Cuanto antes se aprende a hacer las cosas correctamente, más feliz y su código será. :)

+0

gracias por la explicación detallada y prometo que nunca maldeciré a los ingenieros de Apple la próxima vez que estoy trabajando con los cálculos de fecha :-) – Rog