【问题标题】:NSDateComponent RoundingNSDateComponents 舍入
【发布时间】:2014-02-12 13:12:07
【问题描述】:

我希望显示 NSDate 对象的月份数。

//Make Date Six Months In The Future
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *sixMonthsFromNow = [[NSDateComponents alloc] init];
[sixMonthsFromNow setMonth:6];
NSDate *finishDate = [calendar dateByAddingComponents:sixMonthsFromNow toDate:[NSDate date] options:0]; //Six Months Time

//Display
NSCalendarUnit requiredFormat = NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *dateComponents  = [calendar components:requiredFormat fromDate:[NSDate date] toDate:finishDate options:0];

NSLog(@"%d months %d days %d hours %d minutes %d seconds", [dateComponents month], [dateComponents day], [dateComponents hour], [dateComponents minute], [dateComponents second]);

这个输出:5 months 29 days 23 hours 59 minutes 59 seconds

这很好,但我只想显示月数。

如果我只限制几个月:

//Make Date Six Months In The Future
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *sixMonthsFromNow = [[NSDateComponents alloc] init];
[sixMonthsFromNow setMonth:6];
NSDate *finishDate = [calendar dateByAddingComponents:sixMonthsFromNow toDate:[NSDate date] options:0]; //Six Months Time

//Display
NSCalendarUnit requiredFormat = NSMonthCalendarUnit;
NSDateComponents *dateComponents  = [calendar components:requiredFormat fromDate:[NSDate date] toDate:finishDate options:0];
NSLog(@"%d months", [dateComponents month]);

这个输出:5 months

虽然这在技术上是正确的,但我想将天数四舍五入以使其输出六个月。

有没有简单的方法来达到这个效果?我注意到 NSDateComponents 上没有舍入属性。我是否必须手动检查天数并决定四舍五入?

我的最终目标是不仅将舍入效果限制为几个月,如果我只提供以下内容,这应该能够将几小时到几天:NSDayCalendarUnit

【问题讨论】:

  • 输出不完全是“6 个月”的原因是你调用了两次[NSDate date],并且用于计算finishDate 的日期和使用的日期之间可能存在毫秒差异计算差异dateComponents
  • 如果一个月可以有 28、29、30 或 31 天,您如何定义“四舍五入”?
  • 公平,保留对原始日期的引用确实可以正确计算日期,但这只是一个示例。它可以是任何不完美的六个月之后的日期。四舍五入确实是我需要帮助的地方,但似乎它必须由用户定义。为寻找干杯。

标签: ios iphone objective-c xcode nsdatecomponents


【解决方案1】:

以下方法可以达到你想要的。 这个想法是在计算日历单位的(向下舍入)数量之后 在开始日期和结束日期之间,在开始日期加上该金额和另外一个 并检查哪一个更接近结束日期:

@interface NSCalendar (MyCategory)
-(NSInteger)roundedUnit:(NSCalendarUnit)unit fromDate:(NSDate *)fromDate toDate:(NSDate *)toDate;
@end

@implementation NSCalendar (MyCategory)
-(NSInteger)roundedUnit:(NSCalendarUnit)unit fromDate:(NSDate *)fromDate toDate:(NSDate *)toDate
{
    // Number of units between the two dates:
    NSDateComponents *comps = [self components:unit fromDate:fromDate toDate:toDate options:0];
    NSInteger value = [comps valueForComponent:unit];

    // Add (value) units to fromDate:
    NSDate *date1 = [self dateByAddingComponents:comps toDate:fromDate options:0];

    // Add (value + 1) units to fromDate:
    [comps setValue:(value + 1) forComponent:unit];
    NSDate *date2 = [self dateByAddingComponents:comps toDate:fromDate options:0];

    // Now date1 <= toDate < date2. Check which one is closer,
    // and return the corresponding value:
    NSTimeInterval diff1 = [toDate timeIntervalSinceDate:date1];
    NSTimeInterval diff2 = [date2 timeIntervalSinceDate:toDate];
    return (diff1 <= diff2 ? value : value + 1);
}
@end

你会用它作为

NSInteger months = [calendar roundedUnit:NSMonthCalendarUnit fromDate:[NSDate date] toDate:finishDate];

代码使用NSDateComponents 的实用方法来自 https://github.com/henrinormak/NSDateComponents-HNExtensions/blob/master/README.md:

- (void)setValue:(NSInteger)value forComponent:(NSCalendarUnit)unit;
- (NSInteger)valueForComponent:(NSCalendarUnit)unit;

这些方法在 OS X 10.9 中是新的,但在 iOS 7 中不可用。

【讨论】:

    【解决方案2】:

    正如另一位评论者指出的那样,您调用 NSDate 两次,第二个日期比第一个稍晚。使用相同的日期对象两次,不会出现舍入错误。

    至于如何舍入:

    我认为 NSCalendars 日历计算方法中没有内置任何舍入。

    你需要决定这对你意味着什么。你可能会在中途四舍五入。在这种情况下,您可以使用 rangeOfUnit:inUnit:forDate 方法询问日历当前月份有多少天,然后将一半的天数添加到结束日期,然后询问月份。如果您只想“在一周内四舍五入,则只需在结束日期前加上一周,然后再询问月数差异。

    【讨论】:

    • 在任何情况下四舍五入都是模棱两可的。示例:从 2014 年 1 月 20 日到 2 月 4 日是 15 天。应该向下取整(因为 1 月有 31 天,15 28/2)?
    • @MartinR 这可能是没有内置函数的原因,正如你所说的“模棱两可” - 我想我需要考虑一些舍入规则。
    【解决方案3】:

    我也有类似的问题:

    let fmt1 = NSDateComponentsFormatter()
    
    fmt1.allowedUnits = .CalendarUnitYear |
        .CalendarUnitMonth |
        .CalendarUnitDay
    
    let dc1 = NSDateComponents()
    dc1.month = 1
    dc1.day = 15
    
    // This occasionally outputs "1 month, 14 days" instead of
    // "1 month, 15 days" when the formatter is constrained to
    // years, months and days. If formatter is allowed to use
    // all units, the output is "1 month, 14 days, 23 hours,
    // 59 minutes, 59 seconds".
    fmt1.stringFromDateComponents(dc1)
    

    这可以通过指定正的秒数并将其与 将maximumUnitCount formatter 参数设置为固定值:

    let fmt2 = NSDateComponentsFormatter()
    
    fmt2.allowedUnits = .CalendarUnitYear |
        .CalendarUnitMonth |
        .CalendarUnitDay
    
    fmt2.maximumUnitCount = 2
    fmt2.collapsesLargestUnit = true
    
    let dc2 = NSDateComponents()
    dc2.month = 1
    dc2.day = 15
    dc2.second = 10
    
    // This always outputs "1 month, 15 days"
    fmt2.stringFromDateComponents(dc2)
    

    此修复可能也适用于您的情况:只需在调用 dateByAddingComponents 之前为 NSDateComponents 分配一些正数秒数:

    [sixMonthsFromNow setMonth: 6];
    [sixMonthsFromNow setSecond: 10];
    

    【讨论】:

      猜你喜欢
      • 2012-06-19
      • 2021-11-05
      • 1970-01-01
      • 1970-01-01
      • 2014-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多