Come impostare il fuso orario di un java.util.Date?


225

Ho analizzato a java.util.Dateda a Stringma sta impostando il fuso orario locale come fuso orario datedell'oggetto.

Il fuso orario non è specificato nel Stringda cui Dateviene analizzato. Voglio impostare un fuso orario specifico datedell'oggetto.

Come lo posso fare?


8
Pur non essendo davvero una risposta alla tua domanda, ho usato Joda Time dopo averlo visto menzionato qui alcune volte. Mi sembra più razionale delle API standard e può fare questo genere di cose abbastanza facilmente.
clstrfsck,

2
@msandiford Al giorno d'oggi, utilizzare le classi java.time anziché Joda-Time. Il progetto Joda-Time è ora in modalità manutenzione , con il team che consiglia la migrazione alle classi java.time . Vedi Tutorial di Oracle .
Basil Bourque,

Risposte:


315

Usa DateFormat. Per esempio,

SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = isoFormat.parse("2010-05-23T09:01:02");

6
se la data viene creata dalla classe Calendar, è possibile impostare il fuso orario per Calendar.
lwpro2,

19
@ lwpro2 quell'affermazione è fuorviante; È possibile impostare il fuso orario per un oggetto Calendar, ma ottenere un oggetto Date da esso utilizzando il metodo getTime () restituirà un oggetto Date con il fuso orario del computer host.
BrDaHa,

Ho problemi ad analizzare il 20/02/15 alle 14:44. Qualcuno può aiutare
MS

@BrDaHa è corretto. È necessario TimeZone.setDefault()prima di chiamare in getTime()modo che il nuovo oggetto data si trovi nel fuso orario desiderato. In JDK 1.8, Calendar.getTime()chiama return new Date(getTimeInMillis());.
jpllosa,

182

Tenere presente che gli java.util.Dateoggetti non contengono alcuna informazione sul fuso orario da soli: non è possibile impostare il fuso orario su un Dateoggetto. L'unica cosa che Datecontiene un oggetto è un numero di millisecondi dall' "epoca" - 1 gennaio 1970, 00:00:00 UTC.

Come mostra ZZ Coder, imposti il ​​fuso orario DateFormatsull'oggetto, per dirgli in quale fuso orario vuoi visualizzare la data e l'ora.


77
Dopo aver cercato su Google, sperimentando ed esaurendo, mi sono reso conto che questa è un'aggiunta precisa e utile alla risposta - e vale la pena evidenziarla: la data contiene solo il valore in millisecondi . Se guardi la fonte, c'è praticamente solo un longcampo chiamato fastTime. Date.toString()in realtà usa a Calendarper interpretare questo millisecondo tempo. Così la stampa di un Daterende appaiono avere un fuso orario (di default), che porta a domande comprensibili su come impostare quel fuso orario.
David Carboni,

3
ci sono informazioni sul fuso orario all'interno degli oggetti Date. Ma potrebbe essere vero, non puoi cambiarlo.
lwpro2

3
@ Iwpro2, Jesper afferma (e sono d'accordo) che l'oggetto Date non memorizza un fuso orario. Se si afferma che il fuso orario è memorizzato all'interno di java.util.Date, fornire un riferimento.
Jim,

6
@Jim, questo è il codice sorgente per java.util.Date. Contiene fastTime come epoca unix e cdate BaseCalendar.Date che viene utilizzato a favore di fastTime, se definito. Quello contiene informazioni sul fuso orario. Lo capisco in modo che un'istanza di Date possa contenere informazioni sul fuso orario, ma potrebbe non esserlo.
eis,

2
@Jim non ho detto che potresti impostarlo, stavo dicendo che contiene queste informazioni. Non vedo alcun modo di impostarlo come utente. Penso che OP sia corretto in quanto sembra essere sempre il fuso orario predefinito utente / sistema.
Eis,

95

tl; dr

... analizzato ... da una stringa ... il fuso orario non è specificato ... Voglio impostare un fuso orario specifico

LocalDateTime.parse( "2018-01-23T01:23:45.123456789" )  // Parse string, lacking an offset-from-UTC and lacking a time zone, as a `LocalDateTime`.
    .atZone( ZoneId.of( "Africa/Tunis" ) )              // Assign the time zone for which you are certain this date-time was intended. Instantiates a `ZonedDateTime` object.

Nessun fuso orario in data juDate

Come indicato nelle altre risposte corrette, un java.util.Date non ha fuso orario . Rappresenta UTC / GMT (nessun offset del fuso orario). Molto confuso perché il suo toStringmetodo applica il fuso orario predefinito della JVM durante la generazione di una rappresentazione String.

Evita il juDate

Per questo e molti altri motivi, dovresti evitare di usare java.util.Date & .Calendar & java.text.SimpleDateFormat integrati. Sono notoriamente fastidiosi.

Utilizzare invece il pacchetto java.time in bundle con Java 8 .

java.time

Le classi java.time possono rappresentare un momento sulla sequenza temporale in tre modi:

  • UTC ( Instant)
  • Con un offset ( OffsetDateTimecon ZoneOffset)
  • Con un fuso orario ( ZonedDateTimecon ZoneId)

Instant

In java.time , il blocco predefinito di base è Instantun momento sulla linea temporale in UTC. Usa gli Instantoggetti per gran parte della tua logica aziendale.

Instant instant = Instant.now();

OffsetDateTime

Applicare un offset da UTC per regolare l' ora dell'orologio a muro di alcune località .

Applicare a ZoneOffsetper ottenere un OffsetDateTime.

ZoneOffset zoneOffset = ZoneOffset.of( "-04:00" );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset );

ZonedDateTime

È meglio applicare un fuso orario , un offset più le regole per la gestione di anomalie come l' ora legale (DST) .

Applicare a ZoneIda Instantper ottenere a ZonedDateTime. Specificare sempre un nome di fuso orario corretto . Non utilizzare mai 3-4 abbreviazioni come ESTo ISTche non sono né uniche né standardizzate.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

LocalDateTime

Se la stringa di input mancava di qualsiasi indicatore di offset o zona, analizzarla come a LocalDateTime.

Se si è certi del fuso orario previsto, assegnare a ZoneIdper produrre a ZonedDateTime. Vedi l'esempio di codice sopra nella sezione tl; dr in alto.

Stringhe formattate

Chiamare il toStringmetodo su una di queste tre classi per generare una stringa che rappresenta il valore data-ora nel formato ISO 8601 standard . La ZonedDateTimeclasse estende il formato standard aggiungendo il nome del fuso orario tra parentesi.

String outputInstant = instant.toString(); // Ex: 2011-12-03T10:15:30Z
String outputOdt = odt.toString(); // Ex: 2007-12-03T10:15:30+01:00
String outputZdt = zdt.toString(); // Ex: 2007-12-03T10:15:30+01:00[Europe/Paris]

Per altri formati usa la DateTimeFormatterclasse. Generalmente è meglio lasciare che quella classe generi formati localizzati usando il linguaggio umano atteso dall'utente e le norme culturali. Oppure puoi specificare un formato particolare.


Tabella di tutti i tipi di data e ora in Java, sia moderni che legacy


Informazioni su java.time

Il framework java.time è integrato in Java 8 e versioni successive. Queste classi soppiantare la vecchia fastidiosi legacy classi data-time come java.util.Date, Calendar, e SimpleDateFormat.

Il progetto Joda-Time , ora in modalità manutenzione , consiglia la migrazione alle classi java.time .

Per saperne di più, consulta il tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .

Puoi scambiare oggetti java.time direttamente con il tuo database. Utilizzare un driver JDBC conforme a JDBC 4.2 o successivo. Nessuna necessità di stringhe, nessuna necessità di java.sql.*classi.

Dove ottenere le classi java.time?

Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un banco di prova per possibili aggiunte future a java.time. Si possono trovare alcune classi utili, come per esempio Interval, YearWeek, YearQuartere altro .


Joda-Time

Mentre Joda-Time è ancora attivamente mantenuto, i suoi produttori ci hanno detto di migrare su java.time non appena è conveniente. Lascio intatta questa sezione come riferimento, ma suggerisco invece di utilizzare la java.timesezione precedente.

In Joda-Time , un oggetto data-ora ( DateTime) conosce veramente il fuso orario assegnato. Ciò significa un offset rispetto a UTC e le regole e la cronologia dell'ora legale (DST) di quel fuso orario e altre anomalie simili.

String input = "2014-01-02T03:04:05";
DateTimeZone timeZone = DateTimeZone.forID( "Asia/Kolkata" );
DateTime dateTimeIndia = new DateTime( input, timeZone );
DateTime dateTimeUtcGmt = dateTimeIndia.withZone( DateTimeZone.UTC );

Chiamare il toStringmetodo per generare una stringa in formato ISO 8601 .

String output = dateTimeIndia.toString();

Joda-Time offre anche funzionalità avanzate per la generazione di tutti i tipi di altri formati di stringa.

Se necessario, è possibile convertire da Joda-Time DateTime a java.util.Date.

Java.util.Date date = dateTimeIndia.toDate();

Cerca StackOverflow per "data joda" per trovare molti altri esempi, alcuni abbastanza dettagliati.


In realtà non è un fuso orario incorporato in un java.util.Date, usato per alcune funzioni interne (vedi commenti su questa risposta). Ma questo fuso orario interno non è esposto come proprietà e non può essere impostato. Questo fuso orario interno non è quello utilizzato dal toStringmetodo per generare una rappresentazione in formato stringa del valore data-ora; invece l'attuale fuso orario predefinito della JVM viene applicato al volo. Quindi, come abbreviazione, spesso diciamo che "juDate non ha fuso orario". Confondere? Sì. Ancora un altro motivo per evitare queste vecchie classi stanche.


3
"No Time Zone in juDate" è sbagliato. Ci sono informazioni sul fuso orario in juDate memorizzate nella sua BaseCalendar.Date cdateproprietà se impostate. Dai un'occhiata al codice sorgente qui . Non è possibile impostare il fuso orario di un oggetto juDate se non modificando il fuso orario predefinito di JVM chiamando TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"));. Quindi, c'è un offset del fuso orario e puoi ottenere l'offset chiamando il metodo deprecato juDate.getTimezoneOffset ()
Thai Bui

3
@blquythai Esatto, hai fatto i compiti. Come ho visto prima quel codice sorgente. C'è un fuso sepolto in là. Ma per tutti gli scopi pratici quel fuso orario viene ignorato. Un java.util.Date funziona senza fuso orario, in effetti essendo in UTC, ignorando quel fuso orario sepolto. Ad eccezione del toStringmetodo che applica il fuso orario predefinito corrente della JVM; ignorando di nuovo il fuso orario sepolto. Quindi, per brevità, diciamo che java.util.Date non ha fuso orario. Come l'arte , è una bugia che dice la verità.
Basil Bourque,

@blquythai Come per la chiamata TimeZone.setDefault, si non è l'impostazione del fuso orario dell'oggetto java.util.Date - l'oggetto Date ignora ancora il suo fuso orario sepolto, agendo efficacemente in UTC. Avresti influenzato il toStringmetodo di Date . L'impostazione predefinita modifica il fuso orario predefinito della JVM, che di solito è impostato sul fuso orario del sistema operativo host. Questa chiamata non è consigliata in quanto influisce su tutto il codice in tutti i thread di tutte le app in esecuzione in quella JVM e lo fa al volo durante l'esecuzione. Essendo scortese e pericoloso, quella chiamata dovrebbe essere considerata solo come ultima risorsa.
Basil Bourque,

5
Questo fuso orario viene utilizzato molto spesso (usato in equals, hashcode, getTime..) Se si dà un'occhiata al equalsmetodo, le chiamate getTime()che le chiamate getTimeImpl(), che chiama normalize()se la cdateproprietà non è normalizzata. Nel normalize()metodo, l'ultima condizione if ricalcola i millisecondi dall'1 / 1/70 in base alle informazioni sul fuso orario memorizzato se il fuso orario di cdateè diverso dal fuso orario dell'ambiente JVM corrente su cui è in esecuzione. (Dai un'occhiata sun.util.calendar.AbstractCalendar getCalendarDate(long millis, CalendarDate date))
Thai Bui

2
@MichalM Per tuo consiglio, qualche tempo fa ho aggiunto una postfazione sulla zona incorporata.
Basil Bourque,

75

È inoltre possibile impostare il fuso orario a livello di JVM

Date date1 = new Date();
System.out.println(date1);

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
// or pass in a command line arg: -Duser.timezone="UTC"

Date date2 = new Date();
System.out.println(date2);

produzione:

Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013

Questo mi ha aiutato. setting timeZone in SDF non ha fatto alcuna differenza
vishnu viswanath il

Penso che sia più affidabile. (+1)
foobar

23
Attenzione: la chiamata TimeZone.setDefaultè piuttosto drastica, poiché influenza l'intera JVM, influenza tutti gli altri oggetti e thread. Vedere questa risposta per i dettagli, comprese ancora più complicazioni se si esegue un SecurityManager. Aggiunta di ulteriori complicazioni: questo comportamento è cambiato in varie versioni di Java, come discusso in questa domanda .
Basil Bourque

Questo imposta un fuso orario comune a tutti i thread generati dopo questa affermazione, giusto?
Jaydev,

Questa è una risposta migliore alla domanda di quella accettata.
francogrex,

10

Se devi lavorare solo con le classi JDK standard puoi usare questo:

/**
 * Converts the given <code>date</code> from the <code>fromTimeZone</code> to the
 * <code>toTimeZone</code>.  Since java.util.Date has does not really store time zome
 * information, this actually converts the date to the date that it would be in the
 * other time zone.
 * @param date
 * @param fromTimeZone
 * @param toTimeZone
 * @return
 */
public static Date convertTimeZone(Date date, TimeZone fromTimeZone, TimeZone toTimeZone)
{
    long fromTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, fromTimeZone);
    long toTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, toTimeZone);

    return new Date(date.getTime() + (toTimeZoneOffset - fromTimeZoneOffset));
}

/**
 * Calculates the offset of the <code>timeZone</code> from UTC, factoring in any
 * additional offset due to the time zone being in daylight savings time as of
 * the given <code>date</code>.
 * @param date
 * @param timeZone
 * @return
 */
private static long getTimeZoneUTCAndDSTOffset(Date date, TimeZone timeZone)
{
    long timeZoneDSTOffset = 0;
    if(timeZone.inDaylightTime(date))
    {
        timeZoneDSTOffset = timeZone.getDSTSavings();
    }

    return timeZone.getRawOffset() + timeZoneDSTOffset;
}

Il merito va a questo post .


1
L'offset dei fusi orari non è sempre costante. In effetti, possono cambiare a causa di ragioni geopolitiche. TimeZone.getDSTSavings () non tiene conto di ciò e restituisce sempre l' offset corrente . Ciò significa che potresti avere una conversione errata nel caso in cui tu abbia a che fare con una data storica con da fromTimeZone / aTimeZone che ha cambiato gli offset da quella data.
andrerobot,

6

java.util.Calendarè il solito modo di gestire i fusi orari usando solo le classi JDK. Apache Commons ha alcune ulteriori alternative / utilità che potrebbero essere utili. Modifica la nota di Spong mi ha ricordato che ho sentito cose davvero positive su Joda-Time (anche se non l'ho usato da solo).


+1 per Joda Time. Sebbene non fornisca alcuna funzionalità aggiuntiva che non è possibile ottenere dall'API Java standard (che ho trovato in ogni caso - felice di essere mostrato in altro modo), Joda Time semplifica alcune attività.
Joshua Hutchison,

2
@JoshuaHutchison Joda-Time ha tonnellate di funzionalità aggiuntive. Esempio: Rappresentazione lassi di tempo con le classi Period, Duratione Interval. Tali campate includono metodi di confronto come contains, abuts, overlape gap. E PeriodFormatterBuilderpuò costruire frasi descrittive come "15 anni e 8 mesi".
Basil Bourque

1

Converti la data in stringa e fallo con SimpleDateFormat.

    SimpleDateFormat readFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    readFormat.setTimeZone(TimeZone.getTimeZone("GMT" + timezoneOffset));
    String dateStr = readFormat.format(date);
    SimpleDateFormat writeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    Date date = writeFormat.parse(dateStr);

1
Le risposte su Stack Overflow dovrebbero avere qualche discussione o spiegazione. Questo sito è pensato per essere più di una libreria di snippet di codice.
Basil Bourque,

grazie @Basil Bourque per il tuo commento. Modifica la risposta
avisper

0

Se qualcuno ne ha mai bisogno, se hai bisogno di convertire un XMLGregorianCalendarfuso orario nel tuo fuso orario corrente da UTC, allora tutto ciò che devi fare è impostare il fuso orario su 0, quindi chiamare toGregorianCalendar()- rimarrà lo stesso fuso orario, ma Datesa come convertirlo in i tuoi, così puoi ottenere i dati da lì.

XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar(
        ((GregorianCalendar)GregorianCalendar.getInstance());
xmlStartTime.setTimezone(0);
GregorianCalendar startCalendar = xmlStartTime.toGregorianCalendar();
Date startDate = startCalendar.getTime();
XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
    .newXMLGregorianCalendar(startCalendar);
xmlStartTime.setHour(startDate.getHours());
xmlStartTime.setDay(startDate.getDate());
xmlStartTime.setMinute(startDate.getMinutes());
xmlStartTime.setMonth(startDate.getMonth()+1);
xmlStartTime.setTimezone(-startDate.getTimezoneOffset());
xmlStartTime.setSecond(startDate.getSeconds());
xmlStartTime.setYear(startDate.getYear() + 1900);
System.out.println(xmlStartTime.toString());

Risultato:

2015-08-26T12:02:27.183Z
2015-08-26T14:02:27.183+02:00

0

Questo codice è stato utile in un'app su cui sto lavorando:

    Instant date = null;
    Date sdf = null;
    String formatTemplate = "EEE MMM dd yyyy HH:mm:ss";
    try {
        SimpleDateFormat isoFormat = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss");
        isoFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("US/Pacific")));
        sdf = isoFormat.parse(timeAtWhichToMakeAvailable);
        date = sdf.toInstant();

    } catch (Exception e) {
        System.out.println("did not parse: " + timeAtWhichToMakeAvailable);
    }

    LOGGER.info("timeAtWhichToMakeAvailable: " + timeAtWhichToMakeAvailable);
    LOGGER.info("sdf: " + sdf);
    LOGGER.info("parsed to: " + date);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.