J'ai une chaîne de date en CST (24 heures) et je souhaite la convertir en GMT (12 heures). J'ai une méthode java comme ci-dessous qui fonctionne bien lorsque mon heure système est l'heure de Kolkata. (Système sur lequel j'exécute la méthode java) Mais lorsque mon système est à l'heure de Shanghai, l'heure GMT est incorrecte.

String inputDate = "01-19-2017 06:01 CST";
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
parsedInput = inputFormatter.parse(inputDate);

// parsedInput -> Tue Jan 19 06:01:00 CST 2017   -> When system time is Shanghai time
// parsedInput -> Thu Jan 19 17:31:00 IST 2017   -> When system time is Kolkata time


SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy hh:mm a Z");
TimeZone gmt = TimeZone.getTimeZone("GMT");
formatter.setTimeZone(gmt);
String formattedDate = formatter.format(parsedInput);

// formattedDate -> 01-18-2017 10:01 PM +0000   -> When system time is Shanghai time (Incorrect)
// formattedDate -> 01-19-2017 12:01 PM +0000   -> When system time is Kolkata time
1
Rey Rajesh 20 avril 2017 à 18:08

3 réponses

Meilleure réponse

Évitez les pseudo fuseaux horaires

Les abréviations de fuseau horaire de 3 à 4 lettres telles que CST ne sont pas de fuseaux horaires réels. Ils ne sont pas standardisés. Ils ne sont même pas uniques!

Votre CST peut être "Heure normale de Chine" ou "Heure normale du centre" dans les Amériques, ou peut-être autre chose. Il n'y a aucun moyen de savoir lequel.

Autre exemple: IST peut signifier "heure normale de l'Inde" ou "heure normale de l'Irlande" ou autres.

Décrypter ces pseudo-zones de manière fiable est impossible. La bibliothèque Joda-Time a une sage politique de refuser même d'essayer. Malheureusement, les classes java.time font une estimation lors de l'analyse, mais le résultat peut ne pas être la zone que vous attendez.

N'utilisez donc jamais ces pseudo-zones inutiles. Utilisez un nom de fuseau horaire au format continent/region tel que { {X1}}, Asia/Kolkata, Pacific/Auckland.

Évitez les anciennes classes de date et d'heure

Vous utilisez les anciennes classes de date-heure gênantes qui sont maintenant héritées, supplantées par les classes java.time.

Solution de contournement

Si vous savez que toutes vos entrées sont destinées au même fuseau horaire, supprimez le pseudo-zone et procédez comme un LocalDateTime, appliquez la zone souhaitée en tant que ZoneId pour produire un ZonedDateDate. À partir de là, vous pouvez extraire un Instant pour l'heure UTC.

String input = "01-19-2017 06:01".replace( " " , "T" ) ;  // Insert a “T” to comply with standard ISO 8601 format used by default in java.time.
LocalDateTime ldt = LocalDateTime.parse( input );  // Lacks any concept of time zone or offset-from-UTC. So *not* an actual moment on the timeline.
ZoneId z = ZoneId.of( "America/Chicago" ); // Assuming “CST” meant this zone in North America.
ZonedDateTime zdt = ldt.atZone( z );  // Assign a time zone to determine an actual moment on the timeline. 
Instant instant = zdt.toInstant();  // Extract a value in UTC.

Vous pouvez choisir de voir ce même moment à travers l'objectif de l'horloge murale en Inde.

ZonedDateTime zdtKolkata = zdt.withZoneSameInstant( ZoneId.of( "Asia/Kolkata" ) ) ;

Ne vous fiez jamais à la zone par défaut

Le fuseau horaire par défaut actuel de votre JVM peut être modifié à tout moment par n'importe quel code dans n'importe quel thread de n'importe quelle application s'exécutant dans cette JVM. Comme cela peut changer à tout moment, vous ne pouvez pas vous fier à une certaine valeur.

Lorsque vous omettez l'argument facultatif pour le fuseau horaire, les classes date-heure appliquent implicitement silencieusement le fuseau horaire par défaut actuel. La solution est donc simple: Spécifiez toujours le fuseau horaire attendu / souhaité .

Remarquez comment l'exemple de code ci-dessus spécifie la zone à chaque opportunité.

(Idem pour Locale, au fait. Spécifiez explicitement plutôt que de vous fier à la valeur par défaut actuelle.)

4
Basil Bourque 20 avril 2017 à 19:04

Essayez également de définir TimeZone sur inputFormatter, par exemple:

SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
inputFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));

Si vous ne spécifiez pas le fuseau horaire, il prend en compte la valeur par défaut qui est Asia/Shanghai dans votre cas et donc un résultat incorrect.

Vous pouvez le reproduire dans votre système actuel en remplaçant GMT par Asia/Shanghai dans l'extrait de code ci-dessus

0
Darshan Mehta 20 avril 2017 à 15:18

Votre problème est l'ambiguïté de CST. Dans la plupart des cas, SimpleDateFormat comprend CST comme l'heure normale du centre comme vous vous y attendiez. Mais lorsque votre ordinateur exécute l'heure de Shanghai, cela devient le fuseau horaire du calendrier du formateur, puis il comprend soudainement CST comme l'heure standard de la Chine, la même que l'heure de Shanghai.

Par conséquent, la solution de Darshan Mehta, en définissant le fuseau horaire de inputFormatter sur autre chose que l'heure standard de la Chine, fonctionne sur mon ordinateur. Je ne sais pas pourquoi cela n'a pas fonctionné sur le vôtre (je l'ai réglé sur TimeZone.getTimeZone("America/Chicago") pour correspondre à l'interprétation prévue de CST).

La solution correcte et bonne, cependant, est d'éviter complètement les abréviations de fuseau horaire à trois et quatre lettres. Le vôtre n'est qu'un exemple parmi tant d'autres où ils causent des problèmes. Si vous le pouvez, donnez votre chaîne d'entrée avec un décalage de zone explicite par rapport à UTC, ce n'est jamais ambigu:

    String inputDate = "01-19-2017 06:01 -0600";

Votre code fonctionne désormais comme prévu, quel que soit le paramètre de fuseau horaire de votre ordinateur. Le Z dans la chaîne de modèle que vous avez déjà correspond bien à -0600.

Tout cela étant dit, si vous pouvez utiliser les classes de date et d'heure Java 8, vous pouvez vous faire une faveur en y basculant. J'hésite à les appeler «classes de date et d'heure Java 8» car elles ont également été rétroportées vers Java 6 et 7, donc si vous pouvez vivre avec une dépendance de bibliothèque jusqu'à ce que vous arriviez à Java 8, vous devriez avoir toutes les chances. Les classes les plus récentes sont beaucoup plus conviviales pour les programmeurs et pratiques à utiliser. Un exemple simple pour vous aider à démarrer:

    String inputDate = "01-19-2017 06:01 -0600";
    DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm Z");
    ZonedDateTime parsedInput = ZonedDateTime.parse(inputDate, inputFormatter);

    OffsetDateTime gmtTime = parsedInput.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy hh:mm a Z");
    String formattedDate = gmtTime.format(formatter);
    System.out.println(formattedDate); // prints 01-19-2017 12:01 PM +0000
2
Community 23 mai 2017 à 12:25