【问题标题】:How can I make NSDateFormatter not include hours if there are zero hours?如果小时数为零,如何使 NSDateFormatter 不包括小时数?
【发布时间】:2012-09-02 19:22:50
【问题描述】:

我正在使用 NSDateFormatter 来格式化持续时间 (see here)。

我想使用如下格式:“3 小时 15 分钟”。
这是我正在使用的格式字符串:@"H' hours, 'm' minutes'"

问题在于,对于短于一小时的持续时间,结果类似于“0 小时 35 分钟”。
在这种情况下,我只想显示“35 分钟”。

有没有办法告诉 NSDateFormatter 在没有小时数的情况下根本不显示小时数,还是我应该只使用 if 语句并手动构造字符串?

编辑:
我很清楚,这很容易通过几行额外的代码手动完成,这就是我过去的做法。我只是想知道格式化程序是否有足够的智能来自行处理这个问题,因为这似乎是一个常见问题。

【问题讨论】:

  • 请记住,如果时间间隔超过 24 小时,NSDateFormatter 版本将产生废话。
  • 您是否还希望输出不包含“0 分钟”?例如,输出“1 小时”而不是“1 小时 0 分钟”?
  • This approach 比使用 NSDateFormatter 更好、更简单、更高效。而且,事实上,如果需要,可以使用简单的 C 条件轻松地将“0 小时”的省略添加到该解决方案中。
  • @CameronSpickert 我可以接受“0 分钟”,因为它清楚地表明时间不仅仅是四舍五入到最接近的小时(就像科学测量中的尾随零)。
  • Nathan,请注意,如果您只向下滚动一点,该解决方案就在您引用的线程中。 “接受”的解决方案并不总是最好的。

标签: objective-c ios time nsdate nsdateformatter


【解决方案1】:

从 iOS 8.0 和 Mac OS X 10.10 (Yosemite) 开始,您可以使用 NSDateComponentsFormatter。因此,让我们使用小时和分钟作为allowedUnits(如您的示例)和NSDateComponentsFormatterZeroFormattingBehaviorDropLeading 作为zeroFormattingBehavior 以减少零小时。示例:

NSTimeInterval intervalWithHours = 11700; // 3:15:00
NSTimeInterval intervalWithZeroHours = 2100; // 0:35:00
NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.allowedUnits = NSCalendarUnitHour | NSCalendarUnitMinute;
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull;
formatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDropLeading;
NSString *string = [formatter stringFromTimeInterval:intervalWithHours];
NSString *stringWithoutHours = [formatter stringFromTimeInterval:intervalWithZeroHours];
NSLog(@"%@ / %@", string, stringWithoutHours);
// output: 3 hours, 15 minutes / 35 minutes

Swift 5 版本:

let intervalWithHours: TimeInterval = 11700
let intervalWithZeroHours: TimeInterval = 2100
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute]
formatter.unitsStyle = .full
formatter.zeroFormattingBehavior = .dropLeading
if let string = formatter.string(from: intervalWithHours), let stringWithoutHours = formatter.string(from: intervalWithZeroHours) {
    print("\(string) / \(stringWithoutHours)")
    // output: "3 hours, 15 minutes / 35 minutes\n"
}

【讨论】:

    【解决方案2】:

    一旦你有了日期字符串(我们称之为 foo),只需通过发送 stringByReplacingOccurencesOfString:withString: 来创建另一个字符串。例如:

    NSString* foo = @"0 hours, 35 minutes";
    NSString* bar = [foo stringByReplacingOccurrencesOfString:@"0 hours, " withString:@"" options:0 range:NSMakeRange(0,9)];
    NSLog(@"%@",bar);
    

    输出是:

    35 minutes
    

    我怀疑这与其他可能的方法相比是否非常有效,但除非您必须对数百个字符串执行此操作,否则它可能并不重要。

    编辑:另外,如果您希望能够在没有 0 小时的情况下获得日期,并假设您使用的是 stringFromDate:您可以添加一个类别来添加这样的方法:

    - (NSString *)stringFromDateWithoutZeroHours:(NSDate *)date{
        result = [self stringFromDate:date];
        return [result stringByReplacingOccurrencesOfString:@"0 hours, " withString:@"" options:0 range:NSMakeRange(0,9)];
    }
    

    【讨论】:

    • 这对@"10 小时 35 分钟" 仍然有效吗?我认为您需要包括范围。
    • 好点!我没有想到。幸运的是,我们知道范围;这是我们要删除的开始部分的长度,包括空格。这现在适用于所有情况,因为您的示例会将匹配移出范围。现在更新。
    【解决方案3】:

    为什么不为这种情况创建自己的 NSDateFormatter 子类并覆盖 stringFromDate(如果这是您所说的)?

    【讨论】:

    • 呃,为什么不直接使用if 语句并从两种格式中选择一种呢?这是额外的 5 行代码,而一个子类大约是 30 行。
    • 不会是 30 行。这将是大约 5 行,更重要的是,它们会在正确的位置。你应该子类化的“如果”是“难闻的气味”。从一开始就让格式化程序做你想做的事情,而不是测试。
    • “应该是子类化”???对于甚至不属于 NSDateFormatter 的逻辑,但可以更快更清楚地以不同的方式完成?您会为此创建一个子类(要管理两个额外的文件)?
    • 我必须将 NSDateFormatter 传递给 CorePlot 以获得轴标签,并且我成功地使用了 @matt 方法:
    【解决方案4】:

    这是使用 NSCalendar 和 NSDateComponents 的一种方法:

    static NSString *stringWithTimeInterval (NSTimeInterval interval)
    {
        NSDate *referenceDate = [NSDate dateWithTimeIntervalSince1970:0];
        NSDate *intervalDate = [NSDate dateWithTimeIntervalSince1970:interval];
    
        NSCalendar *calendar = [NSCalendar currentCalendar];
        NSDateComponents *components = [calendar components:NSHourCalendarUnit|NSMinuteCalendarUnit fromDate:referenceDate toDate:intervalDate options:0];
    
        NSMutableArray *stringComponents = [NSMutableArray array];
    
        NSInteger hours = [components hour];
        NSInteger minutes = [components minute];
    
        if (hours > 0) {
            [stringComponents addObject:[NSString stringWithFormat:@"%ld hours", hours]];
        }
        [stringComponents addObject:[NSString stringWithFormat:@"%ld minutes", minutes]];
    
        return [stringComponents componentsJoinedByString:@" "];
    }
    

    【讨论】:

      【解决方案5】:

      好的,我要从Rob Mayoff窃取一些代码:

      他的原作(稍作修改):

      -(NSString*) doit:(NSTimeInterval)timeInterval {
          NSUInteger seconds = (NSUInteger)round(timeInterval);
          NSString *result = [NSString stringWithFormat:@"%u hours, %u minutes", seconds / 3600, (seconds / 60) % 60];
          return result;
      }
      

      简单的修改以返回带/不带“小时”:

      -(NSString*) doit:(NSTimeInterval)timeInterval {
          NSString* result = nil;
          NSUInteger seconds = (NSUInteger)round(timeInterval);
          NSUInteger hours = seconds / 3600;
          NSUInteger minutes = (seconds / 60) % 60;
          if (hours > 0) {
              result = [NSString stringWithFormat:@"%u hours, %u minutes", hours, minutes];
          }
          else {
              result = [NSString stringWithFormat:@"%u minutes", minutes];
          }
          return result;
      }
      

      再次,但消除了if

      -(NSString*) doit:(NSTimeInterval)timeInterval {
          NSUInteger seconds = (NSUInteger)round(timeInterval);
          NSUInteger hours = seconds / 3600;
          NSUInteger minutes = (seconds / 60) % 60;
          NSString* result = hours ? [NSString stringWithFormat:@"%u hours, %u minutes", hours, minutes] : [NSString stringWithFormat:@"%u minutes", minutes];
          return result;
      }
      

      现在,我可以使用 , 将其简化为一个荒谬的 1-liner,但这有点愚蠢,不会使代码更清晰。

      (请注意,由于 iOS 12/24 “fechure”,所有使用 NSDateFormatter 的方案都被破坏了。)

      【讨论】:

        【解决方案6】:

        根据@matt 的回答,我已成功使用他的方法进行条件日期格式设置。在我的情况下,我只想要该月的第一天,否则就是一天。我需要为 CorePlot 提供一个日期格式化程序以进行轴格式化,并且它必须是有条件的。

        @interface MyNSDateFormatter : NSDateFormatter
        @property (nonatomic) NSDateFormatter *dateFormatterFirstDay;
        @property (nonatomic) NSDateFormatter *dateFormatterTheRest;
        @end
        
        @implementation MyNSDateFormatter
        -(id)init
        {
            self = [super init];
            if(self != nil)
                {
                _dateFormatterFirstDay = [[NSDateFormatter alloc] init];
                _dateFormatterTheRest = [[NSDateFormatter alloc] init];
        
                [_dateFormatterFirstDay setDateFormat:@"MMM"];
                [_dateFormatterTheRest setDateFormat:@"d"];
            }
            return self;
        }
        
        -(NSString *)stringFromDate:(NSDate *)date
        {
            NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSDayCalendarUnit fromDate:date];
            if ([dateComponents day] == 1) return [_dateFormatterFirstDay stringFromDate:date];
            else return [_dateFormatterTheRest stringFromDate:date];
        }
        @end
        

        【讨论】:

          猜你喜欢
          • 2016-03-15
          • 1970-01-01
          • 2021-12-25
          • 2015-08-01
          • 1970-01-01
          • 2015-05-09
          • 2015-09-02
          • 2018-11-02
          相关资源
          最近更新 更多