2009-01-25 7 views
1

J'ai créé le code suivant pour calculer la durée entre deux horodatages qui peut venir en deux formats différents:Java: calcul de durée

public class dummyTime { 
public static void main(String[] args) { 
    try { 
     convertDuration("2008-01-01 01:00 pm - 01:56 pm"); 
     convertDuration("2008-01-01 8:30 pm - 2008-01-02 09:30 am"); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

private static String convertDuration(String time) throws Exception { 
    String ts[] = time.split(" - "); 
    SimpleDateFormat formatNew = new SimpleDateFormat("HH:mm"); 
    Date beg, end; 
    String duration = null; 

    beg = getDateTime(ts[0]); 
    end = getDateTime(ts[1], beg); 

    duration = formatNew.format(end.getTime() - beg.getTime()); 
    System.out.println(duration + " /// " + time + " /// " + beg + " /// " 
      + end); 

    return duration; 
} 

private static Date getDateTime(String dateTime) throws ParseException { 
    DateFormat formatOldDateTime = new SimpleDateFormat(
      "yyyy-MM-dd hh:mm aa"); 
    DateFormat formatOldTimeOnly = new SimpleDateFormat("hh:mm aa"); 
    Date date = null; 

    try { 
     date = formatOldDateTime.parse(dateTime); 
    } catch (ParseException e) { 
     date = formatOldTimeOnly.parse(dateTime); 
    } 

    return date; 
} 

private static Date getDateTime(String dateTime, Date orig) 
     throws ParseException { 
    Date end = getDateTime(dateTime); 

    if (end.getYear() == 70) { 
     end.setYear(orig.getYear()); 
     end.setMonth(orig.getMonth()); 
     end.setDate(orig.getDate()); 
    } 

    return end; 
} 
} 

La sortie génère est:

01:56 /// 2008-01-01 01:00 pm - 01:56 pm /// Tue Jan 01 13:00:00 CET 2008 /// Tue Jan 01 13:56:00 CET 2008 
14:00 /// 2008-01-01 8:30 pm - 2008-01-02 09:30 am /// Tue Jan 01 20:30:00 CET 2008 /// Wed Jan 02 09:30:00 CET 2008 

Mes questions sont :

  1. Pourquoi les résultats sont toujours faux (toujours + 1h)?
  2. Qu'est-ce qu'un meilleur façon d'identifier les horodatages sans jour? == 70 ne semble pas bon et le getDay & les fonctions de setDay sont désapprouvé aussi.

Beaucoup de mercis, ce numéro m'a rendu fou pendant plusieurs heures.

+0

Les entrées sont incohérentes. La plupart des valeurs de l'heure ont un zéro de remplissage, mais '20:30' omet ceci. –

Répondre

2
  1. À mon ordinateur cela est hors de 2 heures, parce que je suis à GMT + 2, et vous êtes probablement à GMT + 1. Notez que formatNew.format(end.getTime() - beg.getTime()); reçoit la date, c'est-à-dire traite vos 56 minutes en tant que 1970-01-01-00: 56: 00 GMT + 1. Pour résoudre ce problème rapidement, appelez le formatNew.setTimeZone(TimeZone.getTimeZone("GMT"));

  2. Pour le 2ème élément, vous pouvez vérifier si le format aaaa-MM-jj a échoué (vous avez rencontré une erreur d'analyse), et vous savez ainsi qu'il n'y a pas d'année.

+0

Heure JODA http://joda-time.sourceforge.net/ – basszero

4

Vous mettez en forme l'heure et non le nombre d'heures et de minutes. Comme vous êtes dans le fuseau horaire CET [heure d'Europe centrale] en hiver, c'est-à-dire une heure différente de l'heure UTC ("GMT"). Vous souhaitez probablement utiliser Calendar au lieu de Date. Ou Joda-Time.

1

Réponse simple: il est inapproprié d'utiliser SimpleDateFormat pour formater les valeurs qui représentent l'heure sans date. Réponse plus longue: Les valeurs de temps Java sont un compte de millisecondes depuis «l'époque»: minuit, le 1er janvier 1970, UTC. SimpleDateFormat suppose que vous lui attribuez un horodatage valide et applique une conversion localisée à une date et une heure. Je soupçonne que votre région est à une heure de l'heure GMT (Europe continentale), c'est pourquoi vous voyez des résultats à une heure d'arrêt.

Alors que vous pouvez tromper SimpleDateFormat en réglant le fuseau horaire GMT, vous êtes probablement mieux afficher des durées en utilisant les mathématiques explicites:

int duration = 90; 
System.out.printf("%02d:%02d", duration/60, duration % 60); 
0

Tout d'abord, vos chaînes par exemple ne sont pas compatibles: 8:30 pm manque un rembourrage zéro. Je vais supposer que c'est une faute de frappe, et aurait dû être 08:30 pm.

formats de chaîne

indésirables

Par ailleurs, ces formats de chaîne d'entrée ne sont pas souhaitables. - Un bien meilleur moyen est d'utiliser les formats standard ISO 8601. - Les horloges de 12 heures avec AM/PM sont problématiques.Les formats standard utilisent l'horloge 24 heures, avec les heures 0-23.
- La notation standard pour un intervalle est la paire de chaînes date-heure séparées par une barre oblique: 2008-01-01T13:00/2008-01-01T13:56.

Vos chaînes d'entrée présentent un autre problème sérieux: Aucune indication de offset-from-UTC ou de fuseau horaire. Sans décalage ou fuseau horaire, nous devons revenir à l'hypothèse de jours génériques de 24 heures. Cela ne tient pas compte des anomalies telles que l'heure d'été (DST) qui peuvent entraîner des journées de 23 ou 25 heures.

Si vous connaissez le fuseau horaire prévu pour les chaînes entrantes, transmettez-le comme deuxième argument pour obtenir un résultat correct.

java.time

Cette question est assez ancienne. Depuis lors, Java a supplanté les vieilles classes de date-heure (Date, Calendar, etc.) avec les classes java.time modernes. Nous utilisons java.time dans le code ci-dessous.

Exemple classe

Voici une classe complète pour le traitement de ces chaînes comme indiqué dans votre question. Un Duration est produit.

package javatimestuff; 

import java.time.Duration; 
import java.time.LocalDateTime; 
import java.time.LocalTime; 
import java.time.ZoneId; 
import java.time.ZoneOffset; 
import java.time.ZonedDateTime; 
import java.time.format.DateTimeFormatter; 
import java.time.format.DateTimeParseException; 
import java.util.Locale; 

/** 
* 
* @author Basil Bourque 
*/ 
public class DurationProcessor { 

    static final int SHORT = 30; 
    static final int LONG = 41; 

    static final DateTimeFormatter FORMATTER_LOCALDATETIME = DateTimeFormatter.ofPattern ("uuuu-MM-dd hh:mm a"); 
    static final DateTimeFormatter FORMATTER_LOCALTIME = DateTimeFormatter.ofPattern ("hh:mm a"); 

    static public Duration process (String input) { 
     return DurationProcessor.process (input , ZoneOffset.UTC); 
    } 

    static public Duration process (String input , ZoneId zoneId) { 
     Duration d = Duration.ZERO; // Or maybe null. To be generated by the bottom of this code. 

     if (null == input) { 
      // … 
      System.out.println ("ERROR - Passed null argument."); 
      return d; 
     } 
     if (input.length() == 0) { 
      // … 
      System.out.println ("ERROR - Passed empty string as argument."); 
      return d; 
     } 

     String inputModified = input.toUpperCase (Locale.ENGLISH); // Change `am` `pm` to `AM` `PM` for parsing. 

     String[] parts = inputModified.split (" - "); 
     String inputStart = parts[ 0 ]; // A date-time sting. 
     String inputStop = parts[ 1 ]; // Either a date-time string or a time-only string (assume the same date). 

     ZonedDateTime start = null; // To be generated in this block of code. 
     try { 
      LocalDateTime ldt = LocalDateTime.parse (inputStart , DurationProcessor.FORMATTER_LOCALDATETIME); 
      start = ldt.atZone (zoneId); 
     } catch (DateTimeParseException e) { 
      // … 
      System.out.println ("ERROR - The start failed to parse. inputStart: " + inputStart); 
      return d; 
     } 

     ZonedDateTime stop = null; // To be generated in this block of code. 
     switch (input.length()) { 
      case DurationProcessor.SHORT: // Example: "2008-01-01 01:00 pm - 01:56 pm" 
       try { 
        LocalTime stopTime = LocalTime.parse (inputStop , DurationProcessor.FORMATTER_LOCALTIME); 
        stop = ZonedDateTime.of (start.toLocalDate() , stopTime , zoneId); 
       } catch (DateTimeParseException e) { 
        // … 
        System.out.println ("ERROR - The stop time failed to parse."); 
        return d; 
       } 
       break; 
      case DurationProcessor.LONG: // "2008-01-01 8:30 pm - 2008-01-02 09:30 am" 
       try { 
        LocalDateTime ldt = LocalDateTime.parse (inputStop , DurationProcessor.FORMATTER_LOCALDATETIME); 
        stop = ldt.atZone (zoneId); 
       } catch (DateTimeParseException e) { 
        // … 
        System.out.println ("ERROR - The stop date-time failed to parse."); 
        return d; 
       } 
       break; 
      default: 
       // … 
       System.out.println ("ERROR - Input string is of unexpected length: " + input.length()); 
       break; 
     } 

     d = Duration.between (start , stop); 
     return d; 
    } 

    public static void main (String[] args) { 
     // Run with out time zone (assumes UTC). 
     Duration dShort = DurationProcessor.process ("2008-01-01 01:00 pm - 01:56 pm"); 
     System.out.println ("dShort: " + dShort); 

     Duration dLong = DurationProcessor.process ("2008-01-01 08:30 pm - 2008-01-02 09:30 am"); 
     System.out.println ("dLong: " + dLong); 

     // Run with specified time zone. 
     ZoneId z = ZoneId.of ("America/Montreal"); 
     Duration dShortZoned = DurationProcessor.process ("2008-01-01 01:00 pm - 01:56 pm" , z); 
     System.out.println ("dShortZoned: " + dShortZoned); 

     Duration dLongZoned = DurationProcessor.process ("2008-01-01 08:30 pm - 2008-01-02 09:30 am" , z); 
     System.out.println ("dLongZoned: " + dLongZoned); 

    } 
} 

Notez la méthode main dans la classe pour des usages par exemple.

D'abord une paire d'appels sans spécifier de fuseau horaire. Donc UTC et les jours de 24 heures seront utilisés.

Duration dShort = DurationProcessor.process ("2008-01-01 01:00 pm - 01:56 pm"); 
System.out.println ("dShort: " + dShort); 

Duration dLong = DurationProcessor.process ("2008-01-01 08:30 pm - 2008-01-02 09:30 am"); 
System.out.println ("dLong: " + dLong); 

Une autre paire d'appels où nous spécifions le fuseau horaire prévu.

ZoneId z = ZoneId.of ("America/Montreal"); 
Duration dShortZoned = DurationProcessor.process ("2008-01-01 01:00 pm - 01:56 pm" , z); 
System.out.println ("dShortZoned: " + dShortZoned); 

Duration dLongZoned = DurationProcessor.process ("2008-01-01 08:30 pm - 2008-01-02 09:30 am" , z); 
System.out.println ("dLongZoned: " + dLongZoned); 

Code en direct

Voir cette course de classe live code in IdeOne.com.

DSHORT: PT56M

Dlong: PT13H

dShortZoned: PT56M

dLongZoned: PT13H

Comme indiqué ailleurs sur cette page, le format de sortie en utilisant le temps de style de jour tel que 00:56 est ambigu et confus, et devrait être évité. La classe Duration utilise à la place la norme ISO 8601 format for durations. Ci-dessus, nous voyons des résultats de cinquante-six minutes et treize minutes.


A propos java.time

Le cadre java.time est construit en Java 8 et versions ultérieures. Ces classes supplantent les vieilles classes de date-heure legacy gênantes telles que java.util.Date, Calendar, & SimpleDateFormat.Le projet Joda-Time, maintenant en maintenance mode, conseille la migration vers les classes java.time.

Pour en savoir plus, voir le Oracle Tutorial. Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310.

Où obtenir les classes java.time?

  • Java SE 8 et SE 9 et plus tard
    • intégré.
    • Partie de l'API Java standard avec une implémentation groupée.
    • Java 9 ajoute quelques fonctionnalités et corrections mineures.
  • Java SE 6 et SE 7
    • Une grande partie de la fonctionnalité de java.time est de retour à Java 6 Ported .
  • Android

Le projet ThreeTen-Extra java.time étend avec des classes supplémentaires. Ce projet est un terrain d'essai pour d'éventuels ajouts futurs à java.time. Vous pouvez trouver ici des classes utiles telles que Interval, YearWeek, YearQuarter et more.