【问题标题】:Objective-C: Getting the number of days in a calendar yearObjective-C:获取日历年的天数
【发布时间】:2013-02-18 16:38:16
【问题描述】:

我正在使用下面的NSCalendar 方法来获取一个日历年的天数:

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

我期待 range.length 返回类型为 NSRange 的值,即 365 或 366(天)。

但是,对于日期值2005-06-06,此代码返回31 中的range.length

我的代码有什么问题?

这是完整的sn-p代码:

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

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

【问题讨论】:

  • 您将永远得到 31 对于任何一个月。
  • 是的,我在任何年份都尝试过,任何月份它总是显示 31。一旦我更改了 NSMonthCalenderUnit 天数是正确的。!!!要么我们遗漏了什么,要么这个 API 有一些错误。
  • 该方法工作正常。它计算一个单位在一个较大单位的时间跨度内的可能值的范围。每年有一个月有 31 天。

标签: objective-c cocoa-touch cocoa nsdate nscalendar


【解决方案1】:

这会计算给定日期一年中的天数:

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;

遗憾的警告:这在 1582 年无法正常工作。

1582 年,即 Gregor 引入目前广泛使用的日历的那一年,需要修复以使太阳与日历年保持一致。所以他们采用了务实的解决方案:他们刚刚在 10 月 5 日至 14 日放弃了。 (他们也没有疯狂到改变工作日)。因此,1582 年只有 355 天。

附录:上面的代码只能在 1582 年之后的年份正常工作。例如,它返回 1500 年的 365 天,即使在当时使用的儒略历中今年是闰年。公历从 1582 年 10 月 15 日开始。在公历上进行的计算只是在该日期之前没有定义。所以这样苹果的实现是正确的。在 1583 年之前,我不知道 Cocoa 上的正确实现。

【讨论】:

  • 只是出于好奇,Cocoa 的公历实施是否也解释了逐个国家逐渐采用日历的变幻莫测?例如,法国在采用率方面落后于意大利和西班牙两个月,而瑞典则采取了类似跳过闰日一个世纪而不是一次跳过一周的做法。
  • @JoshCaswell 据我所知,这甚至没有建模:您不能要求特定地区或日期的日历。所以我猜公历只是按原样工作。您可以在定义闰年之前向它询问闰年,它会回答,就好像当时公历已经生效一样。
  • 完全一致:它使用“公历”,大致意思是“使用相同的规则重新标记倒退的日子”。因此 1500 不是闰年,但 1200 是(与 0000 一起,请不要开始讨论 0 年)。必须注意避免误解,但它是 ISO 8601 规定的,天文学家使用什么,以及历史学家转换成什么以使计算更容易(通常带有后缀“NS”)。
  • @tc。感谢您的澄清。提供了很好的信息!
  • 啊哈!那么 ISO教皇新世界秩序阴谋的一部分! (笑话!)感谢 Nikolai 和 @tc 提供的信息。
【解决方案2】:

这是一个相对干净的版本。这是 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];    
}

感谢 AKV 指出 -components:fromDate:toDate:options 在这种情况下可以工作。

这似乎不适用于 1582 年或更早,根据 Nikolai Ruhe 的回答,这是因为在此之前根本没有定义公历计算。

【讨论】:

    【解决方案3】:

    如何找出给定年份的天数:虽然这不是问题的相对解决方案。

    -(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 *yourYear=@"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);
    }            
    

    编辑:

    由于 dateWithString: 已被弃用,您可以使用

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

    形成date1date2

    【讨论】:

    • 如果日期在不同月份,-daysBetweenTwoDates:andDate 的代码是否真的有效?
    • 是的,即使是不同年份。这是经过测试的,并且已经是我其他答案的一部分......几个月前
    • dateWithString: 已弃用。
    【解决方案4】:

    您将获得日期当月的天数。

    您可以使用相同的函数,只需传入一个以二月为月份的日期。如果 range.length 返回 28,则一年中的总天数为 365,如果返回 29,则天数为 366。

    【讨论】:

    • 他有 31 天,六月有 30 天。
    • 他得到了第 6 个月的天数,也就是 6 月(而 6 月有 30 天)
    • 我每个月(包括九月)都会收到31。如果方法总是返回下一个(较大的)日期类型(在这种情况下为月份),为什么我可以将 NSYearCalendarUnit 指定为 inUnit 参数?
    • 在Xntrgk星球上也是无效的。
    • 像你这样的编码员要为 20 万年甚至 10 万年的错误负责! ;)
    猜你喜欢
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 2013-08-07
    • 2019-04-10
    • 2015-09-14
    • 1970-01-01
    相关资源
    最近更新 更多