2013-02-18 10 views
8

Używam następujący NSCalendar metodę, aby uzyskać liczbę dni w danym roku kalendarzowym:Objective-C: Pierwsze liczbę dni w roku kalendarzowym

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

Oczekuję wartość range.length powrotną typu NSRange czyli 365 lub 366 (dni).

Jednak ten kod zwraca range.length z 31 dla wartości daty o wartości 2005-06-06.

Co jest nie tak z moim kodem?

Jest to pełna fragment kodu:

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

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

pokazać pełny kod. –

+0

Zawsze otrzymasz 31 na dowolny miesiąc. –

+0

Tak, wypróbowałem to samo z każdym rokiem, w każdym miesiącu zawsze pokazywało 31. Kiedy zmieniłem NSMonthCalenderUnit liczba dni była poprawna. !!! Albo coś nam brakuje, albo ten interfejs API ma kilka błędów. –

Odpowiedz

2

ten oblicza liczbę dni w roku na dany dzień:

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 zastrzeżenie: to nie działa poprawnie na rok 1582

W roku 1582 roku, kiedy Gregor wprowadził szeroko rozpowszechniony kalendarz, potrzebował poprawki, aby wyrównać energię słoneczną do lat kalendarzowych. Więc poszli z pragmatycznym rozwiązaniem: po prostu zrzucili 5-14 października. (Nie byli na tyle szaleni, aby zmienić dni powszednie). W rezultacie rok 1582 ma tylko 355 dni.

Uzupełnienie: Powyższy kod działa tylko poprawnie przez wiele lat po roku 1582. Zwraca ona przez 365 dni w roku 1500, na przykład, choć ten rok był rokiem przestępnym w następnie wykorzystywane kalendarza juliańskiego. Kalendarz gregoriański rozpoczyna się 15 października 1582 r. Obliczenia dokonane w kalendarzu gregoriańskim nie zostały zdefiniowane przed tą datą. W ten sposób wdrożenie Apple jest poprawne.Nie jestem świadomy prawidłowej implementacji przez lata przed 1583 rokiem na Cocoa.

+0

Po prostu z ciekawości, czy gregoryjska implementacja Cocoa również uwzględnia kaprysy stopniowego przyjmowania kalendarza według krajów? Na przykład Francja była dwa miesiące w tyle za Włochami i Hiszpanią w okresie adopcji, a Szwecja zrobiła coś takiego, jak przeskoczyć dni przeskoczyć przez sto lat, zamiast przeskakiwać od tygodnia. –

+0

@JoshCaswell O ile mogę powiedzieć, nie jest to nawet modelowane: Nie można poprosić o kalendarz dla określonego regionu lub daty. Zgaduję więc, że kalendarz gregoriański działa tak jak jest. Możesz go poprosić o rok przestępny, zanim zostanie on zdefiniowany i odpowie, jakby kalendarz gregoriański działał w tym czasie. –

+2

Jest * całkowicie * spójny: używa "prostego kalendarza gregoriańskiego", co w przybliżeniu oznacza "relabel dni pracy wstecz, używając tych samych zasad". Zatem 1500 nie jest rokiem przestępnym, ale 1200 jest (wraz z 0000 i proszę nie rozpoczynać debaty w roku 0). Należy zachować ostrożność, aby uniknąć błędnej interpretacji, ale to właśnie określa ISO 8601, co astronomowie używają i na co historycy się przekonują, aby ułatwić obliczenia (często z przyrostkiem "NS"). –

0

otrzymujesz liczbę dni w miesiącu datą.

Można użyć tej samej funkcji i po prostu przekazać datę z lutego jako miesiąc. Jeśli range.length zwróci 28, Łączna liczba dni w roku wyniesie 365, jeśli zwróci 29, wtedy liczba dni wyniesie 366.

+4

Dostaje 31 dni, Czerwiec ma 30. – vikingosegundo

+0

Dostaje dni w szóstym miesiącu, czyli czerwcu (i czerwcu ma 30 dni). –

+0

Otrzymuję '31' za każdy miesiąc (w tym wrzesień). Dlaczego mogę podać 'NSYearCalendarUnit' jako parametr' inUnit', jeśli metoda zawsze zwraca następny (większy) typ daty (w tym przypadku miesiąc)? – AlexR

1

Co ze znalezieniem liczby dni w danym roku jako: Chociaż jest to nie względne rozwiązanie tego pytania.

-(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:

Jak dateWithString: jest przestarzała, można użyć

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

tworząc date1 i date2

+0

Czy ten kod dla '-daysBetweenTwoDates: andDate' faktycznie działa, jeśli daty są w różnych miesiącach? – paulmelnikow

+1

tak, nawet w różnych latach. to jest przetestowane i jest już częścią innej odpowiedzi przeze mnie ... miesiące temu –

+0

'dateWithString:' jest przestarzałe. –

1

Oto stosunkowo czysta wersja. To kategoria na 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];  
} 

Dzięki AKV za wskazanie, że w tym przypadku zadziała -components:fromDate:toDate:options.

Wydaje się, że nie działa to dla wersji 1582 lub wcześniejszej, co odpowiada Nikolai Ruhe, ponieważ obliczenia kalendarza gregoriańskiego po prostu nie zostały zdefiniowane przed tym terminem.

Powiązane problemy