Round NSDate to the next 5 minutes

For example, I have

NSDate *curDate = [NSDate date]; 

and its value is 9:13. I do not use the year, month and day of the curDate part.

What I want to get is a date with a 9:15 time value; If I have a time value of 9:16, I want to advance it to 9:20, etc.

How can I do this using NSDate?

+42
objective-c iphone cocoa
Jul 19 '09 at 4:46
source share
18 answers

Take the minute value, divide it by 5 rounds to get the next highest 5-minute block, multiply by 5 to get it back in minutes, and build a new 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]; 
+46
Jul 19 '09 at 6:56
source share

Here is my solution:

 NSTimeInterval seconds = round([date timeIntervalSinceReferenceDate]/300.0)*300.0; NSDate *rounded = [NSDate dateWithTimeIntervalSinceReferenceDate:seconds]; 

I did some testing, and it is about ten times faster than the Voss solution. With 1M iterations, it took about 3.39 seconds. This was completed in 0.38 seconds. The J3RM solution took 0.50 seconds. Memory usage should also be the lowest.

Not that performance is all but one line. You can also easily control rounding with division and multiplication.

EDIT: To answer the question, you can use ceil to properly round:

 NSTimeInterval seconds = ceil([date timeIntervalSinceReferenceDate]/300.0)*300.0; NSDate *rounded = [NSDate dateWithTimeIntervalSinceReferenceDate:seconds]; 

EDIT: extension in 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) } } 
+37
Oct 01 '13 at 18:43
source share

Wowers, I see many answers here, but many are long or hard to understand, so I will try to drop my 2 cents in case this helps. The NSCalendar class provides the necessary functionality in a safe and concise way. Here is a solution that works for me, without multiplying the second interval of seconds, rounding, or anything else. NSCalendar takes into account leap days / years and other oddities in time and 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() 

It can be added to the extension to NSDate , if necessary, or as a free-form function that returns a new instance of NSDate , no matter what you need. Hope this helps everyone who needs it.

Swift 3 Update

 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() 
+21
Jun 30 '16 at 2:00
source share

I think this is the best solution, but only my opinion based on the previous bed code. rounds to the nearest mark of 5 min. This code should use a lot less memory than solutions for date components. Brilliant, thanks for the direction.

 +(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; } 
+13
Nov 28 2018-11-11T00:
source share

How about this based on Chris' and swift3

import 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) 
+11
May 16 '16 at 18:43
source share

Thanks for the sample. Below I added the code to the round until the next 5 minutes

  -(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; } 
+6
Nov 21 '09 at 22:57
source share

Most of the answers here, unfortunately, are not entirely correct (although they seem to work very well for most users), because they either rely on the current active system calendar, or on the Gregorian calendar (which may be wrong), or on the fact that jump seconds do not exist and / or will always be ignored by OS X iOS. The following code works with copy and paste, it will be guaranteed to be correct and will not make such assumptions (and, therefore, will also not break in the future if Apple changes its support for leap seconds, since in this case NSCalendar will also correctly support them)

 { 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]; } 

If the current time is already a multiple of 5 minutes, the code will not change it. The original question did not explicitly indicate this case. If the code is always rounded to the next multiple of 5 minutes, simply remove the if (minutes % 5) { test and it will always be rounded.

+6
May 15 '14 at 12:41
source share

I just started experimenting with this for my application and came up with the following. This is in Swift, but the concept should be clear even if you don't know 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)! } 

The result looks right on my playground, where I add various user dates, close to midnight, closer to the new year, etc.

Edit: Swift2 support:

  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)! } 
+3
Sep 03 '14 at 12:39 on
source share

Here is my solution to the original problem (rounding) using the idea of ​​ayianni wrapper.

 -(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; } 
+2
Dec 23 '09 at 6:34
source share

Another common Swift solution that works up to 30 minutes of rounding using NSCalendar

 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!; } } 
+2
Apr 30 '15 at 15:47
source share

I searched for it myself, but using the above example, gave me the dates of the year 0001.

Here's my alternative, included in smorgan's more elegant suggestion, although be careful, I haven't tested this yet:

 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)]; 
+1
Jul 21 '09 at 13:38
source share

I'm not sure how effective NSDateComponents are, but if you just want to deal with NSDate itself, it can give you values ​​based on seconds, which can then be manipulated.

For example, this method is rounded to the nearest minute. Change 60 to 300 and round it to the next 5 minutes.

 + (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; } 
0
03 Sep 2018-11-11T00:
source share

I rewrote the @ J3RM solution as an extension in Swift in the NSDate class. Here to round the date to the nearest 15 minute interval:

 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 } } 
0
Oct 19 '14 at 4:59
source share

I know this is an older thread, but since there are more recent answers, I will share the utility method that I use to round NSDate to the nearest 5 minute interval.

I use this to populate a UITextField with the current date of the UIDatePicker when it becomes FirstResponder. You cannot just use [NSDate date] when the UIDatePicker is configured with something other than a 1 minute interval. The mine is configured at intervals of 5 minutes.

 + (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; } } 

Ignoring the title of the question and reading what @aler really wants to do (round to the nearest 5 minutes). All you have to do is the following:

 NSDate *ceilingDate = [NSDate dateWithTimeIntervalSinceReferenceDate:ceil([[NSDate date] timeIntervalSinceReferenceDate]/300.0)*300.0]; 
0
Dec 23 '14 at 10:18
source share

This is a general solution, which is rounded to the nearest mins 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]; } 
0
Mar 13 '15 at 18:06
source share
 - (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]; } 
0
Jul 20 '15 at 14:53
source share

Even shorter ... limit in seconds:

 let seconds = ceil(Date().timeIntervalSinceReferenceDate/300.0)*300.0 let roundedDate = Date(timeIntervalSinceReferenceDate: seconds) 
0
Aug 18 '17 at 7:49 on
source share

https://forums.developer.apple.com/thread/92399

See the link for a complete and detailed answer from an Apple employee. To save you a click, the solution:

 let original = Date() let rounded = Date(timeIntervalSinceReferenceDate: (original.timeIntervalSinceReferenceDate / 300.0).rounded(.toNearestOrEven) * 300.0) 
0
Dec 20 '17 at 20:54 on
source share



All Articles