Question

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

Étant donné que cette date est utilisée pour un WebService qui suppose que l'heure indiquée est toujours en GMT, il me faut convertir le paramètre de l'utilisateur de say (EST) à (GMT). Voici le kicker: L'utilisateur est inconscient de son TZ. Il saisit la date de création qu'il souhaite envoyer sur le serveur Web; il me faut donc:

L'utilisateur entre: le 01/05/2008 18:12
Le paramètre du WS doit être : 01/05/2008 18:12 (GMT)

Je sais que les horodatages sont toujours supposés être à l'heure 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 à l'heure GMT), les heures sont toujours désactivées à moins que l'utilisateur ne en GMT. Qu'est-ce qui me manque?

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 l'ancien code, voici ce que j'ai comme résultat (format court pour faciliter la lecture):

[1 mai 2008 23h12]

Était-ce utile?

La solution 2

Merci à tous d'avoir répondu. Après une enquête plus approfondie, je suis parvenu à la bonne réponse. Comme mentionné par Skip Head, l'horodatage que je recevais de mon application était en cours d'ajustement au fuseau horaire de l'utilisateur. Donc, si l'utilisateur entre 18h12 (HNE), j'aurais 14h12 (GMT). Ce dont j'avais besoin était un moyen d'annuler la conversion afin que le temps entré par l'utilisateur soit celui que j'ai envoyé à la demande WebServer. Voici comment j'ai accompli ceci:

// 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: (entré par l'utilisateur le 01/05/2008 18:12

Fuseau horaire de l'utilisateur actuel: EST
Décalage de courant par rapport à l'heure GMT (en heures): - 4 (normalement -5, sauf si l'heure d'été est ajustée)
TS from ACP: 2008-05-01 14: 12: 00.0
Date de conversion de calendrier à partir de TS à l'aide de GMT et US_EN Locale: 01/05/08 18:12 (GMT)

Autres conseils

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" de Calendar.getInstance () ) dans:

  

DEBUG - le calendrier d'entrée a une date [jeu 23 octobre 12:09:05 EDT 2008]
  DEBUG - offset is -14400000
  DEBUG - Création du rapport GMT avec date [jeu 23 oct 08:09:05 HAE 2008]

12h09h05 GMT est 8h09h05 HAE.

La partie déroutante est que Calendar.getTime () vous renvoie une Date dans votre fuseau horaire actuel et qu'il n'existe pas de méthode pour modifier le fuseau horaire d'un calendrier et faire rouler la date sous-jacente également. Selon le type de paramètre utilisé par votre service Web, vous souhaiterez peut-être simplement obtenir le contrat WS en millisecondes à partir d'une époque.

Vous dites que la date est utilisée en relation avec les services Web, alors je suppose que cette dernière est sérialisée dans une chaîne à un moment donné.

Si tel est le cas, consultez le Méthode setTimeZone de la classe DateFormat. Cela indique quel fuseau horaire 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());

Vous pouvez le résoudre avec temps Joda :

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

Il semble que votre horodatage soit défini sur le fuseau horaire du système d'origine.

Ceci est déconseillé, mais cela devrait fonctionner:

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

La méthode non-obsolète consiste à utiliser

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

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

Méthode de conversion d'un timeZone en un autre (ça marche probablement :)).

/**
 * Adapt calendar to client time zone.
 * @param calendar - adapting calendar
 * @param timeZone - client time zone
 * @return adapt calendar to client time zone
 */
public static Calendar convertCalendar(final Calendar calendar, final TimeZone timeZone) {
    Calendar ret = new GregorianCalendar(timeZone);
    ret.setTimeInMillis(calendar.getTimeInMillis() +
            timeZone.getOffset(calendar.getTimeInMillis()) -
            TimeZone.getDefault().getOffset(calendar.getTimeInMillis()));
    ret.getTime();
    return ret;
}

Les objets

Date et Horodatage sont dépourvus du fuseau horaire: ils représentent un certain nombre de secondes depuis l'époque sans que vous souhaitiez interpréter cet instant sous la forme d'heures et de jours. . Les fuseaux horaires entrent dans l'image uniquement dans les calendriers Gregorian (non directement nécessaires pour cette tâche) et dans SimpleDateFormat , qui nécessitent un décalage de fuseau horaire pour la conversion entre des champs distincts et Date (ou long ) valeurs.

Le problème du PO se situe au tout début de son traitement: l'utilisateur entre des heures ambiguës, qui sont interprétées dans le fuseau horaire local non GMT; à ce stade, la valeur est "6: 12 EST" , qui peut être facilement imprimée sous la forme "11.12 GMT" ou tout autre fuseau horaire mais ne va jamais remplacez par "6.12 GMT" .

Il n’existe aucun moyen de créer le SimpleDateFormat qui analyse "06: 12" comme "HH: MM" (valeur par défaut). le fuseau horaire local) par défaut à UTC; SimpleDateFormat est un peu trop intelligent pour son propre bien.

Cependant, vous pouvez convaincre n'importe quelle instance SimpleDateFormat d'utiliser le bon fuseau horaire si vous le mettez explicitement dans l'entrée: ajoutez simplement une chaîne fixe à l'élément reçu (et validé de manière adéquate) " ; 06: 12 "" pour analyser "06: 12 GMT" comme "HH: MM z" .

Il n'est pas nécessaire de définir explicitement les champs GregorianCalendar , ni de récupérer et d'utiliser des fuseaux horaires et des décalages de l'heure d'été.

Le vrai problème est de séparer les entrées par défaut du fuseau horaire local, les entrées par défaut du temps UTC et les entrées nécessitant une indication de fuseau horaire explicite.

Ce qui a fonctionné pour moi par le passé a été de déterminer le décalage (en millisecondes) entre le fuseau horaire de l'utilisateur et l'heure GMT. Une fois que vous avez le décalage, vous pouvez simplement ajouter / soustraire (en fonction de la direction prise par la conversion) pour obtenir l'heure appropriée dans l'un ou l'autre des fuseaux horaires. Pour ce faire, je définirais généralement le champ millisecondes d'un objet Calendar, mais je suis certain que vous pourriez facilement l'appliquer à un objet timestamp. Voici le code que j'utilise pour obtenir le décalage

int offset = TimeZone.getTimeZone(timezoneId).getRawOffset();

timezoneId est l'identifiant du fuseau horaire de l'utilisateur (tel que EST).

java.time

L’approche moderne utilise les classes java.time qui ont supplanté les anciennes classes date-heure problématiques associées aux versions les plus anciennes de Java.

La classe java.sql.Timestamp est l’une de ces classes héritées. Ne sont plus nécessaires. Utilisez plutôt Instant ou d'autres classes java.time directement avec votre base de données à l'aide de JDBC 4.2 ou version ultérieure.

Le Instantané La classe représente un moment de la chronologie dans UTC avec une résolution de nanosecondes (jusqu'à neuf (9) chiffres d'une fraction décimale).

Instant instant = myResultSet.getObject( … , Instant.class ) ; 

Si vous devez interagir avec un timestamp existant, convertissez immédiatement en java.time via les nouvelles méthodes de conversion ajoutées aux anciennes classes.

Instant instant = myTimestamp.toInstant() ;

Pour vous adapter à un autre fuseau horaire, indiquez-le comme objet ZoneId . Spécifiez un nom de fuseau horaire approprié au format continent / region tels que Amérique / Montréal , Afrique / Casablanca ou Pacifique / Auckland . N'utilisez jamais les pseudo-zones de 3 à 4 lettres telles que EST ou IST car elles ne sont pas , ni normalisées, ni même unique(!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;

Appliquer à Instant pour générer un objet ZonedDateTime .

ZonedDateTime zdt = instant.atZone( z ) ;

Pour générer une chaîne à afficher à l'utilisateur, recherchez DateTimeFormatter dans Stack Overflow pour rechercher de nombreuses discussions et exemples.

Votre question concerne réellement l'inverse, allant de la saisie de données utilisateur aux objets date-heure. Il est généralement préférable de diviser votre saisie de données en deux parties, une date et une heure du jour.

LocalDate ld = LocalDate.parse( dateInput , DateTimeFormatter.ofPattern( "M/d/uuuu" , Locale.US ) ) ;
LocalTime lt = LocalTime.parse( timeInput , DateTimeFormatter.ofPattern( "H:m a" , Locale.US ) ) ;

Votre question n'est pas claire. Voulez-vous interpréter la date et l'heure entrées par l'utilisateur pour être au format UTC? Ou dans un autre fuseau horaire?

Si vous vouliez dire UTC, créez un OffsetDateTime avec un décalage utilisant la constante pour UTC, ZoneOffset.UTC .

OffsetDateTime odt = OffsetDateTime.of( ld , lt , ZoneOffset.UTC ) ;

Si vous vouliez parler d'un autre fuseau horaire, combinez avec un objet de fuseau horaire un ZoneId . Mais quel fuseau horaire? Vous pourriez détecter un fuseau horaire par défaut. Ou, si critique, vous devez confirmer avec l'utilisateur pour être certain de leur intention.

ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;

Pour obtenir un objet plus simple qui est toujours au format UTC par définition, extrayez un Instant .

Instant instant = odt.toInstant() ;

& # 8230; ou & # 8230;

Instant instant = zdt.toInstant() ; 

Envoyer vers votre base de données.

myPreparedStatement.setObject( … , instant ) ;

À propos de java.time

Le java.time Le framework est intégré à Java 8 et supérieur. Ces classes supplantent les anciennes classes de date-heure héritées , telles que java.util.Date , Agenda , & amp ; SimpleDateFormat .

Le projet Joda-Time , maintenant dans mode maintenance , conseille la migration vers le classes java.time .

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

Où obtenir les classes java.time?

Le projet ThreeTen-Extra étend le java.time avec des cours supplémentaires. Ce projet est un terrain d’essai pour d’éventuels ajouts à java.time. Vous pouvez trouver ici des classes utiles telles que Intervalle , YearWeek , < code> YearQuarter , et plus .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top