web-dev-qa-db-fra.com

Comparer deux NSDates et ignorer la composante temporelle

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

71
Magic Bullet 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 .

72
Gregory Pakosz

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.

83
Ed Marty

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.

19
static0886

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

14
James

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
    }
10
Bms270

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;
}
6
jcesarmobile

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.

6
Felixyz

À 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.

3
Arjan

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.

3
Duncan C

Avec Swift 3, selon vos besoins, vous pouvez choisir l'un des deux modèles suivants afin de résoudre votre problème.


#1. Utilisation de la méthode compare(_:to:toGranularity:)

Calendar a une méthode appelée compare(_:​to:​to​Granularity:​) . compare(_:​to:​to​Granularity:​) 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 ordered​Same Si elles sont identiques dans le composant donné et tous les composants plus gros, sinon ordered​Ascending Ou ordered​Descending.

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"
}

# 2. Utilisation de 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
}
2
Imanou Petit

Pour les développeurs codant en Swift 3

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

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  
    }
1
Zaster
int interval = (int)[firstTime timeIntervalSinceDate:secondTime]/(60*60*24);
if (interval!=0){
   //not the same day;
}
1
ChihHao
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

0
Minho Lee

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]];
0
Scott Means

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;
    }
}
0
Kaji