Définir et formater le fuseau horaire avec l'objet Calendrier en Java, puis renvoyer un objet Date


J'ai une fonction où je dois saisir la date actuelle, définir un autre fuseau horaire et renvoyer cette date convertie/formatée en tant qu'objet Date. J'ai du code qui fonctionne, cependant, l'objet Date ne définit pas la date nouvellement convertie, il renvoie la date actuelle.

Voici le code:

public static Date getCurrentLocalDateTime() {

    Calendar currentdate = Calendar.getInstance();
    DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    TimeZone obj = TimeZone.getTimeZone("America/Denver");
    formatter.setTimeZone(obj);

    Logger.info("Local:: " + currentdate.getTime());

    String formattedDate = formatter.format(currentdate.getTime());

    Logger.info("America/Denver:: "+ formattedDate);

    Date finalDate = null;
    try {
        finalDate = formatter.parse(formattedDate);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    Logger.info("finalDate:: " + finalDate);

    return finalDate;
}

D'après les exemples que j'ai examinés et essayés, cela devrait fonctionner correctement. L'un des problèmes est que je dois retourner l'objet Date pour qu'il fonctionne avec le courant code.

La sortie ressemble à:

2017-07-03 17:08:24,499 [INFO] from application in application-akka.actor.default-dispatcher-3 -
                Local:: Mon Jul 03 17:08:24 UTC 2017
2017-07-03 17:08:24,501 [INFO] from application in application-akka.actor.default-dispatcher-3 -
                America/Denver:: 2017-07-03 11:08:24
2017-07-03 17:08:24,502 [INFO] from application in application-akka.actor.default-dispatcher-3 -
                finalDate:: Mon Jul 03 17:08:24 UTC 2017

Comme vous pouvez le voir, il formate la date correctement sur le fuseau horaire de la montagne, mais la remet ensuite à l'heure Calendar.


MODIFIER - - - Solution de code:

public static Date getCurrentLocalDateTime() {
    Calendar currentdate = Calendar.getInstance();
    ZonedDateTime converted = currentdate.toInstant().atZone(ZoneId.of("America/Denver"))
            .withZoneSameLocal(ZoneOffset.UTC);
    Date finalDate = Date.from(converted.toInstant());
    return finalDate;
}
Author: Dan, 2017-07-03

1 answers

Un java.util.Date objet n'a pas d'informations de fuseau horaire. Il n'a qu'une valeur long, qui est le nombre de millisecondes de 1970-01-01T00:00:00Z (également connu sous le nom de "époque unix" ou simplement "époque"). Cette valeur est absolument indépendante du fuseau horaire (vous pouvez également dire "c'est en UTC").

Lorsque vous appelez Logger.info("finalDate:: " + finalDate);, il appelle la méthode toString() de java.util.Date, et cette méthode utilise le fuseau horaire par défaut du système dans les coulisses, donnant l'impression que l'objet date lui-même a un fuseau horaire - mais ce n'est pas le cas.

Vérifiez les valeurs de finalDate.getTime() et currentdate.getTimeInMillis(), vous verrez qu'elles sont presque les mêmes - "presque" parce que le SimpleDateFormat n'a pas la fraction de secondes, donc vous perdez la précision en millisecondes (la méthodeformat crée un String sans les millisecondes, et la méthode parse le met à zéro lorsque le champ non présent). Si je change le formateur à ceci, cependant:

// using ".SSS" to don't lose milliseconds when formatting
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

La sortie est:

Local: le: Lun Juil 03 17:34:34 UTC 2017
Amérique / Denver:: 2017-07-03 11:34:34.508
finalDate:: Lun Juil 03 17: 34: 34 UTC 2017

Et les deux finalDate.getTime() et currentdate.getTimeInMillis() auront exactement les mêmes valeurs (Notez que Date.toString() n'imprime pas les millisecondes, donc vous ne pouvez pas savoir quelle est leur valeur - seulement en comparant getTime() les valeurs que vous savez si elles sont les mêmes).

Conclusion: changez simplement votre formateur pour utiliser les millisecondes (.SSS) et l'analyse/formatage fonctionnera. Le le fait qu'il affiche les dates dans un autre fuseau horaire est un détail d'implémentation (la méthodetoString() utilise le fuseau horaire par défaut du système), mais la valeur en millisecondes est correcte.

Si vous voulez obtenir 11h à UTC, vous devez créer un autre formateur et définir son fuseau horaire à UTC:

DateFormat parser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
finalDate = parser.parse(formattedDate);

Alors, le temps de finalDate aura la valeur de 11h à UTC:

FinalDate: le: Lun Juil 03 11:34:34 UTC 2017


Nouvelle API Java Date / Heure

Les anciennes classes (Date, Calendar et SimpleDateFormat) ont beaucoup de problèmes et problèmes de conception de, et ils sont remplacés par la nouvelle Api.

Si vous utilisez Java 8, envisagez d'utiliser le nouveau java.temps API . C'est plus facile, moins buggé et moins sujet aux erreurs que les anciennes API.

Si vous utilisez Java , vous pouvez utiliser le ThreeTen Backport, un excellent backport pour les nouvelles classes date/heure de Java 8. Et pour Android , il y a le ThreeTenABP (en savoir plus sur la façon de l'utiliser ici).

Le code ci-dessous fonctionne pour les deux. La seule différence est les noms des paquets (dans Java 8 est java.time et dans ThreeTen Backport (ou ThreeTenABP d'Android) est org.threeten.bp), mais les classes et les méthodes noms sont les mêmes.

Pour faire ce dont vous avez besoin, vous pouvez utiliser un ZonedDateTime (une date et une heure + un fuseau horaire) et convertir en un autre fuseau horaire en gardant les mêmes valeurs de date/heure:

// current date/time in Denver
ZonedDateTime denverNow = ZonedDateTime.now(ZoneId.of("America/Denver"));
// convert to UTC, but keeping the same date/time values (like 11:34)
ZonedDateTime converted = denverNow.withZoneSameLocal(ZoneOffset.UTC);
System.out.println(converted); // 2017-07-03T11:34:34.508Z

La sortie sera être:

2017-07-03T11:34:34.508 Z

Si vous voulez un format différent, utilisez un DateTimeFormatter:

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // pattern for day/hour
    .appendPattern("EEE MMM dd HH:mm:ss ")
    // UTC offset
    .appendOffset("+HHMM", "UTC")
    // year
    .appendPattern(" yyyy")
    // create formatter
    .toFormatter(Locale.ENGLISH);
System.out.println(fmt.format(converted));

, La sortie sera:

Lun Juil 03 11: 34: 34 UTC 2017


Si vous avez encore besoin d'utiliser java.util.Date, vous pouvez facilement convertir de et vers la nouvelle API.

En Java > = 8:

// convert your Calendar object to ZonedDateTime
converted = currentdate.toInstant()
               .atZone(ZoneId.of("America/Denver"))
               .withZoneSameLocal(ZoneOffset.UTC);
// converted is equals to 2017-07-03T11:34:34.508Z

// from ZonedDateTime to Date and Calendar (date will be 11:34 UTC)
Date d = Date.from(converted.toInstant());
Calendar cal = Calendar.getInstance();
cal.setTime(d);

// to get a Date that corresponds to 11:34 in Denver
Date d = Date.from(converted.withZoneSameLocal(ZoneId.of("America/Denver")).toInstant());
Calendar cal = Calendar.getInstance();
cal.setTime(d);

Dans Java org.threeten.bp.DateTimeUtils:

// convert Calendar to ZonedDateTime 
converted = DateTimeUtils.toInstant(currentdate)
                .atZone(ZoneId.of("America/Denver"))
                .withZoneSameLocal(ZoneOffset.UTC);
// converted is equals to 2017-07-03T11:34:34.508Z

// convert ZonedDateTime to Date (date will be 11:34 UTC)
Date d = DateTimeUtils.toDate(converted.toInstant());
Calendar c = DateTimeUtils.toGregorianCalendar(converted);

// to get a Date that corresponds to 11:34 in Denver
Date d = DateTimeUtils.toDate(converted.withZoneSameLocal(ZoneId.of("America/Denver")).toInstant());
Calendar c = DateTimeUtils.toGregorianCalendar(converted.withZoneSameLocal(ZoneId.of("America/Denver")));
 1
Author: , 2017-07-07 16:01:24