web-dev-qa-db-fra.com

Comment s'attaquer à l'heure d'été en utilisant TimeZone en Java

Je dois imprimer l'heure EST dans mon application Java. J'avais réglé le fuseau horaire sur EST en utilisant:

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));

Mais lorsque l'heure d'été est suivie dans ce fuseau horaire, mon code n'imprime pas l'heure correcte (il affiche 1 heure de moins).

Comment faire en sorte que le code fonctionne toujours pour lire l’heure exacte, que l’on observe ou non l’heure avancée?

PS: J'ai essayé de régler le fuseau horaire sur EDT, mais cela ne résout pas le problème.

54
Surya Chandra

C'est le problème pour commencer:

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));

Les abréviations de 3 lettres doivent être évitées de tout cœur en faveur des identifiants de zone TZDB. EST correspond à l'Est Standard Heure - et Standard l'heure n'observe jamais l'heure d'été; ce n'est pas vraiment un nom de fuseau horaire complet. C'est le nom utilisé pour part d'un fuseau horaire. (Malheureusement, je n'ai pas trouvé un bon terme pour ce concept de "demi-fuseau horaire".)

Vous voulez un complet nom du fuseau horaire. Par exemple, America/New_York est situé dans le fuseau horaire est:

TimeZone zone = TimeZone.getTimeZone("America/New_York");
DateFormat format = DateFormat.getDateTimeInstance();
format.setTimeZone(zone);

System.out.println(format.format(new Date()));
104
Jon Skeet

Les autres réponses sont correctes, notamment celle de Jon Skeet , mais obsolète. 

Java.time

Ces anciennes classes date-heure ont été remplacées par le framework Java.time intégré à Java 8 et versions ultérieures.

Si vous voulez simplement l'heure actuelle en UTC, utilisez la classe Instant.

Instant now = Instant.now();

EST n'est pas un fuseau horaire, comme expliqué dans le réponse correcte de Jon Skeet . De tels codes à 3 ou 4 lettres ne sont ni normalisés ni uniques et accentuent la confusion entourant l'heure d'été (DST). Utilisez un nom de fuseau horaire approprié dans le format "continent/région".

Vous vouliez peut-être parler de l'heure normale de l'Est sur la côte est de l'Amérique du Nord? Ou l'heure normale d'Egypte? Ou l'heure standard européenne?

ZoneId zoneId = ZoneId.of( "America/New_York" );
ZoneId zoneId = ZoneId.of( "Africa/Cairo" );
ZoneId zoneId = ZoneId.of( "Europe/Lisbon" );

Utilisez l'un de ces objets ZoneId pour que le moment actuel soit ajusté à un fuseau horaire particulier afin de générer un objet ZonedDateTime .

ZonedDateTime zdt = ZonedDateTime.now( zoneId ) ;

Ajustez ce ZonedDateTime dans un fuseau horaire différent en produisant un autre objet ZonedDateTime à partir du premier. Le framework Java.time utilise objets immuables plutôt que de changer (de muter) les objets existants.

ZonedDateTime zdtGuam = zdt.withZoneSameInstant( ZoneId.of( "Pacific/Guam" ) ) ;
14
Basil Bourque

Au lieu d'entrer "EST" pour le fuseau horaire, vous pouvez entrer "EST5EDT" en tant que tel. Comme vous l'avez noté, "EDT" ne fonctionne pas. Cela expliquera le problème de l'heure avancée. La ligne de code ressemble à ceci:

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST5EDT"));
2
Brian N
private static Long DateTimeNowTicks(){
    long TICKS_AT_Epoch = 621355968000000000L;
    TimeZone timeZone = Calendar.getInstance().getTimeZone();
    int offs = timeZone.getRawOffset();
    if (timeZone.inDaylightTime(new Date()))
        offs += 60 * 60 * 1000;
    return (System.currentTimeMillis() + offs) * 10000 + TICKS_AT_Epoch;
}
0
fartwhif

Selon cette réponse :

TimeZone tz = TimeZone.getTimeZone("EST");
boolean inDs = tz.inDaylightTime(new Date());
0
Daniil Shevelev
public static float calculateTimeZone(String deviceTimeZone) {
    float ONE_HOUR_MILLIS = 60 * 60 * 1000;

    // Current timezone and date
    TimeZone timeZone = TimeZone.getTimeZone(deviceTimeZone);
    Date nowDate = new Date();
    float offsetFromUtc = timeZone.getOffset(nowDate.getTime()) / ONE_HOUR_MILLIS;

    // Daylight Saving time
    if (timeZone.useDaylightTime()) {
        // DST is used
        // I'm saving this is preferences for later use

        // save the offset value to use it later
        float dstOffset = timeZone.getDSTSavings() / ONE_HOUR_MILLIS;
        // DstOffsetValue = dstOffset
        // I'm saving this is preferences for later use
        // save that now we are in DST mode
        if (timeZone.inDaylightTime(nowDate)) {
            Log.e(Utility.class.getName(), "in Daylight Time");
            return -(ONE_HOUR_MILLIS * dstOffset);
        } else {
            Log.e(Utility.class.getName(), "not in Daylight Time");
            return 0;
        }
    } else
        return 0;
}
0
Jackson