2010-01-16 14 views
5

? ¿Cómo puedo calcular el número de días en un año para cualquier calendario, no solo gregoriano? He intentado esto¿Cómo puedo calcular el número de días de este año en el Objetivo C

NSUInteger *days = [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:[NSDate date]];

pero eso me da el número de días del mes en curso en lugar del número de días en el año en curso.

+2

No es en realidad le da el número de días en el mes actual. En realidad, le proporciona el rango posible de valores para los días del año con 1 a 31. No hay fecha que podría tener un número de día de 32. –

Respuesta

10

Finalmente encontré una solución que funciona. Lo que hago primero es calcular la cantidad de meses del año y luego, para cada mes, calcular la cantidad de días para ese mes.

El código es el siguiente:

NSUInteger days = 0; 
NSCalendar *calendar = [NSCalendar currentCalendar]; 
NSDate *today = [NSDate date]; 
NSDateComponents *components = [calendar components:NSYearCalendarUnit fromDate:today]; 
NSUInteger months = [calendar rangeOfUnit:NSMonthCalendarUnit 
            inUnit:NSYearCalendarUnit 
            forDate:today].length; 
for (int i = 1; i <= months; i++) { 
    components.month = i; 
    NSDate *month = [calendar dateFromComponents:components]; 
    days += [calendar rangeOfUnit:NSDayCalendarUnit 
          inUnit:NSMonthCalendarUnit 
          forDate:month].length; 
} 

return days; 

No es tan limpio como me hubiera esperado, sino que funcionará para cualquier calendario gregoriano como el ordinario o el islámico.

3

No sé objetivo-c, pero es un algoritmo simple para determinar si es un año bisiesto.

if (year % 400 == 0) 
    then 366 // Leap Year 
else if (year % 100 == 0) 
    then 365 // Non-Leap Year 
else if (year % 4 == 0) 
    then 366 // Leap Year 
else 
    365 // Non-Leap Year 

O si desea la versión un poco menos prolijo

if ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)) 
    then 366 // Leap Year 
else 
    365 // Non-Leap Year 
+5

Tenga en cuenta que este algoritmo se romperá durante años antes de 1582, antes de saltar años donde se introdujo ;-) – notnoop

+0

Esto es cierto. Debería agregar un cheque para asegurarse de que el año sea posterior a 1582. Si necesita tomar eso en cuenta. –

+5

Esto no es realmente lo que estaba pidiendo, porque solo funcionará para el calendario gregoriano. – Godisemo

1

Tal vez se puede utilizar el selector components:fromDate:toDate:options: que está destinado a (y cito official Apple docs): devoluciones, como un NSDateComponents objeto utilizando especifican componentes, la diferencia entre dos fechas proporcionadas.?

Lea también this post que aclara el comportamiento que está viendo.

+0

Esto podría funcionar, pero ¿cómo debo describir la fecha "último día del año" – Godisemo

+0

http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/DatesAndTimes/Articles/dtCalendars.html Vea el ejemplo al pie de esa página, si trabaja con los NSDateComponents que podrían funcionar para todos los calendarios. –

+0

Pensé en esto y no creo que funcione porque me llevará de vuelta al punto de partida y volveré a tener el mismo problema. – Godisemo

1

Para hacer lo que necesita, tiene que encontrar el rango de días en un mes y meses en un año para un calendario específico. Esta función hará lo que usted necesita para cualquier calendario que utiliza unidades de día/mes/año (o tiene algún equivalente asignada):

NSInteger getDaysInYear(NSDate* date) 
{ 
    // Get the current calendar 
    NSCalendar* c = [NSCalendar currentCalendar]; 

    // Find the range for days and months in this calendar 
    NSRange dayRange = [c rangeOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:date]; 
    NSRange monthRange = [c rangeOfUnit:NSMonthCalendarUnit inUnit:NSYearCalendarUnit forDate:date]; 

    // Get the year from the suppled date 
    NSDateComponents* yearComps = [c components:NSYearCalendarUnit fromDate:date]; 
    NSInteger thisYear = [yearComps year]; 

    // Create the first day of the year in the current calendar 
    NSUInteger firstDay = dayRange.location; 
    NSUInteger firstMonth = monthRange.location; 
    NSDateComponents* firstDayComps = [[[NSDateComponents alloc] init] autorelease]; 
    [firstDayComps setDay:firstDay]; 
    [firstDayComps setMonth:firstMonth]; 
    [firstDayComps setYear:thisYear]; 
    NSDate* firstDayDate = [c dateFromComponents:firstDayComps]; 

    // Create the last day of the year in the current calendar 
    NSUInteger lastDay = dayRange.length; 
    NSUInteger lastMonth = monthRange.length; 
    NSDateComponents* lastDayComps = [[[NSDateComponents alloc] init] autorelease]; 
    [lastDayComps setDay:lastDay]; 
    [lastDayComps setMonth:lastMonth]; 
    [lastDayComps setYear:thisYear]; 
    NSDate* lastDayDate = [c dateFromComponents:lastDayComps]; 

    // Find the difference in days between the first and last days of the year 
    NSDateComponents* diffComps = [c components:NSDayCalendarUnit 
             fromDate:firstDayDate 
             toDate:lastDayDate 
             options:0]; 

    // We have to add one since this was subtraction but we really want to 
    // give the total days in the year 
    return [diffComps day] + 1; 
} 

Si desea especificar este año, se le puede llamar simplemente como getDaysInYear([NSDate date]);, o podría crear una fecha a partir de un año específico/otros componentes y pasar eso. También podría volver a implementarlo fácilmente como una llamada a método.

-2
[[NSCalendar currentCalendar] ordinalityOfUnit:NSYearCalendarUnit 
             inUnit:NSEraCalendarUnit forDate:date]; 
-1

Aquí está la respuesta proporcionada por William Clemens pero en C Objetivo:

    int year; 
        int yearCode; 

        if (year % 400 == 0) 
         {yearCode = 366;} // Leap Year 
        else if (year % 100 == 0) 
        { 
         {yearCode = 365;} // Non Leap Year 
        } 
        else if (year % 4 == 0) 
        { 
         {yearCode = 366;} // Leap Year 
        } 
        else 
        {yearCode = 365;} // Non-Leap Year 
8

En general se puede usar el número de días entre el 1 día del año y el día 1 del próximo año. Aquí está el código que ilustra este enfoque

- (NSDate *)firstDateOfYear:(NSInteger)year 
{ 
    NSDateComponents *dc = [[NSDateComponents alloc] init]; 
    dc.year = year; 
    dc.month = 1; 
    dc.day = 1; 
    return [[NSCalendar currentCalendar] dateFromComponents:dc]; 
} 

- (NSUInteger)numberOfDaysInYear:(NSInteger)year 
{ 
    NSDate *firstDateOfYear = [self firstDateOfYear:year]; 
    NSDate *firstDateOfNextYear = [self firstDateOfYear:year + 1]; 
    NSDateComponents *components = [[NSCalendar currentCalendar] components:NSDayCalendarUnit fromDate:firstDateOfYear toDate:firstDateOfNextYear options:0]; 
    return [components day]; 
} 
2

No sé si alguien todavía está interesada en este tema, pero es otra posible solución:

NSCalendar *calendar = [NSCalendar currentCalendar]; 
NSDate *startOfYear; 
NSTimeInterval lengthOfYear; 
[calendar rangeOfUnit:NSYearCalendarUnit 
      startDate:&startOfYear 
      interval:&lengthOfYear 
       forDate:[NSDate date]]; 
NSDate *endOfYear = [startOfYear dateByAddingTimeInterval:lengthOfYear]; 
NSDateComponents *comp = [calendar components:NSDayCalendarUnit 
            fromDate:startOfYear 
             toDate:endOfYear 
             options:0]; 
NSUInteger numberOfDaysInThisYear = [comp day]; 

usando sólo 2 llamadas a NSCalendar.

+0

hey. ¿Podría esto hacer que su solución sea aún mejor si simplemente dividimos la longitud del año por la cantidad de segundos en un día? – Olex

+0

@Olex: ¿Cómo calcularías la "duración del año"? Además, el número o segundos en un día no es un valor fijo. En regiones con horario de verano, un día puede tener 23 o 25 horas, cuando el horario de verano comienza o termina. Además, la pregunta pregunta por el número de días en * cualquier * calendario, no solo el gregoriano. Entonces, el número de días en un año puede ser diferente de 365. –

+0

Martin, ya calcula la duración del año (que está en segundos). Y el número de segundos en un día es 24 * 60 * 60. El horario de verano no afectará este enfoque, ya que 23 + 25 = 48 = 24 + 24. Otros calendarios también deberían funcionar porque calcula la duración del año en un calendario específico . Sin embargo, puede ser necesario redondear al entero más cercano. – Olex

0

Guau, esas son soluciones involucradas. Simplemente cree una fecha del 29 de febrero del año en curso y vea si se convierte primero en marcha o no.

-(BOOL)isLeapYear { 
    NSDateComponents *components = [[NSDate currentCalendar] components:componentFlags fromDate:self]; 
    components.month = 2; 
    components.day = 29; 

    NSDate *date = [[NSDate currentCalendar] dateFromComponents:components]; 
    return (date.month == 2 && date.day == 29); 
} 

** Nota, algunos de estos artículos como date.month y date.day relé en otros métodos que no se muestra, pero usted consigue la idea.

+0

Esto sería específico del calendario gregoriano. –

0

Otro ejemplo en Swift 3:

let calendar = Calendar.current 
let date = Date() // use any date in this year 
if let yearInterval = calendar.dateInterval(of: Calendar.Component.year, for: date), 
    let daysInYear = calendar.dateComponents([Calendar.Component.day], from: yearInterval.start, to: yearInterval.end).day 
{ 
    print(daysInYear) 
} 

En Objective-C:

NSCalendar *calendar = [NSCalendar currentCalendar]; 
NSDate* date = [NSDate date]; 
NSDate* startOfYear = nil; 
NSTimeInterval yearDuration = 0; 
if ([calendar rangeOfUnit:NSCalendarUnitYear startDate:&startOfYear interval:&yearDuration forDate:d 
]) { 
    NSDateComponents *daysComponents = [calendar components:NSCalendarUnitDay fromDate:startOfYear toDate:[startOfYear dateByAddingTimeInterval:yearDuration] options:0]; 
    NSInteger daysInYear = daysComponents.day; 
    NSLog(@"Days in year: %ld", daysInYear); 
} 
0

Swift 3 extensión:

extension Date { 
    public func isLeapYear() -> Bool { 
     let components = Calendar.current.dateComponents([.year], from: self) 

     guard let year = components.year else { return false } 

     return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) 
    } 
} 
Cuestiones relacionadas