91 votes

Comment gérer les fuseaux horaires du calendrier en utilisant Java ?

J'ai une valeur d'horodatage qui provient de mon application. L'utilisateur peut se trouver dans n'importe quel fuseau horaire local.

Comme cette date est utilisée pour un WebService qui suppose que l'heure donnée est toujours en GMT, j'ai besoin de convertir le paramètre de l'utilisateur de disons (EST) en (GMT). Voici le problème : l'utilisateur n'est pas conscient de son TZ. Il entre la date de création qu'il veut envoyer au WS, donc ce dont j'ai besoin est :

L'utilisateur entre : 5/1/2008 6:12 PM (EST)
Le paramètre du WS doit être : 5/1/2008 6:12 PM (GMT)

Je sais que les TimeStamps sont toujours censés être en GMT par défaut, mais lors de l'envoi du paramètre, même si j'ai créé mon calendrier à partir du TS (qui est censé être en GMT), les heures sont toujours décalées, sauf si l'utilisateur est en GMT. Qu'est-ce qui m'échappe ?

Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
...
private static java.util.Calendar convertTimestampToJavaCalendar(Timestamp ts_) {
  java.util.Calendar cal = java.util.Calendar.getInstance(
      GMT_TIMEZONE, EN_US_LOCALE);
  cal.setTimeInMillis(ts_.getTime());
  return cal;
}

Avec le code précédent, voici ce que j'obtiens comme résultat (format court pour une lecture facile) :

[1er mai 2008 11:12 PM]

61voto

matt b Points 73770
public static Calendar convertToGmt(Calendar cal) {

    Date date = cal.getTime();
    TimeZone tz = cal.getTimeZone();

    log.debug("input calendar has date [" + date + "]");

    //Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT 
    long msFromEpochGmt = date.getTime();

    //gives you the current offset in ms from GMT at the current date
    int offsetFromUTC = tz.getOffset(msFromEpochGmt);
    log.debug("offset is " + offsetFromUTC);

    //create a new calendar in GMT timezone, set to this date and add the offset
    Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    gmtCal.setTime(date);
    gmtCal.add(Calendar.MILLISECOND, offsetFromUTC);

    log.debug("Created GMT cal with date [" + gmtCal.getTime() + "]");

    return gmtCal;
}

Voici le résultat si je passe l'heure actuelle ("12:09:05 EDT") à partir de Calendar.getInstance() ) dans :

DEBUG - le calendrier d'entrée a une date [Thu Oct 23 12:09:05 EDT 2008].
DEBUG - le décalage est de -14400000
DEBUG - Created GMT cal with date [Thu Oct 23 08:09:05 EDT 2008]

12:09:05 GMT est 8:09:05 EDT.

Ce qui est déroutant ici, c'est que Calendar.getTime() vous renvoie un Date dans votre fuseau horaire actuel, et aussi qu'il n'existe aucune méthode pour modifier le fuseau horaire d'un calendrier et faire en sorte que la date sous-jacente soit également modifiée. En fonction du type de paramètre que prend votre service Web, il se peut que vous souhaitiez simplement que le WS traite en termes de millisecondes à partir de l'époque.

29voto

Jorge Valois Points 501

Merci à tous d'avoir répondu. Après une enquête plus approfondie, j'ai trouvé la bonne réponse. Comme l'a mentionné Skip Head, l'horodatage que je recevais de mon application était adapté au fuseau horaire de l'utilisateur. Ainsi, si l'utilisateur saisissait 6:12 PM (EST), j'obtenais 2:12 PM (GMT). J'avais besoin d'un moyen d'annuler la conversion pour que l'heure saisie par l'utilisateur soit l'heure que j'ai envoyée à la requête du serveur Web. Voici comment j'ai procédé :

// Get TimeZone of user
TimeZone currentTimeZone = sc_.getTimeZone();
Calendar currentDt = new GregorianCalendar(currentTimeZone, EN_US_LOCALE);
// Get the Offset from GMT taking DST into account
int gmtOffset = currentTimeZone.getOffset(
    currentDt.get(Calendar.ERA), 
    currentDt.get(Calendar.YEAR), 
    currentDt.get(Calendar.MONTH), 
    currentDt.get(Calendar.DAY_OF_MONTH), 
    currentDt.get(Calendar.DAY_OF_WEEK), 
    currentDt.get(Calendar.MILLISECOND));
// convert to hours
gmtOffset = gmtOffset / (60*60*1000);
System.out.println("Current User's TimeZone: " + currentTimeZone.getID());
System.out.println("Current Offset from GMT (in hrs):" + gmtOffset);
// Get TS from User Input
Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
System.out.println("TS from ACP: " + issuedDate);
// Set TS into Calendar
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
// Adjust for GMT (note the offset negation)
issueDate.add(Calendar.HOUR_OF_DAY, -gmtOffset);
System.out.println("Calendar Date converted from TS using GMT and US_EN Locale: "
    + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
    .format(issueDate.getTime()));

La sortie du code est la suivante : (Utilisateur entré le 5/1/2008 6:12PM (EST)

Fuseau horaire de l'utilisateur actuel : EST
Décalage actuel par rapport à GMT (en heures):-4 (Normalement -5, sauf ajustement de l'heure d'été)
TS de l'ACP : 2008-05-01 14:12:00.0
Date du calendrier convertie de TS en utilisant GMT et US_EN Locale : 5/1/08 6:12 PM (GMT)

20voto

Henrik Points 1579

Vous dites que la date est utilisée en relation avec des services web, je suppose donc qu'elle est sérialisée en une chaîne à un moment donné.

Si c'est le cas, vous devriez jeter un coup d'œil à la page Méthode setTimeZone de la classe DateFormat. Cela détermine le fuseau horaire qui sera utilisé lors de l'impression de l'horodatage.

Un exemple simple :

SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

Calendar cal = Calendar.getInstance();
String timestamp = formatter.format(cal.getTime());

12voto

Vitalii Fedorenko Points 17469

Vous pouvez le résoudre avec Joda Time :

Date utcDate = new Date(timezoneFrom.convertLocalToUTC(date.getTime(), false));
Date localDate = new Date(timezoneTo.convertUTCToLocal(utcDate.getTime()));

Java 8 :

LocalDateTime localDateTime = LocalDateTime.parse("2007-12-03T10:15:30");
ZonedDateTime fromDateTime = localDateTime.atZone(
    ZoneId.of("America/Toronto"));
ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant(
    ZoneId.of("Canada/Newfoundland"));

8voto

Skip Head Points 4062

Il semble que votre TimeStamp soit réglé sur le fuseau horaire du système d'origine.

Cette méthode est obsolète, mais elle devrait fonctionner :

cal.setTimeInMillis(ts_.getTime() - ts_.getTimezoneOffset()) ;

La méthode non-dépréciée est d'utiliser

Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)

mais cela devrait être fait du côté client, puisque ce système sait dans quel fuseau horaire il se trouve.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X