2009-08-06 8 views
54

J'ai un intervalle de temps qui s'étend sur des années et je veux tous les composants de temps de l'année à quelques secondes. Ma première pensée est de diviser l'intervalle de temps par secondes dans une année, soustraire cela d'un total cumulé de secondes, diviser cela par secondes dans un mois, soustraire cela du total cumulé et ainsi de suite. Cela semble juste alambiqué et j'ai lu que lorsque vous faites quelque chose qui semble alambiquée, il y a probablement une méthode intégrée.Comment décomposer un NSTimeInterval en année, mois, jours, heures, minutes et secondes sur iPhone?

Y a-t-il?

J'ai intégré la 2ème méthode d'Alex dans mon code.

C'est dans une méthode appelée par un UIDatePicker dans mon interface.

NSDate *now = [NSDate date]; 
NSDate *then = self.datePicker.date; 
NSTimeInterval howLong = [now timeIntervalSinceDate:then]; 

NSDate *date = [NSDate dateWithTimeIntervalSince1970:howLong]; 
NSString *dateStr = [date description]; 
const char *dateStrPtr = [dateStr UTF8String]; 
int year, month, day, hour, minute, sec; 

sscanf(dateStrPtr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minute, &sec); 
year -= 1970; 

NSLog(@"%d years\n%d months\n%d days\n%d hours\n%d minutes\n%d seconds", year, month, day, hour, minute, sec); 

Quand je mets le sélecteur de date à une date 1 an et 1 jour dans le passé, je reçois:

1 année 1 mois 1 jours 16 heures 0 minutes 20 secondes

qui est de 1 mois et 16 heures de repos. Si je définis le sélecteur de date sur 1 jour dans le passé, je suis absent du même montant.

Mise à jour: J'ai une application qui calcule votre âge en années, compte tenu de votre anniversaire (mis à partir d'un UIDatePicker), mais il était souvent éteint. Cela prouve qu'il y avait une inexactitude, mais je n'arrive pas à comprendre d'où ça vient, n'est-ce pas?

+0

Si vous réglez le sélecteur de date il y a un jour, vous êtes hors d'un mois et seize heures ? –

+0

oui, un mois et seize heures de repos. – willc2

+0

Si le résultat est toujours un mois et seize heures d'inactivité, quelle que soit la valeur que vous choisissez, soit vous soustrayez cela des variables, soit vous changez, soit vous devez évaluer d'où vient cette erreur. Ma conjecture est que quelque chose ne va pas avec votre intervalle, si vous êtes toujours de ce montant. –

Répondre

99

Brève description

  1. Juste une autre approche pour compléter la réponse de JBRWilkinson, mais en ajoutant un code . Il peut également offrir une solution au commentaire d'Alex Reynolds.

  2. Utilisation procédé NSCalendar:

    • (NSDateComponents *)components:(NSUInteger)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSUInteger)opts

    • "retour, comme un NSDateComponents objet en utilisant les composants indiqués, la différence entre les deux dates fournies". (De la documentation de l'API).

  3. Créez 2 NSDate dont la différence est NSTimeInterval que vous voulez décomposer.(Si votre NSTimeInterval vient de comparer 2 NSDate vous n'avez pas besoin de faire cette étape, et vous n'avez même pas besoin de NSTimeInterval, appliquez juste les dates à la méthode NSCalendar).

  4. Obtenez vos citations de NSDateComponents

Exemple de code

// The time interval 
NSTimeInterval theTimeInterval = ...; 

// Get the system calendar 
NSCalendar *sysCalendar = [NSCalendar currentCalendar]; 

// Create the NSDates 
NSDate *date1 = [[NSDate alloc] init]; 
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:theTimeInterval sinceDate:date1]; 

// Get conversion to months, days, hours, minutes 
NSCalendarUnit unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit; 

NSDateComponents *breakdownInfo = [sysCalendar components:unitFlags fromDate:date1 toDate:date2 options:0]; 
NSLog(@"Break down: %i min : %i hours : %i days : %i months", [breakdownInfo minute], [breakdownInfo hour], [breakdownInfo day], [breakdownInfo month]); 
+0

Le code de travail réel, par opposition au texte, est apprécié. – willc2

+0

Merci monsieur. – Martin

+5

cette méthode a un problème de passer les changements de l'heure d'été, vous rencontrerez un problème d'une heure directement. – hokkuk

4

Convertissez votre intervalle en NSDate en utilisant +dateWithIntervalSince1970, récupérez les composants de date à l'aide de la méthode -componentsFromDate de NSCalendar.

SDK Reference

+4

+1. Pour être un peu plus bavard pour l'OP, vous ne pouvez pas convertir précisément un intervalle de temps à ces composants, vous devez savoir d'où vous venez (donc dateWithIntervalFrom1970), parce que par exemple 29 jours sont un mois si vous êtes en février 1er (sauf sur les années bisextiles bien sûr) mais pas si vous êtes au 1er janvier ... – fraca7

2
NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval]; 

// format: YYYY-MM-DD HH:MM:SS ±HHMM 
NSString *dateStr = [date description]; 
NSRange range; 

// year 
range.location = 0; 
range.length = 4; 
NSString *yearStr = [dateStr substringWithRange:range]; 
int year = [yearStr intValue] - 1970; 

// month 
range.location = 5; 
range.length = 2; 
NSString *monthStr = [dateStr substringWithRange:range]; 
int month = [monthStr intValue]; 

// day, etc. 
... 
0

est ici une autre possibilité, un peu plus propre:

NSDate *date = [NSDate dateWithTimeIntervalSince1970:timeInterval]; 
NSString *dateStr = [date description]; 
const char *dateStrPtr = [dateStr UTF8String]; 

// format: YYYY-MM-DD HH:MM:SS ±HHMM 
int year, month, day, hour, minutes, seconds; 
sscanf(dateStrPtr, "%d-%d-%d %d:%d:%d", &year, &month, &day, &hour, &minutes, &seconds); 
year -= 1970; 
+0

Pour un intervalle de temps de 1 seconde (il y a une seconde), renvoie "année -1, mois 12, jour 31, heure 16, minute 0, seconde 1 ". – willc2

+0

Cela ressemble à un bug. –

3

Cela fonctionne pour moi:

float *lenghInSeconds = 2345.234513; 
    NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:lenghInSeconds]; 
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 


    [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]]; 

    [formatter setDateFormat:@"HH:mm:ss"]; 
    NSLog(@"%@", [formatter stringFromDate:date]); 
    [formatter release]; 

La principale différence est ici que vous devez régler pour le fuseau horaire.

+1

+1 pour ne pas oublier le fuseau horaire –

+3

-1 pour avoir oublié quoi que ce soit au-delà de 24 heures va échouer – Jonny

3

Ou il y a ma méthode de classe. Il ne gère pas les années, mais cela pourrait facilement être ajouté, même si c'est mieux pour les petits timelaps comme les jours, les heures et les minutes. Il prend en compte pluriels et montre seulement ce qui est nécessaire:

+(NSString *)TimeRemainingUntilDate:(NSDate *)date { 

    NSTimeInterval interval = [date timeIntervalSinceNow]; 
    NSString * timeRemaining = nil; 

    if (interval > 0) { 

     div_t d = div(interval, 86400); 
     int day = d.quot; 
     div_t h = div(d.rem, 3600); 
     int hour = h.quot; 
     div_t m = div(h.rem, 60); 
     int min = m.quot; 

     NSString * nbday = nil; 
     if(day > 1) 
      nbday = @"days"; 
     else if(day == 1) 
      nbday = @"day"; 
     else 
      nbday = @""; 
     NSString * nbhour = nil; 
     if(hour > 1) 
      nbhour = @"hours"; 
     else if (hour == 1) 
      nbhour = @"hour"; 
     else 
      nbhour = @""; 
     NSString * nbmin = nil; 
     if(min > 1) 
      nbmin = @"mins"; 
     else 
      nbmin = @"min"; 

     timeRemaining = [NSString stringWithFormat:@"%@%@ %@%@ %@%@",day ? [NSNumber numberWithInt:day] : @"",nbday,hour ? [NSNumber numberWithInt:hour] : @"",nbhour,min ? [NSNumber numberWithInt:min] : @"00",nbmin]; 
    } 
    else 
     timeRemaining = @"Over"; 

    return timeRemaining; 
} 
0
- (NSString *)convertTimeFromSeconds:(NSString *)seconds { 

    // Return variable. 
    NSString *result = @""; 

    // Int variables for calculation. 
    int secs = [seconds intValue]; 
    int tempHour = 0; 
    int tempMinute = 0; 
    int tempSecond = 0; 

    NSString *hour  = @""; 
    NSString *minute = @""; 
    NSString *second = @""; 

    // Convert the seconds to hours, minutes and seconds. 
    tempHour = secs/3600; 
    tempMinute = secs/60 - tempHour * 60; 
    tempSecond = secs - (tempHour * 3600 + tempMinute * 60); 

    hour = [[NSNumber numberWithInt:tempHour] stringValue]; 
    minute = [[NSNumber numberWithInt:tempMinute] stringValue]; 
    second = [[NSNumber numberWithInt:tempSecond] stringValue]; 

    // Make time look like 00:00:00 and not 0:0:0 
    if (tempHour < 10) { 
     hour = [@"0" stringByAppendingString:hour]; 
    } 

    if (tempMinute < 10) { 
     minute = [@"0" stringByAppendingString:minute]; 
    } 

    if (tempSecond < 10) { 
     second = [@"0" stringByAppendingString:second]; 
    } 

    if (tempHour == 0) { 

     NSLog(@"Result of Time Conversion: %@:%@", minute, second); 
     result = [NSString stringWithFormat:@"%@:%@", minute, second]; 

    } else { 

     NSLog(@"Result of Time Conversion: %@:%@:%@", hour, minute, second); 
     result = [NSString stringWithFormat:@"%@:%@:%@",hour, minute, second]; 

    } 

    return result; 

} 
15

Ce code est conscient des temps d'économie de lumière du jour et d'autres choses désagréables possibles.

NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar]; 
NSDateComponents *components = [gregorianCalendar components: (NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit) 
                fromDate:startDate 
                 toDate:[NSDate date] 
                options:0]; 


NSLog(@"%ld", [components year]); 
NSLog(@"%ld", [components month]); 
NSLog(@"%ld", [components day]); 
NSLog(@"%ld", [components hour]); 
NSLog(@"%ld", [components minute]); 
NSLog(@"%ld", [components second]); 
4

De iOS8 et au-dessus, vous pouvez utiliser NSDateComponentsFormatter

Il a des méthodes pour convertir la différence de temps facile à utiliser chaîne formatée.

NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init]; 
formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleFull; 

NSLog(@"%@", [formatter stringFromTimeInterval:1623452]); 

Cela donne la sortie - 2 semaines, 4 jours, 18 heures, 57 minutes, 32 secondes

Questions connexes