如何将NSTimeInterval(秒)转换为分钟

105

我有一个记录了某个事件经过的秒数的变量,它是以NSTimeInterval数据类型存储的。

我想将它转换成分钟的形式。

例如,如果我有: "326.4"秒,我想将其转换为以下字符串:“5:26”。

实现这个目标的最佳方法是什么?

谢谢。

12个回答

183

简要说明

  1. 如果你只需要将时间间隔转换为分钟,那么Brian Ramsay的答案更方便。
  2. 如果要使用Cocoa API,不仅要将NSTimeInterval转换为分钟,还要转换为天数、月份、星期等等,我认为这是一种更通用的方法。
  3. 使用NSCalendar方法:

    • (NSDateComponents *)components:(NSUInteger)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSUInteger)opts

    • "使用指定的组件,返回两个提供的日期之间的差异作为NSDateComponents对象"。来自API文档。

  4. 创建两个NSDate,它们的差值是你想要转换的NSTimeInterval。(如果你的NSTimeInterval来自比较两个NSDate,那么你不需要执行此步骤,也不需要NSTimeInterval)。

  5. 从NSDateComponents获取你所需的报价

示例代码

// The time interval 
NSTimeInterval theTimeInterval = 326.4;

// Get the system calendar
NSCalendar *sysCalendar = [NSCalendar currentCalendar];

// Create the NSDates
NSDate *date1 = [[NSDate alloc] init];
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:theTimeInterval sinceDate:date1]; 

// Get conversion to months, days, hours, minutes
unsigned int unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit;

NSDateComponents *conversionInfo = [sysCalendar components:unitFlags fromDate:date1  toDate:date2  options:0];

NSLog(@"Conversion: %dmin %dhours %ddays %dmoths",[conversionInfo minute], [conversionInfo hour], [conversionInfo day], [conversionInfo month]);

[date1 release];
[date2 release];

已知问题

  • 仅仅进行转换来说,这太多了,你是对的,但这就是API的工作方式。
  • 我的建议:如果你习惯使用NSDate和NSCalendar管理时间数据,API会为你完成繁重的工作。

4
做一点转换需要做很多的解决方法,但它感觉很舒适、模块化和非常灵活。为这个示例代码 @Albaregar 投赞成票。 - Rigo Vides
2
对于一个好的详尽回答,点个赞。不过这段代码很耗费资源。 - Nick Weaver
1
+1...一个注意事项。如果需要将分数秒四舍五入,使用此代码风格时要小心。-[NSCalendar components:fromDate:toDate:options:]的默认行为可能会在toDate组件比fromDate超前31.5秒(例如)的情况下出现问题-返回的组件将在秒字段中有31。有一个可选参数NSWrapCalendarComponents,但是从我所做的(有限的)实验来看,它会将其包装到最近的一秒,而不是四舍五入。您的结果可能会有所不同。 - David Doyle
如果您使用的是64位机器,请使用%ld - Anoop Vaidya
5
像Nick Weaver这样的人会认为这段代码看起来会很慢,但事实并非如此。我正在开发一个计时器应用程序,并将Albaregar的版本与类似Brian Ramsey(和其他人)的版本进行了测试......令人惊讶的是,即使在相对较慢的设备(4S)上以每秒100次的频率轮询(这是极度过度的),处理器利用率之间的差异也不到1%。 - mmc

147

伪代码:

minutes = floor(326.4/60)
seconds = round(326.4 - minutes * 60)

91
值得一提的是,NSTimerInterval 只是 double 的一个 typedef。 - Armentage
4
看看Albaregar的回答 - 这是更好的方法(代码更多但更灵活、易于维护)。 - benvolioT
1
那段代码非常昂贵。然而,对于快速将持续时间转换为秒的需求,这个答案更好。 - SpacyRicochet
2
请注意,本答案会给你诸如1:60之类的东西。看看StratFan的回答,它更接近事实。但是去掉floor和round,你就可以轻松完成了。 - InvulgoSoft
在撰写本评论时,Mick MacCallum的回答似乎是最新的,并且是通过使用本地API的方式进行的。 - Tomasz Nazarenko

44
所有这些看起来都比必要的复杂!这里有一种简单明了的方法将时间间隔转换为小时、分钟和秒:

所有这些看起来都比必要的复杂!这里有一种简单明了的方法将时间间隔转换为小时、分钟和秒:

NSTimeInterval timeInterval = 326.4;
long seconds = lroundf(timeInterval); // Since modulo operator (%) below needs int or long

int hour = seconds / 3600;
int mins = (seconds % 3600) / 60;
int secs = seconds % 60;

注意将浮点数转换成整数时,会自动执行 floor() 函数,但如果你感觉更好,可以将其添加到前两个整数中。


太棒了!我试了Ramsay的代码,不过好像慢了一秒钟。 - uplearned.com
你的代码缺少分号,但无法让我编辑以进行修复。 - Jeef
我认为你的方法是最好的 - 简单,完成工作,并且“轻量级”。完美。 - BonanzaDriver

29

请原谅我是一个Stack的新手...我不确定如何回复Brian Ramsay的答案...

使用round对于59.5到59.99999之间的秒值将无效。在这段时间内,秒值将为60。改用trunc...

 double progress;

 int minutes = floor(progress/60);
 int seconds = trunc(progress - minutes * 60);

1
实际上,只需完全删除 floor 和 trunk/round 即可。 :) - InvulgoSoft
@StratFan,您能否在您的答案中添加小时数? - Shmidt

27
如果您的目标是iOS 8或OS X 10.10及以上版本,这个任务就变得容易了许多。新的NSDateComponentsFormatter类允许您将给定的NSTimeInterval从秒转换为本地化字符串以显示给用户。例如:
Objective-C
NSTimeInterval interval = 326.4;

NSDateComponentsFormatter *componentFormatter = [[NSDateComponentsFormatter alloc] init];

componentFormatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
componentFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDropAll;

NSString *formattedString = [componentFormatter stringFromTimeInterval:interval];
NSLog(@"%@",formattedString); // 5:26

迅捷

let interval = 326.4

let componentFormatter = NSDateComponentsFormatter()

componentFormatter.unitsStyle = .Positional
componentFormatter.zeroFormattingBehavior = .DropAll

if let formattedString = componentFormatter.stringFromTimeInterval(interval) {
    print(formattedString) // 5:26
}
NSDateCompnentsFormatter可以将输出格式化为更长的形式。有关更多信息,请查看NSHipster的NSFormatter文章。根据您正在使用的类(如果不是NSTimeInterval),通过以下方法将NSDateComponents实例或两个NSDate对象传递给格式化程序可能更方便。

Objective-C

NSString *formattedString = [componentFormatter stringFromDate:<#(NSDate *)#> toDate:<#(NSDate *)#>];
NSString *formattedString = [componentFormatter stringFromDateComponents:<#(NSDateComponents *)#>];

迅速的

if let formattedString = componentFormatter.stringFromDate(<#T##startDate: NSDate##NSDate#>, toDate: <#T##NSDate#>) {
    // ...
}

if let formattedString = componentFormatter.stringFromDateComponents(<#T##components: NSDateComponents##NSDateComponents#>) {
    // ...
}

这是创建字符串的正确方式,需要适当地进行本地化。 - SwiftArchitect
1
这个答案比被采纳的答案更 "Objective-C",也更有用。你不需要使用 switch 或者 if else 语句。NSDateComponentsFormatter 会为你处理一切。小时,分钟,秒钟都可以轻松输出,无需担心。 - rustyMagnet

17

Brian Ramsay的代码,去伪存真:

- (NSString*)formattedStringForDuration:(NSTimeInterval)duration
{
    NSInteger minutes = floor(duration/60);
    NSInteger seconds = round(duration - minutes * 60);
    return [NSString stringWithFormat:@"%d:%02d", minutes, seconds];
}

这不是本地化。 - SwiftArchitect

7
这是一个Swift版本:
func durationsBySecond(seconds s: Int) -> (days:Int,hours:Int,minutes:Int,seconds:Int) {
    return (s / (24 * 3600),(s % (24 * 3600)) / 3600, s % 3600 / 60, s % 60)
}

可以这样使用:

let (d,h,m,s) = durationsBySecond(seconds: duration)
println("time left: \(d) days \(h) hours \(m) minutes \(s) seconds")

6
    NSDate *timeLater = [NSDate dateWithTimeIntervalSinceNow:60*90];

    NSTimeInterval duration = [timeLater  timeIntervalSinceNow];

    NSInteger hours = floor(duration/(60*60));
    NSInteger minutes = floor((duration/60) - hours * 60);
    NSInteger seconds = floor(duration - (minutes * 60) - (hours * 60 * 60));

    NSLog(@"timeLater: %@", [dateFormatter stringFromDate:timeLater]);

    NSLog(@"time left: %d hours %d minutes  %d seconds", hours,minutes,seconds);

输出:

timeLater: 22:27
timeLeft: 1 hours 29 minutes  59 seconds

这是一个非常完美的日期转换示例。 - ravinder521986

5

由于本质上是双倍...

除以60.0并提取整数部分和小数部分。

整数部分将成为分钟的整数部分。

再次将小数部分乘以60.0。

结果将是剩余的秒数。


3

请注意,原始问题涉及字符串输出,而不是伪代码或单个字符串组件。

我想将其转换为以下字符串:"5:26"

许多答案缺少国际化问题,大多数人手动计算数学运算。这些都是20世纪的做法...

不要自己进行数学计算(Swift 4)

let timeInterval: TimeInterval = 326.4
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.unitsStyle = .positional
if let formatted = dateComponentsFormatter.string(from: timeInterval) {
    print(formatted)
}

5:26


利用现有库

如果你真的需要单独的组件和易读的代码,请查看SwiftDate

import SwiftDate
...
if let minutes = Int(timeInterval).seconds.in(.minute) {
    print("\(minutes)")
}

5


感谢 @mickmaccallum@polarwarDateComponentsFormatter 的充分使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接