Quelle est la manière la plus efficace/recommandée de comparer deux NSDates? Je voudrais pouvoir voir si les deux dates sont le même jour, quelle que soit l'heure et ont commencé à écrire du code qui utilise la méthode timeIntervalSinceDate: dans la classe NSDate et obtient l'entier de cette valeur divisé par le nombre de secondes en un jour. Cela semble de longue haleine et j'ai l'impression de manquer quelque chose d'évident.
Le code que j'essaie de corriger est:
if (!([key compare:todaysDate] == NSOrderedDescending))
{
todaysDateSection = [eventSectionsArray count] - 1;
}
où key et todaysDate sont des objets NSDate et todaysDate crée en utilisant:
NSDate *todaysDate = [[NSDate alloc] init];
Cordialement
Dave
Vous définissez l'heure de la date sur 00:00:00 avant d'effectuer la comparaison:
unsigned int flags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* components = [calendar components:flags fromDate:date];
NSDate* dateOnly = [calendar dateFromComponents:components];
// ... necessary cleanup
Ensuite, vous pouvez comparer les valeurs de date. Voir aperçu dans la documentation de référence .
Je suis surpris qu'aucune autre réponse n'ait cette option pour obtenir la date de "début de journée" pour les objets:
[[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay startDate:&date1 interval:NULL forDate:date1];
[[NSCalendar currentCalendar] rangeOfUnit:NSCalendarUnitDay startDate:&date2 interval:NULL forDate:date2];
Qui définit date1
et date2
au début de leurs jours respectifs. S'ils sont égaux, ils le sont le même jour.
Ou cette option:
NSUInteger day1 = [[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit: forDate:date1];
NSUInteger day2 = [[NSCalendar currentCalendar] ordinalityOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitEra forDate:date2];
Qui définit day1
et day2
à des valeurs quelque peu arbitraires qui peuvent être comparées. S'ils sont égaux, ils le sont le même jour.
Il y a une nouvelle méthode qui a été introduite dans NSCalendar avec iOS 8 qui rend cela beaucoup plus facile.
- (NSComparisonResult)compareDate:(NSDate *)date1 toDate:(NSDate *)date2 toUnitGranularity:(NSCalendarUnit)unit NS_AVAILABLE(10_9, 8_0);
Vous définissez la granularité des unités qui comptent. Cela ignore toutes les autres unités et limite la comparaison avec celles sélectionnées.
Pour iOS8 et versions ultérieures, vérifier si deux dates se produisent le même jour est aussi simple que:
[[NSCalendar currentCalendar] isDate:date1 inSameDayAsDate:date2]
Voir documentation
Ceci est un raccourci de toutes les réponses:
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
}
J'ai utilisé l'approche Duncan C, j'ai corrigé quelques erreurs qu'il a faites
-(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;
}
J'utilise cette petite méthode util:
-(NSDate*)normalizedDateWithDate:(NSDate*)date
{
NSDateComponents* components = [calendar components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
fromDate: date];
return [calendar_ dateFromComponents:components]; // NB calendar_ must be initialized
}
(Vous devez évidemment avoir un ivar appelé calendar_
contenant un NSCalendar
.)
En utilisant cela, il est facile de vérifier si une date est aujourd'hui comme celle-ci:
[[self normalizeDate:aDate] isEqualToDate:[self normalizeDate:[NSDate date]]];
([NSDate date]
renvoie la date et l'heure actuelles.)
C'est bien sûr très similaire à ce que suggère Gregory. L'inconvénient de cette approche est qu'elle a tendance à créer beaucoup d'objets NSDate
temporaires. Si vous prévoyez de traiter un grand nombre de dates, je recommanderais d'utiliser une autre méthode, comme comparer directement les composants ou travailler avec des objets NSDateComponents
au lieu de NSDates
.
À partir d'iOS 8.0, vous pouvez utiliser:
NSCalendar *calendar = [NSCalendar currentCalendar];
NSComparisonResult dateComparison = [calendar compareDate:[NSDate date] toDate:otherNSDate toUnitGranularity:NSCalendarUnitDay];
Si le résultat est par ex. NSOrderedDescending, otherDate est antérieure au [NSDate date] en termes de jours.
Je ne vois pas cette méthode dans la documentation NSCalendar mais elle se trouve dans Différences d'API iOS 7.1 à iOS 8.
La réponse est plus simple que tout le monde le prétend. NSCalendar a une méthode
components:fromDate:toDate:options
Cette méthode vous permet de calculer la différence entre deux dates en utilisant les unités de votre choix.
Écrivez donc une méthode comme celle-ci:
-(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 la méthode ci-dessus renvoie zéro, les deux dates sont le même jour.
Avec Swift 3, selon vos besoins, vous pouvez choisir l'un des deux modèles suivants afin de résoudre votre problème.
compare(_:to:toGranularity:)
Calendar
a une méthode appelée compare(_:to:toGranularity:)
. compare(_:to:toGranularity:)
a la déclaration suivante:
func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult
Compare les dates données au composant donné, en les signalant
orderedSame
Si elles sont identiques dans le composant donné et tous les composants plus gros, sinonorderedAscending
OuorderedDescending
.
Le code Playground ci-dessous montre chaud pour l'utiliser:
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"
}
dateComponents(_:from:to:)
Calendar
a une méthode appelée dateComponents(_:from:to:)
. dateComponents(_:from:to:)
a la déclaration suivante:
func dateComponents(_ components: Set<Calendar.Component>, from start: Date, to end: Date) -> DateComponents
Renvoie la différence entre deux dates.
Le code Playground ci-dessous montre chaud pour l'utiliser:
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
}
Pour les développeurs codant en Swift 3
if(NSCalendar.current.isDate(selectedDate, inSameDayAs: NSDate() as Date)){
// Do something
}
ma solution a été deux conversions avec 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
}
int interval = (int)[firstTime timeIntervalSinceDate:secondTime]/(60*60*24);
if (interval!=0){
//not the same day;
}
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)];
comparer également l'heure pour corriger la différence d'un jour
C'est un chat particulièrement laid à la peau, mais voici une autre façon de le faire. Je ne dis pas que c'est élégant, mais c'est probablement aussi proche que possible de la prise en charge de la date/heure dans iOS.
bool isToday = [[NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle] isEqualToString:[NSDateFormatter localizedStringFromDate:[NSDate date] dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterNoStyle]];
La documentation concernant NSDate indique que le compare:
et isEqual:
les méthodes effectueront à la fois une comparaison de base et ordonneront les résultats, même si elles tiennent encore compte du temps.
La façon la plus simple de gérer la tâche serait probablement de créer une nouvelle méthode isToday
à l'effet suivant:
- (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;
}
}