Wie setze ich die Zeitzone eines java.util.Date?

224

Ich habe a java.util.Datevon a analysiert, Stringaber es legt die lokale Zeitzone als Zeitzone des dateObjekts fest.

Die Zeitzone ist in der nicht angegeben String , von dem Datewird analysiert. Ich möchte eine bestimmte Zeitzone des dateObjekts festlegen .

Wie kann ich das machen?

Yatendra Goel
quelle
8
Obwohl dies keine wirkliche Antwort auf Ihre Frage ist, habe ich Joda Time verwendet, nachdem ich es hier einige Male erwähnt habe. Es scheint mir rationaler zu sein als die Standard-APIs und kann so etwas ganz einfach tun.
Clstrfsck
2
@msandiford Verwenden Sie heutzutage java.time- Klassen anstelle von Joda-Time. Das Joda-Time- Projekt befindet sich jetzt im Wartungsmodus. Das Team empfiehlt die Migration zu den Klassen java.time . Siehe Tutorial von Oracle .
Basil Bourque

Antworten:

314

Verwenden Sie DateFormat. Beispielsweise,

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");
ZZ Coder
quelle
6
Wenn das Datum aus der Kalenderklasse erstellt wurde, können Sie die Zeitzone für den Kalender festlegen.
lwpro2
19
@ lwpro2 diese Aussage ist irreführend; Sie können die Zeitzone für ein Kalenderobjekt festlegen. Wenn Sie jedoch mit der Methode getTime () ein Datumsobjekt daraus abrufen, wird ein Datumsobjekt mit der Zeitzone des Hostcomputers zurückgegeben.
BrDaHa
Ich habe Probleme beim Parsen des 20.02.15 um 14:44 Uhr. Kann jemand helfen
MS
@BrDaHa ist korrekt. Sie müssen dies TimeZone.setDefault()vor dem Aufruf getTime()tun, damit sich das neue Datumsobjekt in der gewünschten Zeitzone befindet . In JDK 1.8, Calendar.getTime()fordert return new Date(getTimeInMillis());.
Jpllosa
182

Beachten Sie, dass java.util.DateObjekte selbst keine Zeitzoneninformationen enthalten. Sie können die Zeitzone für ein DateObjekt nicht festlegen . Das einzige, was ein DateObjekt enthält, ist eine Anzahl von Millisekunden seit der "Epoche" - 1. Januar 1970, 00:00:00 UTC.

Wie ZZ Coder zeigt, legen Sie die Zeitzone für das DateFormatObjekt fest, um anzugeben, in welcher Zeitzone Sie Datum und Uhrzeit anzeigen möchten.

Jesper
quelle
77
Nach einem Googeln, Experimentieren und Erkunden habe ich festgestellt, dass dies eine präzise und hilfreiche Ergänzung der Antwort ist - und hervorzuheben ist: Datum enthält nur den Millisekundenwert . Wenn Sie sich die Quelle ansehen, gibt es so ziemlich nur ein longFeld namens fastTime. Date.toString()verwendet tatsächlich a Calendar, um diese Millisekundenzeit zu interpretieren. Wenn DateSie also a ausdrucken, scheint es eine (Standard-) Zeitzone zu haben, was zu verständlichen Fragen zum Festlegen dieser Zeitzone führt.
David Carboni
3
In den Date-Objekten befinden sich Zeitzoneninformationen. Aber es könnte wahr sein, Sie können es nicht ändern.
lwpro2
3
@ Iwpro2, Jesper behauptet (und ich stimme zu), dass das Date-Objekt keine Zeitzone speichert. Wenn Sie behaupten, dass die Zeitzone in java.util.Date gespeichert ist, geben Sie bitte eine Referenz an.
Jim
6
@ Jim, dies ist der Quellcode für java.util.Date. Es enthält fastTime als Unix-Epoche sowie BaseCalendar.Date-Datum, das zugunsten von fastTime verwendet wird, sofern es definiert ist. Dieser enthält Zeitzoneninformationen. Ich verstehe es so, dass eine Datumsinstanz Zeitzoneninformationen enthalten kann, aber möglicherweise nicht.
Eis
2
@ Jim Ich habe nicht gesagt, dass Sie es einstellen können, ich habe gesagt, dass es diese Informationen enthält. Ich sehe auch keine Möglichkeit, es als Benutzer festzulegen. Ich denke, OP ist insofern richtig, als es immer die Standardzeitzone des Benutzers / Systems zu sein scheint.
uvb
95

tl; dr

… Geparst… aus einem String… Zeitzone ist nicht angegeben… Ich möchte eine bestimmte Zeitzone festlegen

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.

Keine Zeitzone in juDate

Wie in den anderen richtigen Antworten angegeben, hat ein java.util.Date keine Zeitzone . Es repräsentiert UTC / GMT (kein Zeitzonenversatz). Sehr verwirrend, weil estoString Methode beim Generieren einer String-Darstellung die Standardzeitzone der JVM anwendet.

Vermeiden Sie juDate

Aus diesem und vielen anderen Gründen sollten Sie die Verwendung von java.util.Date & .Calendar & java.text.SimpleDateFormat vermeiden. Sie sind notorisch lästig.

Verwenden Sie stattdessen das mit Java 8 gelieferte Paket java.time .

java.time

Die java.time-Klassen können einen Moment auf der Timeline auf drei Arten darstellen:

  • UTC ( Instant)
  • Mit einem Offset ( OffsetDateTimemit ZoneOffset)
  • Mit einer Zeitzone ( ZonedDateTimemit ZoneId)

Instant

In java.time ist der Grundbaustein Instantein Moment auf der Zeitachse in UTC. Verwenden Sie InstantObjekte für einen Großteil Ihrer Geschäftslogik.

Instant instant = Instant.now();

OffsetDateTime

Tragen Sie eine Offset-von-UTC einzustellen in einige der Ortschaft Wandtaktzeit .

Bewerben Sie sich ZoneOffset, um eine zu erhalten OffsetDateTime.

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

ZonedDateTime

Besser ist es, eine Zeitzone , einen Versatz sowie die Regeln für den Umgang mit Anomalien wie Sommerzeit (DST) anzuwenden .

Wenden Sie ein ZoneIdauf ein Instantan, um ein zu erhalten ZonedDateTime. Geben Sie immer einen richtigen Zeitzonennamen an . Verwenden Sie niemals 3-4 Abkürzungen wie ESToder IST, die weder eindeutig noch standardisiert sind.

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

LocalDateTime

Wenn der Eingabezeichenfolge ein Indikator für Versatz oder Zone fehlte, analysieren Sie als LocalDateTime.

Wenn Sie sich der beabsichtigten Zeitzone sicher sind, weisen Sie a ZoneIdzu , um a zu erzeugen ZonedDateTime. Siehe Codebeispiel oben im Abschnitt tl; dr oben.

Formatierte Zeichenfolgen

Rufen Sie die toStringMethode für eine dieser drei Klassen auf, um eine Zeichenfolge zu generieren, die den Datums- / Uhrzeitwert im Standardformat ISO 8601 darstellt . Die ZonedDateTimeKlasse erweitert das Standardformat, indem der Name der Zeitzone in Klammern angehängt wird.

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]

Verwenden Sie für andere Formate die DateTimeFormatterKlasse. Im Allgemeinen ist es am besten, diese Klasse lokalisierte Formate unter Verwendung der vom Benutzer erwarteten menschlichen Sprache und kulturellen Normen generieren zu lassen. Oder Sie können ein bestimmtes Format angeben.


Tabelle aller Datums- und Uhrzeittypen in Java, sowohl moderne als auch ältere


Über java.time

Das java.time- Framework ist in Java 8 und höher integriert. Diese Klassen verdrängen die lästigen alten Legacy - Datum-Zeit - Klassen wie java.util.Date, Calendar, & SimpleDateFormat.

Das Joda-Time- Projekt, das sich jetzt im Wartungsmodus befindet , empfiehlt die Migration auf java.time Klassen .

Weitere Informationen finden Sie im Oracle-Lernprogramm . Suchen Sie im Stapelüberlauf nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .

Sie können java.time- Objekte direkt mit Ihrer Datenbank austauschen . Verwenden Sie einen JDBC-Treiber, der mit JDBC 4.2 oder höher kompatibel ist . Keine Notwendigkeit für Zeichenfolgen, keine Notwendigkeit für java.sql.*Klassen.

Woher bekomme ich die java.time-Klassen?

Das ThreeTen-Extra- Projekt erweitert java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Ergänzungen von java.time. Sie können einige nützliche Klassen hier wie finden Interval, YearWeek, YearQuarter, und mehr .


Joda-Zeit

Während Joda-Time noch aktiv gepflegt wird, haben uns die Macher angewiesen, so schnell wie möglich auf java.time zu migrieren. Ich lasse diesen Abschnitt als Referenz intakt, schlage aber vor, die zu verwendenjava.time obigen Abschnitt zu verwenden.

In Joda-TimeDateTime kennt ein Datums- / Uhrzeitobjekt ( ) seine zugewiesene Zeitzone wirklich. Dies bedeutet einen Versatz von UTC und den Regeln und dem Verlauf der Sommerzeit (DST) dieser Zeitzone und anderer solcher Anomalien.

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

Rufen Sie die toStringMethode auf, um einen String im ISO 8601- Format zu generieren .

String output = dateTimeIndia.toString();

Joda-Time bietet außerdem umfangreiche Funktionen zum Generieren aller Arten anderer String-Formate.

Bei Bedarf können Sie von Joda-Time DateTime in ein java.util.Date konvertieren.

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

Durchsuchen Sie StackOverflow nach "joda date", um viele weitere Beispiele zu finden, von denen einige recht detailliert sind.


Tatsächlich ist in einer java.util.Date eine Zeitzone eingebettet, die für einige interne Funktionen verwendet wird (siehe Kommentare zu dieser Antwort). Diese interne Zeitzone ist jedoch nicht als Eigenschaft verfügbar und kann nicht festgelegt werden. Diese interne Zeitzone ist nicht die, durch die verwendete toStringMethode in einer String - Darstellung des Datum-Zeit - Wert zu erzeugen; Stattdessen wird die aktuelle Standardzeitzone der JVM im laufenden Betrieb angewendet. Als Abkürzung sagen wir oft „juDate hat keine Zeitzone“. Verwirrend? Ja. Ein weiterer Grund, diese müden alten Klassen zu meiden.

Basil Bourque
quelle
3
"Keine Zeitzone in juDate" ist falsch. In juDate ist eine Zeitzoneninformation in seiner BaseCalendar.Date cdateEigenschaft gespeichert, falls festgelegt. Schauen Sie sich hier den Quellcode an . Sie können die Zeitzone eines juDate-Objekts nur festlegen, indem Sie die Standardzeitzone der JVM durch Aufrufen ändern TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"));. Daher gibt es einen Zeitzonenversatz und Sie können den Versatz erhalten, indem Sie die veraltete Methode juDate.getTimezoneOffset ()
Thai Bui
3
@blquythai Richtig, du hast deine Hausaufgaben gemacht. Wie ich, nachdem ich diesen Quellcode schon einmal gesehen hatte. Dort ist eine Zeitzone vergraben. Für alle praktischen Zwecke wird diese Zeitzone jedoch ignoriert. Ein java.util.Date funktioniert ohne Zeitzone, praktisch in UTC, während diese vergrabene Zeitzone ignoriert wird. Mit Ausnahme der toStringMethode, die die aktuelle Standardzeitzone der JVM anwendet. wieder die vergrabene Zeitzone ignorieren. Der Kürze halber sagen wir, dass ein java.util.Date keine Zeitzone hat. Wie Kunst ist es eine Lüge, die die Wahrheit sagt.
Basil Bourque
@blquythai Beim Aufrufen legen TimeZone.setDefaultSie nicht die Zeitzone des Objekts java.util.Date fest. Das Date-Objekt ignoriert weiterhin seine vergrabene Zeitzone und agiert effektiv in UTC. Sie würden die toStringMethode von Date beeinflussen . Durch Festlegen der Standardeinstellung wird die Standardzeitzone der JVM geändert, die normalerweise auf die Zeitzone des Host-Betriebssystems festgelegt ist. Dieser Aufruf wird nicht empfohlen, da er den gesamten Code in allen Threads aller Apps betrifft, die in dieser JVM ausgeführt werden, und dies während der Ausführung im laufenden Betrieb. Da dieser Anruf unhöflich und gefährlich ist, sollte er nur als letzter Ausweg betrachtet werden.
Basil Bourque
5
Das Zeitzone wird sehr häufig verwendet wird (verwendet in equals, hashcode, getTime..) Wenn Sie einen Blick auf die nehmen equalsMethode, ruft sie getTime()die Anrufe getTimeImpl(), die Anrufe , normalize()wenn die cdateEigenschaft nicht normalisiert. Bei der normalize()Methode berechnet die letzte if-Bedingung die Millisekunden seit dem 1.1.70 basierend auf den gespeicherten Zeitzoneninformationen neu, wenn sich die Zeitzone von cdatevon der Zeitzone der aktuellen JVM-Umgebung unterscheidet, in der sie ausgeführt wird. (Schauen Sie sich an sun.util.calendar.AbstractCalendar getCalendarDate(long millis, CalendarDate date))
Thai Bui
2
@MichalM Gemäß Ihrem Rat habe ich vor einiger Zeit ein Nachwort über die eingebettete Zone hinzugefügt.
Basil Bourque
75

Sie können die Zeitzone auch auf JVM-Ebene einstellen

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

Ausgabe:

Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013
Starmer
quelle
Das hat mir geholfen. Das Einstellen der Zeitzone in SDF machte keinen
Unterschied
Ich denke, es ist zuverlässiger. (+1)
foobar
23
Achtung: Das Aufrufen TimeZone.setDefaultist ziemlich drastisch, da es die gesamte JVM und alle anderen Objekte und Threads betrifft. In dieser Antwort finden Sie Details, einschließlich noch weiterer Komplikationen, wenn Sie mit einem SecurityManager arbeiten. Noch komplizierter: Dieses Verhalten hat sich in verschiedenen Java-Versionen geändert, wie in dieser Frage erläutert .
Basil Bourque
Dies legt eine gemeinsame Zeitzone für alle Threads fest, die nach dieser Anweisung erzeugt wurden, oder?
Jaydev
Dies ist eine bessere Antwort auf die Frage als die akzeptierte.
Francogrex
10

Wenn Sie nur mit Standard-JDK-Klassen arbeiten müssen, können Sie Folgendes verwenden:

/**
 * 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;
}

Gutschrift geht an diesen Beitrag .

Diadyne
quelle
1
Der Versatz der Zeitzonen ist nicht immer konstant. Tatsächlich können sie sich aus geopolitischen Gründen ändern. TimeZone.getDSTSavings () berücksichtigt dies nicht und gibt immer den aktuellen Offset zurück. Dies bedeutet, dass Sie möglicherweise eine falsche Konvertierung erhalten, wenn Sie ein historisches Datum mit einer fromTimeZone / toTimeZone haben, deren Offsets seit diesem Datum geändert wurden.
Andrerobot
6

java.util.Calendarist die übliche Methode, um Zeitzonen nur mit JDK-Klassen zu behandeln. Apache Commons bietet einige weitere Alternativen / Dienstprogramme, die hilfreich sein können. Die Notiz von Edit Spong erinnerte mich daran, dass ich wirklich gute Dinge über Joda-Time gehört habe (obwohl ich sie selbst nicht benutzt habe).

TJ Crowder
quelle
+1 für Joda Time. Joda Time bietet zwar keine zusätzlichen Funktionen, die Sie von der Standard-Java-API nicht erhalten konnten (die ich auf jeden Fall gefunden habe - ich freue mich, dass sie anders angezeigt werden), erleichtert jedoch einige Aufgaben.
Joshua Hutchison
2
@JoshuaHutchison Joda-Time hat Tonnen von zusätzlichen Funktionen. Beispiel: mit den Klassen Spannweiten von Zeit darstellt Period, Durationund Interval. Diese Spannen sind Vergleichsmethoden wie contains, abuts, overlap, undgap . Und PeriodFormatterBuilderkann beschreibende Sätze wie "15 Jahre und 8 Monate" erstellen.
Basil Bourque
1

Konvertieren Sie das Datum in einen String und führen Sie dies mit SimpleDateFormat aus.

    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);
avisper
quelle
1
Es wird erwartet, dass die Antworten zum Stapelüberlauf eine Diskussion oder Erklärung enthalten. Diese Site ist mehr als eine Code-Snippet-Bibliothek.
Basil Bourque
Vielen Dank an @Basil Bourque für Ihren Kommentar. Ich bearbeite die Antwort
avisper
0

Wenn jemand dies jemals benötigt, wenn Sie eine XMLGregorianCalendarZeitzone von UTC in Ihre aktuelle Zeitzone konvertieren müssen, müssen Sie lediglich die Zeitzone auf einstellen0 und dann anrufen toGregorianCalendar()- es bleibt dieselbe Zeitzone, aber der Dateweiß, wie man sie konvertiert Ihre, damit Sie die Daten von dort erhalten können.

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

Ergebnis:

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

Dieser Code war hilfreich in einer App, an der ich arbeite:

    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);
VikR
quelle