Jak ustawić strefę czasową java.util.Date?

225

Analizowałem a java.util.Datez, Stringale ustawia lokalną strefę czasową jako strefę czasową dateobiektu.

Strefa czasowa nie jest określona w, Stringz którego Datejest analizowany. Chcę ustawić określoną strefę czasową dateobiektu.

Jak mogę to zrobić?

Yatendra Goel
źródło
8
Chociaż tak naprawdę nie jest to odpowiedź na twoje pytanie, użyłem Joda Time po tym, jak kilka razy tu wspomniano. Wydaje mi się to bardziej racjonalne niż standardowe interfejsy API i można to zrobić dość łatwo.
clstrfsck
2
@msandiford Obecnie używaj klas java.time zamiast Joda-Time. Projekt Joda-Time jest teraz w trybie konserwacji , a zespół doradza migrację do klas java.time . Zobacz samouczek Oracle .
Basil Bourque,

Odpowiedzi:

315

Użyj DateFormat. Na przykład,

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
źródło
6
jeśli data jest tworzona z klasy Calendar, możesz ustawić strefę czasową dla Calendar.
lwpro2
19
@ lwpro2 oświadczenie to wprowadza w błąd; Możesz ustawić strefę czasową dla obiektu kalendarza, ale pobranie z niego obiektu Date za pomocą metody getTime () zwróci obiekt Date ze strefą czasową komputera hosta.
BrDaHa
Mam problem z przetworzeniem 02/20/15 14:44. Czy ktoś może pomóc
MS
@BrDaHa jest poprawny. Musisz to zrobić TimeZone.setDefault()przed wywołaniem, getTime()aby nowy obiekt daty znalazł się w żądanej strefie czasowej. W JDK 1.8 Calendar.getTime()połączenia return new Date(getTimeInMillis());.
jpllosa
182

Należy pamiętać, że java.util.Dateobiekty same w sobie nie zawierają żadnych informacji o strefie czasowej - nie można ustawić strefy czasowej na Dateobiekcie. Jedyne, co Datezawiera obiekt, to kilka milisekund od „epoki” - 1 stycznia 1970 r., 00:00:00 UTC.

Jak pokazuje ZZ Coder, ustawiasz strefę czasową na DateFormatobiekcie, aby powiedzieć mu, w której strefie czasowej chcesz wyświetlić datę i godzinę.

Jesper
źródło
77
Po Googlingu, eksperymentowaniu i eksplorowaniu, zdałem sobie sprawę, że jest to dokładny i pomocny dodatek do odpowiedzi - i warte podkreślenia: Data zawiera tylko wartość milisekundową . Jeśli spojrzysz na źródło, jest prawie tak longzwane pole fastTime. Date.toString()faktycznie używa a Calendardo interpretacji tego milisekundowego czasu. Więc wypisanie Datesprawia, że wydają się mieć (domyślne) strefę czasową, co prowadzi do zrozumiałych pytania dotyczące tego, jak ustawić strefę czasową.
David Carboni,
3
wewnątrz obiektów Date znajdują się informacje o strefie czasowej. Ale to może być prawda, nie możesz tego zmienić.
lwpro2
3
@ Iwpro2, Jesper twierdzi (i zgadzam się), że obiekt Date nie przechowuje strefy czasowej. Jeśli twierdzisz, że strefa czasowa jest przechowywana w pliku java.util.Date, podaj odniesienie.
Jim,
6
@Jim, to jest kod źródłowy dla java.util.Date. Zawiera fastTime jako epokę uniksową, a także cdate BaseCalendar.Date, który jest używany na korzyść fastTime, jeśli jest zdefiniowany. Ten zawiera informacje o strefie czasowej. Rozumiem to, więc instancja Date może zawierać informacje o strefie czasowej, ale może nie.
eis
2
@Jim Nie powiedziałem, że możesz to ustawić, mówiłem, że zawiera te informacje. Nie widzę też żadnego sposobu, aby ustawić go jako użytkownika. Myślę, że OP jest poprawny, ponieważ wydaje się, że zawsze jest domyślną strefą czasową użytkownika / systemu.
eis
95

tl; dr

… Parsowane… z ciągu… strefa czasowa nie jest określona… Chcę ustawić określoną strefę czasową

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.

Brak strefy czasowej w czerwcu

Jak podają inne poprawne odpowiedzi, java.util.Date nie ma strefy czasowej . Reprezentuje UTC / GMT (bez przesunięcia strefy czasowej). Bardzo mylące, ponieważ jego toStringmetoda stosuje domyślną strefę czasową maszyny JVM podczas generowania reprezentacji typu String.

Unikaj juDate

Z tego i wielu innych powodów należy unikać korzystania z wbudowanego java.util.Date & .Calendar & java.text.SimpleDateFormat. Są notorycznie kłopotliwe.

Zamiast tego użyj pakietu java.time dołączonego do Java 8 .

java.time

Klasy java.time mogą reprezentować moment na osi czasu na trzy sposoby:

  • UTC ( Instant)
  • Z przesunięciem ( OffsetDateTimez ZoneOffset)
  • Ze strefą czasową ( ZonedDateTimez ZoneId)

Instant

W java.time podstawowym elementem konstrukcyjnym jest Instantchwila na linii czasu w UTC. Używaj Instantobiektów przez większą część logiki biznesowej.

Instant instant = Instant.now();

OffsetDateTime

Zastosuj przesunięcie względem UTC aby dostosować się do czasu zegara ściennego w danej miejscowości .

Złóż ZoneOffsetwniosek, aby uzyskać OffsetDateTime.

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

ZonedDateTime

Lepiej jest zastosować strefę czasową , przesunięcie oraz zasady postępowania z anomaliami, takimi jak czas letni (DST) .

Zastosuj ZoneIddo, Instantaby uzyskać ZonedDateTime. Zawsze określ prawidłową nazwę strefy czasowej . Nigdy nie używaj 3-4 skrótów, takich jak ESTlub IST, które nie są ani unikalne, ani standardowe.

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

LocalDateTime

Jeśli w łańcuchu wejściowym brakowało jakiegokolwiek wskaźnika przesunięcia lub strefy, parsuj jako LocalDateTime.

Jeśli jesteś pewien planowanej strefy czasowej, przypisz a, ZoneIdaby utworzyć ZonedDateTime. Zobacz przykładowy kod powyżej w sekcji tl; dr u góry.

Sformatowane ciągi

Wywołaj toStringmetodę dowolnej z tych trzech klas, aby wygenerować ciąg znaków reprezentujący wartość daty i godziny w standardowym formacie ISO 8601 . ZonedDateTimeKlasa rozszerza standardowego formatu, dołączając nazwę strefy czasowej w nawiasie.

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]

W przypadku innych formatów użyj DateTimeFormatterklasy. Ogólnie najlepiej, aby pozwolić tej klasie generować zlokalizowane formaty przy użyciu oczekiwanego języka ludzkiego i norm kulturowych. Lub możesz określić konkretny format.


Tabela wszystkich typów dat i godzin w Javie, zarówno nowoczesnych, jak i starszych


O java.time

Środowisko java.time jest wbudowane w Javę 8 i nowsze wersje . Klasy te kłopotliwe zastąpić stary starszych klas Date-Time, takich jak java.util.Date, Calendar, iSimpleDateFormat .

Projekt Joda-Time , teraz w trybie konserwacji , zaleca migrację do klas java.time .

Aby dowiedzieć się więcej, zobacz samouczek Oracle . I przeszukaj stos przepełnienia dla wielu przykładów i wyjaśnień. Specyfikacja to JSR 310 .

Możesz wymieniać obiekty java.time bezpośrednio ze swoją bazą danych. Użyj sterownika JDBC zgodnego z JDBC 4.2 lub nowszym. Nie potrzeba ciągów, nie ma potrzeby java.sql.*klas.

Gdzie można uzyskać klasy java.time?

Projekt ThreeTen-Extra rozszerza java.time o dodatkowe klasy. Ten projekt jest poligonem doświadczalnym dla ewentualnych przyszłych dodatków do java.time. Można znaleźć kilka przydatnych klas tutaj takie jak Interval, YearWeek, YearQuarter, i więcej .


Joda-Time

Chociaż Joda-Time jest nadal aktywnie utrzymywany, jego twórcy powiedzieli nam, abyśmy migrowali do java.time, gdy tylko będzie to wygodne. Zostawiam tę sekcję nietkniętą jako odniesienie, ale sugeruję użyciejava.time zamiast tego powyższej sekcji.

W Joda-Time obiekt date-time ( DateTime) naprawdę zna swoją przypisaną strefę czasową. Oznacza to przesunięcie w stosunku do UTC oraz zasad i historii czasu letniego (DST) w tej strefie czasowej i innych tego rodzaju anomalii.

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

Wywołaj toStringmetodę, aby wygenerować ciąg znaków w formacie ISO 8601 .

String output = dateTimeIndia.toString();

Joda-Time oferuje również bogate możliwości generowania wszelkiego rodzaju innych formatów String.

W razie potrzeby możesz przekonwertować z Joda-Time DateTime na java.util.Date.

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

Wyszukaj StackOverflow dla „joda date”, aby znaleźć wiele innych przykładów, niektóre dość szczegółowe.


To właśnie tam znajduje się strefa czas osadzony w java.util.Date, używane do niektórych funkcji wewnętrznych (patrz komentarze na tę odpowiedź). Ale ta wewnętrzna strefa czasowa nie jest ujawniana jako właściwość i nie można jej ustawić. Ta wewnętrzna strefa czasowa nie jest używana przez toStringmetodę do generowania ciągu reprezentującego wartość daty i godziny; zamiast tego bieżąca domyślna strefa czasowa JVM jest stosowana w locie. Tak więc, jak stenografia, często mówimy „juDate nie ma strefy czasowej”. Mylące? Tak. To kolejny powód, aby unikać tych zmęczonych starych klas.

Basil Bourque
źródło
3
„Brak strefy czasowej w czerwcu” jest błędne. Informacja o strefie czasowej w juDate jest przechowywana w jej BaseCalendar.Date cdatewłaściwości, jeśli jest ustawiona. Spójrz na kod źródłowy tutaj . Nie możesz ustawić strefy czasowej obiektu juDate, chyba że zmienisz domyślną strefę czasową JVM przez wywołanie TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"));. Zatem istnieje przesunięcie strefy czasowej i można je uzyskać, wywołując przestarzałą metodę juDate.getTimezoneOffset ()
Thai Bui
3
@blquythai Prawidłowo, odrobiłeś pracę domową. Podobnie jak ja, po wcześniejszym obejrzeniu tego kodu źródłowego. Jest to strefa czasu pochowano tam. Ale dla wszystkich praktycznych celów strefa czasowa jest ignorowana. Jawa.util.Date działa bez żadnej strefy czasowej, w rzeczywistości jest w UTC, ignorując tę ​​zakopaną strefę czasową. Z wyjątkiem toStringmetody, która stosuje bieżącą domyślną strefę czasową JVM; ponownie ignorując zakopaną strefę czasową. Dla zwięzłości mówimy, że java.util.Date nie ma strefy czasowej. Podobnie jak sztuka , to kłamstwo mówi prawdę.
Basil Bourque,
@blquythai Jeśli chodzi o wywoływanie TimeZone.setDefault, nie ustawiasz strefy czasowej obiektu java.util.Date - obiekt Date nadal ignoruje swoją ukrytą strefę czasową, działając skutecznie w UTC. Wpłynąłbyś na toStringmetodę Date . Ustawienie wartości domyślnej zmienia domyślną strefę czasową maszyny JVM, która jest zwykle ustawiona na strefę czasową systemu operacyjnego hosta. To wywołanie nie jest zalecane, ponieważ wpływa na cały kod we wszystkich wątkach wszystkich aplikacji działających w tej JVM i robi to w locie podczas ich wykonywania. Będąc niegrzecznym i niebezpiecznym, to połączenie powinno być traktowane tylko jako ostateczność.
Basil Bourque,
5
Że strefa czasowa jest używana bardzo często (używane w equals, hashcode, getTime..) Jeśli spojrzeć na equalsmetodzie, wywołuje getTime()który wzywa getTimeImpl(), który wzywa normalize(), jeżeli cdatenieruchomość nie jest znormalizowane. W normalize()metodzie ostatni warunek if ponownie oblicza milisekundy od 1/1/70 na podstawie przechowywanych informacji o strefie czasowej, jeśli strefa czasowa cdatejest inna niż strefa czasowa bieżącego środowiska JVM, na którym działa. (Spójrz na sun.util.calendar.AbstractCalendar getCalendarDate(long millis, CalendarDate date))
Thai Bui
2
@MichalM Zgodnie z twoją radą jakiś czas temu dodałem posłowie o strefie osadzonej.
Basil Bourque,
75

Możesz także ustawić strefę czasową na poziomie 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);

wynik:

Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013
starmer
źródło
To mi pomogło. ustawienie timeZone w SDF nie spowodowało żadnych trudności
wisznu viswanath,
Myślę, że jest bardziej niezawodny. (+1)
foobar
23
Uwaga: Wywołanie TimeZone.setDefaultjest dość drastyczne, ponieważ wpływa na całą JVM, wpływa na wszystkie inne obiekty i wątki. Zobacz tę odpowiedź, aby uzyskać szczegółowe informacje, w tym jeszcze więcej komplikacji, jeśli korzystasz z SecurityManager. Dodanie jeszcze większej komplikacji: zachowanie to zmieniło się w różnych wersjach Java, jak omówiono w tym pytaniu .
Basil Bourque,
To ustawia wspólną strefę czasową dla wszystkich wątków pojawiających się po tym stwierdzeniu, prawda?
Jaydev
To lepsza odpowiedź na pytanie niż zaakceptowane.
francogrex,
10

Jeśli musisz pracować tylko ze standardowymi klasami JDK, możesz użyć tego:

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

Kredyt trafia do tego postu .

diadyne
źródło
1
Przesunięcie stref czasowych nie zawsze jest stałe. W rzeczywistości mogą ulec zmianie z przyczyn geopolitycznych. TimeZone.getDSTSavings () nie bierze tego pod uwagę i zawsze zwraca bieżące przesunięcie. Oznacza to, że możesz uzyskać błędną konwersję w przypadku, gdy masz do czynienia z historyczną datą z fromTimeZone / toTimeZone, która zmieniła przesunięcia od tej daty.
andrerobot
6

java.util.Calendarjest zwykłym sposobem obsługi stref czasowych przy użyciu tylko klas JDK. Apache Commons ma kilka innych alternatyw / narzędzi, które mogą być pomocne. Edytuj notatkę Sponga przypomniało mi, że słyszałem naprawdę dobre rzeczy o Joda-Time (chociaż sam tego nie używałem).

TJ Crowder
źródło
+1 za czas Jody. Chociaż nie zapewnia żadnych dodatkowych funkcji, których nie można uzyskać ze standardowego interfejsu API języka Java (które znalazłem w każdym razie - z przyjemnością pokazuję inaczej), Joda Time ułatwia niektóre zadania.
Joshua Hutchison
2
@JoshuaHutchison Joda-Time ma mnóstwo dodatkowych funkcji. Przykład reprezentowanie zakresy czasu z tych klas Period, Durationi Interval. Przęsła te obejmują metody, takie jak porównanie contains, abuts, overlapi gap. I PeriodFormatterBuildermoże budować opisowe frazy, takie jak „15 lat i 8 miesięcy”.
Basil Bourque,
1

Konwertuj datę na ciąg i zrób to za pomocą 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);
pomocnik
źródło
1
Oczekuje się, że odpowiedzi na temat przepełnienia stosu będą zawierać dyskusję lub wyjaśnienie. Ta witryna ma być czymś więcej niż biblioteką fragmentów kodu.
Basil Bourque,
dziękuję @Basil Bourque za komentarz.
Redaguję
0

Jeśli ktoś tego kiedykolwiek potrzebuje, jeśli chcesz przekonwertować XMLGregorianCalendarstrefę czasową na aktualną strefę czasową z UTC, wystarczy ustawić strefę czasową na 0, a następnie zadzwonić toGregorianCalendar()- pozostanie ta sama strefa czasowa, ale Datewie, jak ją przekonwertować twoje, abyś mógł uzyskać stamtąd dane.

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

Wynik:

2015-08-26T12:02:27.183Z
2015-08-26T14:02:27.183+02:00
EpicPandaForce
źródło
0

Ten kod był pomocny w aplikacji, nad którą pracuję:

    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
źródło