例如我有
NSDate *curDate = [NSDate date];
它的价值是上午9:13。我没有使用curDate的年,月,日部分。
我想得到的是9:15时间值的日期;如果我有9:16的时间价值,我想把它推进到9:20,依此类推。
如何使用NSDate做到这一点?
答案 0 :(得分:55)
这是我的解决方案:
NSTimeInterval seconds = round([date timeIntervalSinceReferenceDate]/300.0)*300.0;
NSDate *rounded = [NSDate dateWithTimeIntervalSinceReferenceDate:seconds];
我做了一些测试,它的速度是Voss解决方案的十倍。通过1M迭代,大约需要3.39秒。这一次在0.38秒内完成。 J3RM的解决方案耗时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)
}
}
答案 1 :(得分:54)
取分钟值,除以5向上舍入以得到下一个最高的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];
答案 2 :(得分:27)
Wowsers,我在这里看到了很多答案,但很多很长或很难理解,所以我会试着投入2美分以防万一。 NSCalendar
类以安全和简洁的方式提供所需的功能。这是一个适合我的解决方案,没有乘以时间间隔秒,舍入或任何东西。 NSCalendar
考虑了闰日/年,以及其他时间和日期的怪异。 (斯威夫特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()
答案 3 :(得分:26)
这基于Chris'和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)
答案 4 :(得分:13)
我认为这是最好的解决方案,但我的观点是基于之前的海报代码。轮到最近的5分钟标记。此代码应使用比日期组件解决方案少得多的内存。太棒了,谢谢你的指导。
+(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;
}
答案 5 :(得分:6)
感谢您的样品。 下面我添加了一些代码到最接近的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;
}
答案 6 :(得分:6)
遗憾的是,这里的大多数回复都不完全正确(尽管它们似乎对大多数用户来说效果很好),因为它们要么依赖当前的活动系统日历作为公历(可能不是这种情况),要么依赖于事实上闰秒不存在和/或将永远被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分钟的倍数,只需删除测试if (minutes % 5) {
,它将始终向上舍入。
答案 7 :(得分:5)
请参阅Apple工作人员的完整详细答案链接。为了节省您的点击,解决方案:
let original = Date()
let rounded = Date(timeIntervalSinceReferenceDate:
(original.timeIntervalSinceReferenceDate / 300.0).rounded(.toNearestOrEven) * 300.0)
答案 8 :(得分:3)
我刚刚开始尝试使用此应用程序,并提出以下内容。它在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)!
}
结果在我的操场上看起来正确,我注入了各种自定义日期,接近午夜,接近新年等。
编辑: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)!
}
答案 9 :(得分:2)
这是我使用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;
}
答案 10 :(得分:2)
另一个Swift通用解决方案,使用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!;
}
}
答案 11 :(得分:1)
我在NSDate类中重写了@ J3RM的解决方案作为Swift的扩展。这里是将日期舍入到最近的第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
}
}
答案 12 :(得分:1)
@ipje的answer在接下来的5分钟内完成了技巧,但是我需要更灵活的方法,并且我想摆脱所有的魔术数字。
由于answer to a similar question,我找到了解决方案
我的解决方案使用Swift 5.2和Measurement
来避免使用幻数:
extension UnitDuration {
var upperUnit: Calendar.Component? {
if self == .nanoseconds {
return .second
}
if self == .seconds {
return .minute
}
if self == .minutes {
return .hour
}
if self == .hours {
return .day
}
return nil
}
}
extension Date {
func roundDate(to value: Int, in unit: UnitDuration, using rule: FloatingPointRoundingRule, and calendar: Calendar = Calendar.current) -> Date? {
guard unit != .picoseconds && unit != .nanoseconds,
let upperUnit = unit.upperUnit else { return nil }
let value = Double(value)
let unitMeasurement = Measurement(value: value, unit: unit)
let interval = unitMeasurement.converted(to: .seconds).value
let startOfPeriod = calendar.dateInterval(of: upperUnit, for: self)!.start
var seconds = self.timeIntervalSince(startOfPeriod)
seconds = (seconds / interval).rounded(rule) * interval
return startOfPeriod.addingTimeInterval(seconds)
}
func roundDate(toNearest value: Int, in unit: UnitDuration, using calendar: Calendar = Calendar.current) -> Date? {
return roundDate(to: value, in: unit, using: .toNearestOrEven)
}
func roundDate(toNext value: Int, in unit: UnitDuration, using calendar: Calendar = Calendar.current) -> Date? {
return roundDate(to: value, in: unit, using: .up)
}
}
在我的操场上:
let calendar = Calendar.current
let date = Calendar.current.date(from: DateComponents(timeZone: TimeZone.current, year: 2020, month: 6, day: 12, hour: 00, minute: 24, second: 17, nanosecond: 577881))! // 12 Jun 2020 at 00:24
var roundedDate = date.roundDate(toNext: 5, in: .seconds)!
//"12 Jun 2020 at 00:24"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 24 second: 20 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNearest: 5, in: .seconds)!
// "12 Jun 2020 at 00:24"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 24 second: 15 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNext: 5, in: .minutes)!
// "12 Jun 2020 at 00:25"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 25 second: 0 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNearest: 5, in: .minutes)!
// "12 Jun 2020 at 00:25"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 25 second: 0 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNext: 5, in: .hours)!
// "12 Jun 2020 at 05:00"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 5 minute: 0 second: 0 nanosecond: 0 isLeapMonth: false
roundedDate = date.roundDate(toNearest: 5, in: .hours)!
// "12 Jun 2020 at 00:00"
calendar.dateComponents([.nanosecond, .second, .minute, .hour, .day, .month], from: roundedDate)
// month: 6 day: 12 hour: 0 minute: 0 second: 0 nanosecond: 0 isLeapMonth: false
答案 13 :(得分:1)
我知道这是一个较老的线程,但由于最近有更多的答案,我将分享用于将NSDate四舍五入到最接近的5分钟间隔的实用程序方法。
我使用它来填充UITextField,当它变为FirstResponder时,使用当前的UIDatePicker日期。当UIDatePicker配置了1分钟以外的时间时,您不能只使用[NSDate date]。我的配置间隔为5分钟。
+ (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];
答案 14 :(得分:1)
我一直在寻找这个,但是使用上面的例子给了我0001年的日期。
这是我的替代方案,与smorgan的更优雅的mod建议合并,但要注意我还没有进行泄密测试:
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)];
答案 15 :(得分:0)
- (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];
}
答案 16 :(得分:0)
这是一个通用的解决方案,它向上舍入到最近的输入'mins':
+(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];
}
答案 17 :(得分:0)
我不确定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;
}
答案 18 :(得分:-1)
更短......限制为秒:
let seconds = ceil(Date().timeIntervalSinceReferenceDate/300.0)*300.0
let roundedDate = Date(timeIntervalSinceReferenceDate: seconds)