轮到NSD到最近的5分钟
比如我有
NSDate *curDate = [NSDate date];
它的价值是上午9点13分。 我不使用curDate的年,月,日部分。
我想得到的是具有9:15时间值的date; 如果我有时间价值9:16我想提前到9:20等等。
我怎么能用NSDate做到这一点?
取分值,除以五舍五入得到下一个最高的5分钟单位,再乘以5分钟即可得出,并构造一个新的NSDate。
NSDateComponents *time = [[NSCalendar currentCalendar] components:NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:curDate]; NSInteger minutes = [time minute]; float minuteUnit = ceil((float) minutes / 5.0); minutes = minuteUnit * 5.0; [time setMinute: minutes]; curDate = [[NSCalendar currentCalendar] dateFromComponents:time];
这是我的解决scheme:
NSTimeInterval seconds = round([date timeIntervalSinceReferenceDate]/300.0)*300.0; NSDate *rounded = [NSDate dateWithTimeIntervalSinceReferenceDate:seconds];
我做了一些testing,大约是Voss解决scheme的十倍。 经过1M次迭代大约需要3.39秒。 这一个执行0.38秒。 J3RM的解决scheme耗时0.50秒。 内存使用量也应该是最低的。
不是说表演是一切,而是一条线。 你也可以很容易地控制分割和乘法舍入。
编辑:要回答这个问题,你可以使用ceil
正确地收起来:
NSTimeInterval seconds = ceil([date timeIntervalSinceReferenceDate]/300.0)*300.0; NSDate *rounded = [NSDate dateWithTimeIntervalSinceReferenceDate:seconds];
编辑:在Swift中的扩展:
public extension Date { public func round(precision: TimeInterval) -> Date { return round(precision: precision, rule: .toNearestOrAwayFromZero) } public func ceil(precision: TimeInterval) -> Date { return round(precision: precision, rule: .up) } public func floor(precision: TimeInterval) -> Date { return round(precision: precision, rule: .down) } private func round(precision: TimeInterval, rule: FloatingPointRoundingRule) -> Date { let seconds = (self.timeIntervalSinceReferenceDate / precision).rounded(rule) * precision; return Date(timeIntervalSinceReferenceDate: seconds) } }
Wowsers,我在这里看到了很多答案,但是很多答案很长或者很难理解,所以我会试着投入我的2分钱以防万一。 NSCalendar
类以安全简洁的方式提供所需的function。 这是一个适用于我的解决scheme,不需要乘以时间间隔秒,四舍五入或任何东西。 NSCalendar
考虑闰日/年,以及其他时间和date的古怪。 (Swift 2.2)
let calendar = NSCalendar.currentCalendar() let rightNow = NSDate() let interval = 15 let nextDiff = interval - calendar.component(.Minute, fromDate: rightNow) % interval let nextDate = calendar.dateByAddingUnit(.Minute, value: nextDiff, toDate: rightNow, options: []) ?? NSDate()
如果需要,可以将它添加到NSDate
的扩展上,或者作为返回新的NSDate
实例的自由格式函数,无论您需要什么。 希望这可以帮助任何需要它的人。
Swift 3更新
let calendar = Calendar.current let rightNow = Date() let interval = 15 let nextDiff = interval - calendar.component(.minute, from: rightNow) % interval let nextDate = calendar.date(byAdding: .minute, value: nextDiff, to: rightNow) ?? Date()
我认为这是最好的解决scheme,但只是我的意见,基于以前的海报代码。 轮到最近的5分钟大关。 这个代码应该使用比date组件解决schemeless得多的内存。 辉煌,感谢您的指导。
+(NSDate *) dateRoundedDownTo5Minutes:(NSDate *)dt{ int referenceTimeInterval = (int)[dt timeIntervalSinceReferenceDate]; int remainingSeconds = referenceTimeInterval % 300; int timeRoundedTo5Minutes = referenceTimeInterval - remainingSeconds; if(remainingSeconds>150) {/// round up timeRoundedTo5Minutes = referenceTimeInterval +(300-remainingSeconds); } NSDate *roundedDate = [NSDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)timeRoundedTo5Minutes]; return roundedDate; }
基于Chris和Swift的这个怎么样?
导入UIKit
enum DateRoundingType { case round case ceil case floor } extension Date { func rounded(minutes: TimeInterval, rounding: DateRoundingType = .round) -> Date { return rounded(seconds: minutes * 60, rounding: rounding) } func rounded(seconds: TimeInterval, rounding: DateRoundingType = .round) -> Date { var roundedInterval: TimeInterval = 0 switch rounding { case .round: roundedInterval = (timeIntervalSinceReferenceDate / seconds).rounded() * seconds case .ceil: roundedInterval = ceil(timeIntervalSinceReferenceDate / seconds) * seconds case .floor: roundedInterval = floor(timeIntervalSinceReferenceDate / seconds) * seconds } return Date(timeIntervalSinceReferenceDate: roundedInterval) } } // Example let nextFiveMinuteIntervalDate = Date().rounded(minutes: 5, rounding: .ceil) print(nextFiveMinuteIntervalDate)
谢谢你的样品。 下面我已经添加了一些代码到最近的5分钟
-(NSDate *)roundDateTo5Minutes:(NSDate *)mydate{ // Get the nearest 5 minute block NSDateComponents *time = [[NSCalendar currentCalendar] components:NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:mydate]; NSInteger minutes = [time minute]; int remain = minutes % 5; // if less then 3 then round down if (remain<3){ // Subtract the remainder of time to the date to round it down evenly mydate = [mydate addTimeInterval:-60*(remain)]; }else{ // Add the remainder of time to the date to round it up evenly mydate = [mydate addTimeInterval:60*(5-remain)]; } return mydate; }
这里的大多数回复不幸的是不完全正确(尽pipe他们似乎对大多数用户来说工作得很好),因为他们要么依靠当前的活动系统日历作为公历(可能并非如此),要么依赖于闰秒不存在和/或将永远被OS X iOS忽略。 下面的代码可以复制和粘贴,保证是正确的,并且没有这样的假设(如果Apple改变了闰秒支持,将来也不会中断,因为在这种情况下NSCalendar也必须正确地支持它们):
{ NSDate * date; NSUInteger units; NSCalendar * cal; NSInteger minutes; NSDateComponents * comp; // Get current date date = [NSDate date]; // Don't rely that `currentCalendar` is a // Gregorian calendar that works the way we are used to. cal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar ]; [cal autorelease]; // Delete that line if using ARC // Units for the day units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit; // Units for the time (seconds are irrelevant) units |= NSHourCalendarUnit | NSMinuteCalendarUnit; // Split current date into components comp = [cal components:units fromDate:date]; // Get the minutes, // will be a number between 0 and 59. minutes = [comp minute]; // Unless it is a multiple of 5... if (minutes % 5) { // ... round up to the nearest multiple of 5. minutes = ((minutes / 5) + 1) * 5; } // Set minutes again. // Minutes may now be a value between 0 and 60, // but don't worry, NSCalendar knows how to treat overflows! [comp setMinute:minutes]; // Convert back to date date = [cal dateFromComponents:comp]; }
如果当前时间已经是5分钟的倍数,则代码不会改变它。 原来的问题没有明确说明这个情况。 如果代码总是四舍五入到5分钟的下一个倍数,只要删除testingif (minutes % 5) {
它总是四舍五入。
我刚开始尝试这个我的一个应用程序,并提出以下。 这是在Swift中,但是这个概念应该是可以理解的,即使你不知道Swift。
func skipToNextEvenFiveMinutesFromDate(date: NSDate) -> NSDate { var componentMask : NSCalendarUnit = (NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay | NSCalendarUnit.CalendarUnitHour | NSCalendarUnit.CalendarUnitMinute) var components = NSCalendar.currentCalendar().components(componentMask, fromDate: date) components.minute += 5 - components.minute % 5 components.second = 0 if (components.minute == 0) { components.hour += 1 } return NSCalendar.currentCalendar().dateFromComponents(components)! }
结果看起来在我的游乐场是正确的,在那里我注入各种自定义date,接近午夜,接近新的一年等。
编辑:Swift2支持:
func skipToNextEvenFiveMinutesFromDate(date: NSDate) -> NSDate { let componentMask : NSCalendarUnit = ([NSCalendarUnit.Year , NSCalendarUnit.Month , NSCalendarUnit.Day , NSCalendarUnit.Hour ,NSCalendarUnit.Minute]) let components = NSCalendar.currentCalendar().components(componentMask, fromDate: date) components.minute += 5 - components.minute % 5 components.second = 0 if (components.minute == 0) { components.hour += 1 } return NSCalendar.currentCalendar().dateFromComponents(components)! }
这里是我使用ayianni的包装理念解决原始问题(四舍五入)。
-(NSDate *)roundDateToCeiling5Minutes:(NSDate *)mydate{ // Get the nearest 5 minute block NSDateComponents *time = [[NSCalendar currentCalendar] components:NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:mydate]; NSInteger minutes = [time minute]; int remain = minutes % 5; // Add the remainder of time to the date to round it up evenly mydate = [mydate addTimeInterval:60*(5-remain)]; return mydate; }
又一个Swift通用解决scheme,使用NSCalendar进行30分钟的四舍五入
extension NSDate { func nearest(minutes: Int) -> NSDate { assert(minutes <= 30, "nearest(m) suppport rounding up to 30 minutes"); let cal = NSCalendar.currentCalendar(); var time = cal.components(.CalendarUnitMinute | .CalendarUnitSecond, fromDate: self); let rem = time.minute % minutes if rem > 0 { time.minute = minutes - rem; } time.second = -time.second; time.nanosecond = -time.nanosecond //updated 7.07.15 let date = cal.dateByAddingComponents(time, toDate: self, options: NSCalendarOptions(0)); return date!; } }
自己一直在寻找这个,但使用上面的例子给了我从0001年的date。
这里是我的替代scheme,join了smorgan的更优雅的modbuild议,不过要注意我还没有泄漏testing:
NSDate *myDate = [NSDate date]; // Get the nearest 5 minute block NSDateComponents *time = [[NSCalendar currentCalendar] components:NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:myDate]; NSInteger minutes = [time minute]; int remain = minutes % 5; // Add the remainder of time to the date to round it up evenly myDate = [myDate addTimeInterval:60*(5-remain)];
我不确定NSDateComponents有多高效,但如果你只是想处理NSDate本身,它可以给你基于秒的值,然后可以被操纵。
例如,此方法向下舍入到最接近的分钟。 将60改为300,并将其舍入到最近的5分钟。
+ (NSDate *)dateRoundedDownToMinutes:(NSDate *)date { // Strip miliseconds by converting to int int referenceTimeInterval = (int)[date timeIntervalSinceReferenceDate]; int remainingSeconds = referenceTimeInterval % 60; int timeRoundedDownToMinutes = referenceTimeInterval - remainingSeconds; NSDate *roundedDownDate = [NSDate dateWithTimeIntervalSinceReferenceDate:(NSTimeInterval)timeRoundedDownToMinutes]; return roundedDownDate; }
我在NSDate类中重写了@ J3RM的解决scheme作为Swift中的扩展。 这里是将date舍入到最接近的第15分钟的时间间隔:
extension NSDate { func nearestFifteenthMinute() -> NSDate! { let referenceTimeInterval = Int(self.timeIntervalSinceReferenceDate) let remainingSeconds = referenceTimeInterval % 900 var timeRoundedTo5Minutes = referenceTimeInterval - remainingSeconds if remainingSeconds > 450 { timeRoundedTo5Minutes = referenceTimeInterval + (900 - remainingSeconds) } let roundedDate = NSDate.dateWithTimeIntervalSinceReferenceDate(NSTimeInterval(timeRoundedTo5Minutes)) return roundedDate } }
我知道这是一个较旧的线程,但由于有更多的近期答案,我将分享我使用的实用程序方法将NSDate舍入到最近的5分钟间隔。
我使用它来填充UITextField与当前的UIDatePickerdate,当它成为FirstResponder。 当UIDatePickerconfiguration为1分钟间隔以外的内容时,不能仅使用[NSDatedate]。 矿井configuration5分钟的时间间隔。
+ (NSDate *)roundToNearest5MinuteInterval { NSDate *ceilingDate = [NSDate dateWithTimeIntervalSinceReferenceDate:ceil([[NSDate date] timeIntervalSinceReferenceDate]/300.0)*300.0]; NSDate *floorDate = [NSDate dateWithTimeIntervalSinceReferenceDate:floor([[NSDate date] timeIntervalSinceReferenceDate]/300.0)*300.0]; NSTimeInterval ceilingInterval = [ceilingDate timeIntervalSinceNow]; NSTimeInterval floorInterval = [floorDate timeIntervalSinceNow]; if (fabs(ceilingInterval) < fabs(floorInterval)) { return ceilingDate; } else { return floorDate; } }
忽略问题的标题,并阅读@aler真正想要完成的事情(四舍五入到最近的5分钟)。 所有你需要做的是以下几点:
NSDate *ceilingDate = [NSDate dateWithTimeIntervalSinceReferenceDate:ceil([[NSDate date] timeIntervalSinceReferenceDate]/300.0)*300.0];
这是一个通用的解决scheme,可以将最近的input“分”
+(NSDate *)roundUpDate:(NSDate *)aDate toNearestMins:(NSInteger)mins { NSDateComponents *components = [[NSCalendar currentCalendar] components:NSUIntegerMax fromDate:aDate]; NSInteger dateMins = components.minute; dateMins = ((dateMins+mins)/mins)*mins; [components setMinute:dateMins]; [components setSecond:0]; return [[NSCalendar currentCalendar] dateFromComponents:components]; }
- (NSDate *)roundDateToNearestFiveMinutes:(NSDate *)date { NSDateComponents *time = [[NSCalendar currentCalendar] components:NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:date]; NSInteger minutes = [time minute]; float minuteUnit = ceil((float) minutes / 5.0); minutes = minuteUnit * 5.0; [time setMinute: minutes]; return [[NSCalendar currentCalendar] dateFromComponents:time]; }
甚至更短…限制到秒:
let seconds = ceil(Date().timeIntervalSinceReferenceDate/300.0)*300.0 let roundedDate = Date(timeIntervalSinceReferenceDate: seconds)