2013-02-18 6 views
8

Sto usando il NSCalendar seguente metodo per ottenere il numero di giorni in un anno solare:Objective-C: Ottenere il numero di giorni in un anno civile

NSRange range = [gregorian rangeOfUnit:NSDayCalendarUnit 
           inUnit:NSYearCalendarUnit 
           forDate:date]; 

mi aspetto un valore range.length ritorno di tipo NSRange che è 365 o 366 (giorni).

Tuttavia, questo codice restituisce un range.length di 31 per un valore di data 2005-06-06.

Cosa c'è di sbagliato nel mio codice?

Questa è la piena frammento di codice:

NSCalendar *gregorian = [[NSCalendar alloc] 
initWithCalendarIdentifier:NSGregorianCalendar]; 

[subArray enumerateObjectsUsingBlock: 
    ^(NSDate *date, NSUInteger idx, BOOL *stop) { 
    NSUInteger numberOfDays = [gregorian ordinalityOfUnit:NSDayCalendarUnit 
    inUnit:NSYearCalendarUnit forDate:date]; 
}]; 
+0

mostra il tuo codice completo. –

+0

Otterrai sempre 31 per uno qualsiasi del mese. –

+0

Sì, ho provato lo stesso con qualsiasi anno, ogni mese mostrava sempre 31. Una volta cambiato NSMonthCalenderUnit il conteggio dei giorni era corretto !!! O ci manca qualcosa o questa API ha alcuni bug. –

risposta

2

Questo calcola il numero di giorni di un anno di una certa data:

NSDate *someDate = [NSDate date]; 

NSDate *beginningOfYear; 
NSTimeInterval lengthOfYear; 
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
[gregorian rangeOfUnit:NSYearCalendarUnit 
      startDate:&beginningOfYear 
       interval:&lengthOfYear 
       forDate:someDate]; 
NSDate *nextYear = [beginningOfYear dateByAddingTimeInterval:lengthOfYear]; 
NSInteger startDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit 
              inUnit:NSEraCalendarUnit 
             forDate:beginningOfYear]; 
NSInteger endDay = [gregorian ordinalityOfUnit:NSDayCalendarUnit 
             inUnit:NSEraCalendarUnit 
             forDate:nextYear]; 
NSInteger daysInYear = endDay - startDay; 

Sad avvertimento: questo non funziona in modo corretto per l'anno 1582.

L'anno 1582, l'anno in cui Gregor ha introdotto il calendario attualmente diffuso, aveva bisogno di una correzione per allineare il solare con gli anni del calendario. Così sono andati con la soluzione pragmatica: sono appena caduti dal 5 al 14 ottobre. (Non erano abbastanza pazzi da cambiare i giorni della settimana, anche). Di conseguenza, l'anno 1582 ha solo 355 giorni.

Addendum: Il codice sopra funziona correttamente solo per anni dopo il 1582. Ritorna 365 giorni all'anno 1500, ad esempio, anche se quest'anno era un anno bisestile nel calendario julian usato allora. Il calendario gregoriano inizia al 15 ottobre 1582. I calcoli fatti sul calendario gregoriano non sono definiti prima di quella data. Quindi in questo modo l'implementazione di Apple è corretta.Non sono a conoscenza di una corretta implementazione per anni prima del 1583 su Cocoa.

+0

Solo per curiosità, l'implementazione gregoriana di Cocoa spiega anche i capricci della graduale adozione da parte del paese del calendario? Ad esempio, la Francia era due mesi indietro rispetto all'Italia e alla Spagna in adozione, e la Svezia ha fatto qualcosa come saltare giorni bisestili per un secolo piuttosto che saltare una settimana in una volta. –

+0

@JoshCaswell Per quanto posso dire questo non è nemmeno modellato: non è possibile chiedere un calendario per una regione o una data specifica. Quindi immagino che il calendario gregoriano funzioni come è. Puoi chiedere un anno bisestile prima che venga definito e risponderebbe come se il calendario gregoriano fosse in vigore in quel momento. –

+2

È * interamente * consistente: utilizza il "calendario proleptico gregoriano", che significa in gran parte "rietichettare i giorni a ritroso, usando le stesse regole". Quindi il 1500 non è un anno bisestile ma il 1200 è (insieme a 0000, e per favore non iniziare il dibattito sull'anno 0). Bisogna fare attenzione per evitare interpretazioni errate, ma è ciò che ISO 8601 specifica, ciò che gli astronomi usano e ciò che gli storici convertono per rendere i calcoli più facili (spesso con il suffisso "NS"). –

0

si stanno ottenendo il numero di giorni nel mese di data.

È possibile utilizzare la stessa funzione e passare una data con febbraio come mese. Se range.length restituisce 28, i giorni totali dell'anno saranno 365, se restituisce 29, quindi il numero di giorni sarà 366.

+4

Ottiene 31 giorni, il 30 giugno. – vikingosegundo

+0

Sta ricevendo i giorni del sesto mese, che è giugno (e giugno ha 30 giorni) –

+0

Mi viene restituito il 31 per ogni mese (incluso settembre). Perché posso specificare 'NSYearCalendarUnit' come parametro' inUnit' se il metodo restituisce sempre il tipo di data successivo (più grande) (mese in questo caso)? – AlexR

1

Che dire di trovare il numero di giorni in un dato anno come: Sebbene questo sia non la soluzione relativa per la domanda.

-(NSInteger)daysBetweenTwoDates:(NSDate *)fromDateTime andDate:(NSDate *)toDateTime{ 

    NSDate *fromDate; 
    NSDate *toDate; 

    NSCalendar *calendar = [NSCalendar currentCalendar]; 

    [calendar rangeOfUnit:NSDayCalendarUnit startDate:&fromDate interval:NULL forDate:fromDateTime]; 
    [calendar rangeOfUnit:NSDayCalendarUnit startDate:&toDate interval:NULL forDate:toDateTime]; 

    NSDateComponents *difference = [calendar components:NSDayCalendarUnit fromDate:fromDate toDate:toDate options:0]; 

    return [difference day]; 
} 

-(void)someMethod { 

    NSString *[email protected]"2012"; 

    NSDate *date1 = [NSDate dateWithString:[NSString stringWithFormat:@"%@-01-01 00:00:00 +0000",yourYear]]; 
    NSDate *date2 = [NSDate dateWithString:[NSString stringWithFormat:@"%@-12-31 00:00:00 +0000",yourYear]]; 

    NSInteger numberOfDays=[self daysBetweenTwoDates:date1 andDate:date2];  

    NSLog(@"There are %ld days in between the two dates.", numberOfDays); 
}    

Edit:

Come dateWithString: è deprecato, è possibile utilizzare

NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init]; 
[dateFormatter dateFormat][email protected]"dd-MM-yyyy"; 
NSDate *date=[dateFormatter dateFromString:dateString]; 

per formare date1 e date2

+0

Il codice per '-daysBetweenTwoDates: andDate' funziona effettivamente se le date sono in diversi mesi? – paulmelnikow

+1

sì, anche per anni diversi. questo è testato e già parte di qualche altra risposta da parte mia ... mesi fa –

+0

'dateWithString:' è deprecato. –

1

Ecco una versione relativamente pulito. È una categoria su NSCalendar.

- (NSInteger) daysInYear:(NSInteger) year { 
    NSDateComponents *dateComponents = [[NSDateComponents alloc] init]; 
    dateComponents.year = year; 
    dateComponents.month = dateComponents.day = 1; 
    NSDate *firstOfYear = [self dateFromComponents:dateComponents]; 
    dateComponents.year = year + 1; 
    NSDate *firstOfFollowingYear = [self dateFromComponents:dateComponents]; 

    return [[self components:NSDayCalendarUnit 
        fromDate:firstOfYear 
        toDate:firstOfFollowingYear 
        options:0] day];  
} 

Grazie a AKV per aver ricordato che -components:fromDate:toDate:options funzionerà in questo caso.

Questo non sembra funzionare per 1582 o precedente, che per risposta di Nikolai Ruhe è perché i calcoli del calendario gregoriano semplicemente non sono definiti prima di allora.

Problemi correlati