Forcer le fuseau horaire Java au format GMT / UTC

95

Je dois forcer toutes les opérations liées au temps à GMT / UTC, quel que soit le fuseau horaire défini sur la machine. Un moyen pratique de le faire dans le code?

Pour clarifier, j'utilise l'heure du serveur DB pour toutes les opérations, mais elle est formatée selon le fuseau horaire local.

Merci!

SyBer
la source
En fait, son problème est un sous-ensemble du mien, mais j'ai trouvé une solution.
SyBer
Regardez la question en double et recherchez "Joda" et "DateTimeZone".
Basil Bourque

Réponses:

124

L'OP a répondu à cette question pour modifier le fuseau horaire par défaut pour une seule instance d'une JVM en cours d'exécution, définissez la user.timezonepropriété système:

java -Duser.timezone=GMT ... <main-class>

Si vous devez définir des fuseaux horaires spécifiques lors de la récupération d'objets Date / Heure / Horodatage à partir d'une base de données ResultSet, utilisez la deuxième forme des getXXXméthodes qui prend un Calendarobjet:

Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
ResultSet rs = ...;
while (rs.next()) {
    Date dateValue = rs.getDate("DateColumn", tzCal);
    // Other fields and calculations
}

Ou, en définissant la date dans un PreparedStatement:

Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
PreparedStatement ps = conn.createPreparedStatement("update ...");
ps.setDate("DateColumn", dateValue, tzCal);
// Other assignments
ps.executeUpdate();

Celles-ci garantiront que la valeur stockée dans la base de données est cohérente lorsque la colonne de base de données ne conserve pas les informations de fuseau horaire.

Les classes java.util.Dateet java.sql.Datestockent l'heure réelle (millisecondes) en UTC. Pour les formater lors de la sortie vers un autre fuseau horaire, utilisez SimpleDateFormat. Vous pouvez également associer un fuseau horaire à la valeur à l'aide d'un objet Calendar:

TimeZone tz = TimeZone.getTimeZone("<local-time-zone>");
//...
Date dateValue = rs.getDate("DateColumn");
Calendar calValue = Calendar.getInstance(tz);
calValue.setTime(dateValue);

Référence utile

https://docs.oracle.com/javase/9/troubleshoot/time-zone-settings-jre.htm#JSTGD377

https://confluence.atlassian.com/kb/setting-the-timezone-for-the-java-environment-841187402.html

Kevin Brock
la source
Une chose à noter à propos de la définition du fuseau horaire pour l'ensemble de la JVM est que cela affecte tout, y compris des choses telles que la journalisation. Juste quelque chose à garder à l'esprit si c'est l'effet souhaité.
Hermann Hans
Ouais, ça va. Juste un point à mentionner, que j'ai en fait défini l'utilisateur.timezone directement dans le code, plutôt que via le paramètre -D.
SyBer
6
@SyBer: Cela fonctionne mais vous devez vous assurer que vous définissez ceci avant que toute méthode appelle la méthode TimeZone.getDefault (). Sinon, vous aurez une certaine confusion puisque la valeur par défaut est mise en cache après la première exécution. Cela signifie également que cela pourrait être un problème si vous faites cela dans une API / bibliothèque (cachée de la vue ordinaire) - assurez-vous de documenter fortement ce que vous faites.
Kevin Brock
23

Aussi si vous pouvez définir le fuseau horaire JVM de cette façon

System.setProperty("user.timezone", "EST");

ou -Duser.timezone=GMTdans les arguments JVM.

Développeur MG
la source
System.setProperty("user.timezone" ...)ne fonctionne pas.
wilmol le
9

Vous pouvez changer le fuseau horaire en utilisant TimeZone.setDefault () :

TimeZone.setDefault(TimeZone.getTimeZone("GMT"))
snorbi
la source
Aucune infraction, mais modifier temporairement un état statique global semble être une très mauvaise idée ...
G.Demecki
Vous pourriez, mais vous ne devriez pas. C'est un conseil extrêmement mauvais. L'ensemble de la JVM a son fuseau horaire modifié.
scot
1
Parfois, c'est exactement ce que vous voulez réaliser. Par exemple. J'ai vu des microservices basés sur Java fonctionnant toujours avec UTC pour éviter les problèmes de fuseau horaire. Dans ces systèmes, le backend de la base de données fonctionne également avec UTC, il est donc naturel de "forcer" la JVM à UTC également. Ce qui est important: vous devez savoir ce que vous faites et quelles sont les conséquences ...
snorbi
absolument pas. Si vous voulez que votre système entier soit en UTC (une très bonne idée), réglez le fuseau horaire du système sur UTC et laissez le fuseau horaire Java seul.
scot
3
Sauf si vous avez plusieurs JVM avec des exigences différentes. Nous pouvons discuter pour toujours, mais chaque cas est unique: parfois vous devez définir le fuseau horaire du système entier, parfois vous ne définissez qu'une seule JVM. Soyons heureux que les systèmes d'aujourd'hui soient si flexibles que nous pouvons faire les deux 😉
snorbi
8

pour moi, rapide SimpleDateFormat,

  private static final SimpleDateFormat GMT = new SimpleDateFormat("yyyy-MM-dd");
  private static final SimpleDateFormat SYD = new SimpleDateFormat("yyyy-MM-dd");
  static {
      GMT.setTimeZone(TimeZone.getTimeZone("GMT"));
    SYD.setTimeZone(TimeZone.getTimeZone("Australia/Sydney"));
  }

puis formatez la date avec un fuseau horaire différent.

lwpro2
la source
6

Je récupérerais l'heure de la base de données sous une forme brute (horodatage long ou date de java), puis utiliserais SimpleDateFormat pour le formater, ou Calendar pour le manipuler. Dans les deux cas, vous devez définir le fuseau horaire des objets avant de l'utiliser.

Voir SimpleDateFormat.setTimeZone(..)et Calendar.setTimeZone(..)pour plus de détails

Eyal Schneider
la source
6

tl; dr

String sql = "SELECT CURRENT_TIMESTAMP ;

OffsetDateTime odt = myResultSet.getObject( 1 , OffsetDateTime.class ) ;

Évitez de dépendre du système d'exploitation hôte ou de la JVM pour le fuseau horaire par défaut

Je vous recommande d'écrire tout votre code pour indiquer explicitement le fuseau horaire souhaité / attendu. Vous n'avez pas besoin de dépendre du fuseau horaire par défaut actuel de la JVM. Et sachez que le fuseau horaire par défaut actuel de la JVM peut changer à tout moment pendant l'exécution, avec n'importe quel code dans n'importe quel thread de n'importe quel appel d'application TimeZone.setDefault. Un tel appel affecte immédiatement toutes les applications de cette JVM.

java.time

Certaines des autres réponses étaient correctes, mais sont maintenant dépassées. Les terribles classes de date-heure fournies avec les premières versions de Java étaient défectueuses, écrites par des personnes qui ne comprenaient pas les complexités et les subtilités de la gestion de la date-heure.

Les anciennes classes date-heure ont été remplacées par les classes java.time définies dans JSR 310.

Pour représenter un fuseau horaire, utilisez ZoneId. Pour représenter un décalage par rapport à UTC, utilisez ZoneOffset. Un décalage est simplement un nombre d'heures-minutes-secondes en avant ou en arrière du méridien principal. Un fuseau horaire, c'est bien plus. Un fuseau horaire est une histoire des changements passés, présents et futurs du décalage utilisé par les habitants d'une région particulière.

Je dois forcer toutes les opérations liées au temps à GMT / UTC

Pour un décalage de zéro heure-minute-seconde, utilisez la constante ZoneOffset.UTC.

Instant

Pour capturer le moment actuel en UTC, utilisez un fichier Instant. Cette classe représente un moment en UTC, toujours en UTC par définition.

Instant instant = Instant.now() ;  // Capture current moment in UTC.

ZonedDateTime

Pour voir ce même moment à travers l'heure de l'horloge murale utilisée par les habitants d'une région particulière, ajustez-vous dans un fuseau horaire. Même moment, même point sur la chronologie, heure d'horloge murale différente.

ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

Base de données

Vous mentionnez une base de données.

Pour récupérer un instant de la base de données, votre colonne doit être d'un type de données similaire au standard SQL TIMESTAMP WITH TIME ZONE. Récupérez un objet plutôt qu'une chaîne. Dans JDBC 4.2 et versions ultérieures, nous pouvons échanger des objets java.time avec la base de données. Le OffsetDateTimeest requis par JDBC, tandis que Instant& ZonedDateTimesont facultatifs.

OffsetDateTime odt = myResultSet.getObject(  , OffsetDateTime.class ) ;

Dans la plupart des bases de données et des pilotes, je suppose que vous obtiendrez le moment comme vu en UTC. Mais sinon, vous pouvez régler de deux manières:

  • Extraire un Instant: odt.toInstant()
  • Ajuster à partir du décalage donné à un décalage de zéro: OffsetDateTime odtUtc = odt.withOffsetSameInstant (ZoneOffset.UTC); `.

Tableau des types date-heure en Java (ancien et moderne) et en SQL standard


À propos de java.time

Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciens gênants hérités des classes date-heure tels que java.util.Date, Calendar, et SimpleDateFormat.

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

Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers les classes java.time .

Vous pouvez échanger des objets java.time directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou version ultérieure. Pas besoin de chaînes, pas besoin de java.sql.*classes.

Où obtenir les classes java.time?

Basil Bourque
la source
1

créer une paire client / serveur, de sorte qu'après l'exécution, le serveur client envoie l'heure et la date correctes. Ensuite, le client demande au serveur pm GMT et le serveur renvoie la bonne réponse.

Bertan
la source
1

Utiliser la propriété système:

-Duser.timezone=UTC
Dawid Stępień
la source
5
Pourquoi utiliser cela? Veuillez ajouter quelques explications à votre réponse afin que les autres puissent en tirer des leçons
Nico Haase
1

Exécutez cette ligne dans MySQL pour réinitialiser votre fuseau horaire:

SET @@global.time_zone = '+00:00';
Saeed
la source
0

Sensationnel. Je sais que c'est un fil ancien, mais tout ce que je peux dire, c'est de ne pas appeler TimeZone.setDefault () dans aucun code de niveau utilisateur. Cela définit toujours le fuseau horaire pour l'ensemble de la JVM et est presque toujours une très mauvaise idée. Apprenez à utiliser la bibliothèque joda.time ou la nouvelle classe DateTime de Java 8 qui est très similaire à la bibliothèque joda.time.

Écossais
la source
-6

Si vous souhaitez obtenir l'heure GMT uniquement avec intiger: var currentTime = new Date (); var currentYear = '2010' var currentMonth = 10; var currentDay = '30 'var currentHours = '20' var currentMinutes = '20 'var currentSeconds = '00' var currentMilliseconds = '00 '

currentTime.setFullYear(currentYear);
currentTime.setMonth((currentMonth-1)); //0is January
currentTime.setDate(currentDay);  
currentTime.setHours(currentHours);
currentTime.setMinutes(currentMinutes);
currentTime.setSeconds(currentSeconds);
currentTime.setMilliseconds(currentMilliseconds);

var currentTimezone = currentTime.getTimezoneOffset();
currentTimezone = (currentTimezone/60) * -1;
var gmt ="";
if (currentTimezone !== 0) {
  gmt += currentTimezone > 0 ? ' +' : ' ';
  gmt += currentTimezone;
}
alert(gmt)
Incmos
la source
5
qui ressemble à JavaScript (! = Java) pour moi.
Phil