web-dev-qa-db-fra.com

Comment décomposer un NSTimeInterval en année, mois, jours, heures, minutes et secondes sur iPhone?

J'ai un intervalle de temps qui s'étend sur des années et je veux toutes les composantes de temps de l'année à quelques secondes.

Ma première pensée est de diviser l'intervalle de temps en secondes dans une année, de le soustraire d'un total cumulé de secondes, de le diviser en secondes dans un mois, de le soustraire du total cumulé, etc.

Cela semble juste compliqué et j'ai lu que chaque fois que vous faites quelque chose qui semble compliqué, il y a probablement une méthode intégrée.

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);

Lorsque je règle le sélecteur de date sur une date de 1 an et 1 jour dans le passé, j'obtiens:

1 ans 1 mois 1 jours 16 heures 0 minutes 20 secondes

qui est de 1 mois et 16 heures de repos. Si j'ai réglé le sélecteur de date sur 1 jour dans le passé, je suis hors tension du même montant.

pdate: J'ai une application qui calcule votre âge en années, compte tenu de votre anniversaire (défini à partir d'un UIDatePicker), mais il était souvent désactivé. Cela prouve qu'il y avait une inexactitude, mais je ne peux pas comprendre d'où elle vient, pouvez-vous?

55
willc2

Brève description

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

  2. Utilisez la méthode NSCalendar:

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

    • "Renvoie, en tant qu'objet NSDateComponents utilisant les composants spécifiés, la différence entre deux dates fournies". (D'après la documentation de l'API).

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

  4. Obtenez vos devis 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]);
100
Albaregar

Ce code est conscient des heures d'été 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]);
17
vikingosegundo

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

Référence SDK

4
JBRWilkinson

Depuis iOS8 et supérieur, vous pouvez utiliser NSDateComponentsFormatter

Il a des méthodes pour convertir la différence de temps dans une chaîne formatée conviviale.

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

4
Omkar

Ou il y a ma méthode de classe. Il ne gère pas les années, mais cela pourrait facilement être ajouté, bien qu'il soit préférable pour les petits timelaps comme les jours, les heures et les minutes. Il prend en compte les pluriels et ne montre que 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;
}
3
Nicolas Manzini

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 ici est que vous devez ajuster le fuseau horaire.

3
doogilasovich
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.
...
2
Alex Reynolds

Voici 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
Alex Reynolds
- (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;

}
0
taus-iDeveloper