iphone objective-c nsdate

iphone - Comparando dos NSDates e ignorando el componente de tiempo



objective-c (16)

¿Cuál es la forma más eficiente / recomendada de comparar dos NSDates? Me gustaría poder ver si ambas fechas están en el mismo día, independientemente de la hora y haber comenzado a escribir algún código que use el método timeIntervalSinceDate: dentro de la clase NSDate y obtenga el entero de este valor dividido por el número de segundos en un día. Esto parece largo aliento y siento que me falta algo obvio.

El código que intento corregir es:

if (!([key compare:todaysDate] == NSOrderedDescending)) { todaysDateSection = [eventSectionsArray count] - 1; }

donde key y todayysDate son objetos NSDate y todaysDate está creando usando:

NSDate *todaysDate = [[NSDate alloc] init];

Saludos

Dave


Con Swift 3, de acuerdo con sus necesidades, puede elegir uno de los dos patrones siguientes para resolver su problema.

# 1. Usando el método compare(_:to:toGranularity:)

Calendar tiene un método llamado compare(_:​to:​to​Granularity:​) . compare(_:​to:​to​Granularity:​) tiene la siguiente declaración:

func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult

Compara las fechas dadas con el componente dado, y las informa ordered​Same si son las mismas en el componente dado y todos los componentes más grandes, de lo contrario, ya sea ordered​Ascending u ordered​Descending .

El código de Playground a continuación muestra calor para usarlo:

import Foundation let calendar = Calendar.current let date1 = Date() // "Mar 31, 2017, 2:01 PM" let date2 = calendar.date(byAdding: .day, value: -1, to: date1)! // "Mar 30, 2017, 2:01 PM" let date3 = calendar.date(byAdding: .hour, value: 1, to: date1)! // "Mar 31, 2017, 3:01 PM" /* Compare date1 and date2 */ do { let comparisonResult = calendar.compare(date1, to: date2, toGranularity: .day) switch comparisonResult { case ComparisonResult.orderedSame: print("Same day") default: print("Not the same day") } // Prints: "Not the same day" } /* Compare date1 and date3 */ do { let comparisonResult = calendar.compare(date1, to: date3, toGranularity: .day) if case ComparisonResult.orderedSame = comparisonResult { print("Same day") } else { print("Not the same day") } // Prints: "Same day" }

# 2. Usar dateComponents(_:from:to:)

Calendar tiene un método llamado dateComponents(_:from:to:) . dateComponents(_:from:to:) tiene la siguiente declaración:

func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents

Devuelve la diferencia entre dos fechas.

El código de Playground a continuación muestra calor para usarlo:

import Foundation let calendar = Calendar.current let date1 = Date() // "Mar 31, 2017, 2:01 PM" let date2 = calendar.date(byAdding: .day, value: -1, to: date1)! // "Mar 30, 2017, 2:01 PM" let date3 = calendar.date(byAdding: .hour, value: 1, to: date1)! // "Mar 31, 2017, 3:01 PM" /* Compare date1 and date2 */ do { let dateComponents = calendar.dateComponents([.day], from: date1, to: date2) switch dateComponents.day { case let value? where value < 0: print("date2 is before date1") case let value? where value > 0: print("date2 is after date1") case let value? where value == 0: print("date2 equals date1") default: print("Could not compare dates") } // Prints: date2 is before date1 } /* Compare date1 and date3 */ do { let dateComponents = calendar.dateComponents([.day], from: date1, to: date3) switch dateComponents.day { case let value? where value < 0: print("date2 is before date1") case let value? where value > 0: print("date2 is after date1") case let value? where value == 0: print("date2 equals date1") default: print("Could not compare dates") } // Prints: date2 equals date1 }


Desde iOS 8.0 en adelante, puede usar:

NSCalendar *calendar = [NSCalendar currentCalendar]; NSComparisonResult dateComparison = [calendar compareDate:[NSDate date] toDate:otherNSDate toUnitGranularity:NSCalendarUnitDay];

Si el resultado es, por ejemplo, NSOrderedDescending, otherDate es anterior a [NSDate date] en términos de días.

No veo este método en la documentación de NSCalendar, pero está en las diferencias de API de iOS 7.1 a iOS 8.0


Esta es una forma abreviada de todas las respuestas:

NSInteger interval = [[[NSCalendar currentCalendar] components: NSDayCalendarUnit fromDate: date1 toDate: date2 options: 0] day]; if(interval<0){ //date1<date2 }else if (interval>0){ //date2<date1 }else{ //date1=date2 }


Establece la hora en la fecha a 00:00:00 antes de hacer la comparación:

unsigned int flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay; NSCalendar* calendar = [NSCalendar currentCalendar]; NSDateComponents* components = [calendar components:flags fromDate:date]; NSDate* dateOnly = [calendar dateFromComponents:components]; // ... necessary cleanup

Entonces puedes comparar los valores de fecha. Vea la descripción general en la documentación de referencia .


Este es un gato particularmente feo a la piel, pero aquí hay otra manera de hacerlo. No digo que sea elegante, pero probablemente sea lo más parecido posible al soporte de fecha y hora en iOS.

bool isToday = [[NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle] isEqualToString:[NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle]];


Hay un nuevo método que se introdujo en NSCalendar con iOS 8 que hace esto mucho más fácil.

- (NSComparisonResult)compareDate:(NSDate *)date1 toDate:(NSDate *)date2 toUnitGranularity:(NSCalendarUnit)unit NS_AVAILABLE(10_9, 8_0);

Usted establece la granularidad para la (s) unidad (es) que importa (n). Esto ignora todas las demás unidades y la comparación de límites con los seleccionados.


La documentación relacionada con NSDate indica que los métodos compare: e isEqual: realizarán una comparación básica y ordenarán los resultados, aunque todavía tienen en cuenta el factor tiempo.

Probablemente la forma más sencilla de administrar la tarea sería crear un nuevo método de hoy en día para el efecto de lo siguiente:

- (bool)isToday:(NSDate *)otherDate { currentTime = [however current time is retrieved]; // Pardon the bit of pseudo-code if (currentTime < [otherDate timeIntervalSinceNow]) { return YES; } else { return NO; } }


La respuesta es más simple de lo que todos creen. NSCalendar tiene un método

components:fromDate:toDate:options

Ese método te permite calcular la diferencia entre dos fechas usando las unidades que quieras.

Entonces escribe un método como este:

-(NSInteger) daysBetweenDate: (NSDate *firstDate) andDate: (NSDate *secondDate) { NSCalendar *currentCalendar = [NSCalendar currentCalendar]; NSDateComponents components* = [currentCalendar components: NSDayCalendarUnit fromDate: firstDate toDate: secondDate options: 0]; NSInteger days = [components days]; return days; }

Si el método anterior arroja cero, las dos fechas están en el mismo día.


Me sorprende que ninguna otra respuesta tenga esta opción para obtener la fecha de "inicio del día" para los objetos:

[[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay startDate:&date1 interval:NULL forDate:date1]; [[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay startDate:&date2 interval:NULL forDate:date2];

Que establece date1 y date2 al comienzo de sus respectivos días. Si son iguales, están en el mismo día.

O esta opción:

NSUInteger day1 = [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit: forDate:date1]; NSUInteger day2 = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date2];

Lo que establece day1 y day2 a valores algo arbitrarios que se pueden comparar. Si son iguales, están en el mismo día.


Para desarrolladores que codifican en Swift 3

if(NSCalendar.current.isDate(selectedDate, inSameDayAs: NSDate() as Date)){ // Do something }


Para iOS8 y posterior, verificar si dos fechas ocurren en el mismo día es tan simple como:

[[NSCalendar currentCalendar] isDate:date1 inSameDayAsDate:date2]

Ver documentation


Utilicé el enfoque de Duncan C, he corregido algunos errores que cometió

-(NSInteger) daysBetweenDate:(NSDate *)firstDate andDate:(NSDate *)secondDate { NSCalendar *currentCalendar = [NSCalendar currentCalendar]; NSDateComponents *components = [currentCalendar components: NSDayCalendarUnit fromDate: firstDate toDate: secondDate options: 0]; NSInteger days = [components day]; return days; }


Yo uso este pequeño método util:

-(NSDate*)normalizedDateWithDate:(NSDate*)date { NSDateComponents* components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate: date]; return [calendar_ dateFromComponents:components]; // NB calendar_ must be initialized }

(Obviamente necesita tener un ivar llamado calendar_ contiene un NSCalendar ).

Al usar esto, es fácil verificar si una fecha es hoy así:

[[self normalizeDate:aDate] isEqualToDate:[self normalizeDate:[NSDate date]]];

( [NSDate date] devuelve la fecha y la hora actuales.)

Esto es, por supuesto, muy similar a lo que sugiere Gregory. El inconveniente de este enfoque es que tiende a crear muchos objetos NSDate temporales. Si va a procesar muchas fechas, le recomendaría usar algún otro método, como comparar los componentes directamente, o trabajar con objetos NSDateComponents lugar de NSDates .


mi solución fue dos conversiones con NSDateFormatter:

NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init]; [dateFormat setDateFormat:@"yyyyMMdd"]; [dateFormat setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]]; NSDate *today = [NSDate dateWithTimeIntervalSinceNow:0]; NSString *todayString=[dateFormat stringFromDate:today]; NSDate *todayWithoutHour=[dateFormat dateFromString:todayString]; if ([today compare:exprDate] == NSOrderedDescending) { //do }


NSUInteger unit = NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit; NSDateComponents *comp = [cal components:unit fromDate:nowDate toDate:setDate options:0]; NSString *dMonth; dMonth = [NSString stringWithFormat:@"%02ld",comp.month]; NSString *dDay; dDay = [NSString stringWithFormat:@"%02ld",comp.day + (comp.hour > 0 ? 1 : 0)];

compare la hora también para arreglar la diferencia de 1 día


int interval = (int)[firstTime timeIntervalSinceDate:secondTime]/(60*60*24); if (interval!=0){ //not the same day; }