2015-07-23 3 views
0

Comment puis-je calculer la première date ultérieure que maintenant d'une date dans le passé en répétant un certain intervalle? Par exemple:Calculer le prochain événement à partir de maintenant donné un NSDate dans le passé et un intervalle de répétition

// Date in the past 
NSDate *pastDate = [NSDate my_dateWithString:@"11/09/2001"]; 

// Time interval 
NSTimeInterval repeatInterval = 14 * 24 * 60 * 60; // 2 weeks 

// Current date 
NSDate *now = [NSDate date]; // 23/07/2015 

// Start calculating next date -> 
NSDate *nextDate = /* interval added to past date */ 
// Is result later than now? 
// No? Calculate again 
// Abracadabra 

NSLog(@"nexDate = %@",nextDate); // 28/07/2015 

Je ne veux pas utiliser itérations. Je suis préoccupé par le calcul d'un cas comme une date de début d'une année dans le passé et un intervalle de répétition d'un jour.

Voici ma solution, mais il a des itérations.

NSDateComponents *twoWeeksDateComponents = [NSDateComponents new]; 
    twoWeeksDateComponents.weekOfMonth = 2; 

    NSDate *date = self.picker.date; 
    NSCalendar *calendar = [NSCalendar currentCalendar]; 

    NSDate *now = [NSDate date]; 
    NSDate *nextDate = date; 
    NSComparisonResult result = [nextDate compare:now]; 
    while (result == NSOrderedAscending) { 
    nextDate = [calendar dateByAddingComponents:kTwoWeeksDateComponents 
             toDate:nextDate 
             options:NSCalendarMatchNextTime]; 
    result = [nextDate compare:now]; 
    } 
    NSLog(@"nexDate = %@",nextDate); // 28/07/2015 

Répondre

3

Vous pouvez en effet le faire sans itération, en utilisant un peu d'arithmétique à la place. Vous recherchez le plus proche multiple d'un facteur f strictement supérieur à une autre valeur n. C'est un peu plus compliqué d'être un calcul de calendrier, mais toujours juste arithmétique (et NSCalendar bien sûr fait tout le lourd soulever pour vous - par exemple, les années bissextiles).

NSDate * pastDate = ...; 
NSDate * now = [NSDate date]; 

Obtenez votre intervalle de répétition mais vous voulez de l'utilisateur et construire un NSDateComponents représenter:

NSCalendarUnit repeatUnit = NSCalendarUnitWeekOfYear; 
NSUInteger repeatInterval = 2; 

NSDateComponents * repeatComps = [NSDateComponents new]; 
[repeatComps setValue:repeatInterval forComponent:repeatUnit]; 

Compte tenu de l'unité de répétition, trouver le montant de cette unité qui se produit entre les deux dates, comme une autre date composants objet:

NSCalendar * cal = [NSCalendar currentCalendar]; 
NSDateComponents * deltaComps = [cal components:repeatUnit 
             fromDate:pastDate 
             toDate:now 
             options:0]; 
NSInteger deltaVal = [deltaComps valueForComponent:repeatUnit]; 

Maintenant vient le bit arithmétique. Arrondissez le delta au multiple le plus proche de l'intervalle de répétition. Ensuite, l'ajout de cette valeur arrondie (en utilisant l'unité appropriée) produira une date égale ou plus tôt que maintenant.

Maintenant, comme jawboxer has pointed out, pour une repeatUnit de NSCalendarUnitMonth, vous obtenez des résultats inattendus si vous calculez la date en utilisant cette valeur et ajoutez ensuite un intervalle plus de répétition (comme la version originale de cette réponse a). Au lieu de cela, ajoutez un au nombre de répétitions immédiatement; alors le calendrier gère correctement les incréments de mois.

NSInteger repeatsJustPastNow = 1 + deltaVal - (deltaVal % repeatInterval); 

NSDateComponents * toNowComps = [NSDateComponents new]; 
[toNowComps setValue:repeatsJustPastNow 
     forComponent:repeatUnit]; 

NSDate * nextDate; 
nextDate = [cal dateByAddingComponents:toNowComps 
           toDate:pastDate 
           options:0]; 

Jawboxer's Swift version of this corrected code is on Github.

+0

fini par moi affichage avant de voir le vôtre ... serait-il OK pour utiliser le mien (il semble toujours revenir la bonne date, conserve le temps et travaille sur des intervalles fous comme 1 semaine + 1 jour + 3 heures + 4 minutes)? Ou devrais-je passer par des composants de date plutôt que de simplement faire des calculs sur l'intervalle? – Metabble

+1

Votre version ne peut pas faire face très facilement avec des unités répétées de mois ou d'années. Essayez d'utiliser une date de début de 2015-03-01 et un intervalle de 1 an, par exemple. 'NSCalendar' représente le jour bissextile de 2016. –

+1

' NSDateComponents' peut également gérer des intervalles composés. –

1

EDIT: Comme le souligne Josh ce ne tient pas compte des années bissextiles. Il s'agit donc d'un bon exemple de mise en œuvre qui peut sembler fonctionner sur des tests à court terme mais qui comporte des lacunes. Il sera également désactivé pendant quelques heures dans le journal car dateFromString: compensera le fuseau horaire alors que le journal l'enregistrera comme GMT.


NSDateFormatter* format = [[NSDateFormatter alloc] init]; 
format.dateFormat = @"yyyy-MM-dd 'at' HH:mm"; 
NSDate *pastDate = [format dateFromString: @"2001-09-11 at 00:00"]; 

NSTimeInterval repeatInterval = 14 * 24 * 60 * 60; // 2 weeks 

NSDate *nowDate = [NSDate date]; 

NSTimeInterval timeFromPastDate = [nowDate timeIntervalSinceDate: pastDate]; 
NSTimeInterval modulus = timeFromPastDate/repeatInterval; 
modulus -= floor(modulus); 

NSDate *nextDate = [NSDate dateWithTimeInterval: repeatInterval * (1 - modulus) sinceDate: nowDate]; 

NSLog(@"nexDate = %@",nextDate); // 28th/July/2015, if current date is 23rd/July/2015 
0

J'ai fait une autre solution rapide.

NSDate *date = ...; 

    static NSTimeInterval k2WeeksInterval = 14 * 24 * 60 * 60.0; 

    NSTimeInterval timeInterval = -date.timeIntervalSinceNow; 

    double repeats = ceil(timeInterval/k2WeeksInterval); 

    NSCalendar *calendar = [NSCalendar currentCalendar]; 

    NSDateComponents *components = [NSDateComponents new]; 
    components.weekOfYear = repeats * 2; 

    NSDate *nextDate = [calendar dateByAddingComponents:components 
               toDate:date 
               options:NSCalendarMatchNextTime]; 
+0

[Comme la réponse de Metabble] (http://stackoverflow.com/questions/31596749/calculate-the-next-event-from-now-given-an-nsdate-in-the-past-and-a-repeat-inter # comment51149875_31598663), cela ne gère pas particulièrement les mois ou les années. –

+0

@JoshCaswell, je suppose que cela fonctionnera bien, parce que NSTimeInterval est une valeur double et le calcul de la quantité de répétitions me donne un vrai résultat, mais les calculs sur l'année bissextile NSCalendar calculera pour moi. :) –

+0

Besoin de tests, mais votre réponse est exactement ce dont j'ai besoin, merci :) P.S. Désolé pour mon mauvais anglais. –