Calendar сдвиг времени в другую временную зону
Если выполнить код, в консоли будет выведено одинаковое время, хотя текущая зона отличается от установленной в Calendar. Почему так? Ведь вроде время должно быть сдвинуто, тое если сейчас к примеру 10:00 и смещение 2 то во втором случае думал что выведет 12:00.
Date d = new Date();
System.out.println(d);
Calendar c = Calendar.getInstance();
c.setTime(d);
c.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println(c.getTime());
Ответы (1 шт):
Метод Calendar::getTime возвращает экземпляр класса java.util.Date, который НЕ содержит никакой информации о часовом поясе, установленном в экземпляре календаря :
The class
Daterepresents a specific instant in time, with millisecond precision. ... ...the Date class is intended to reflect coordinated universal time (UTC)...
Соответственно, при "выводе" экземпляра Date при помощи Date::toString будет использоваться некий часовой пояс по умолчанию (локальный).
Для управления выводом часового пояса в старых классах для работы с датой/временем необходимо использовать SimpleDateFormat:
Date d = new Date();
System.out.println(d + "; default tz=" + TimeZone.getDefault().getDisplayName());
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm:SS Z");
System.out.println("formatted with default tz: d=" + sdf.format(d) + "; tz=" + sdf.getTimeZone().getDisplayName());
sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println("formatted with Pacific tz: d=" + sdf.format(d) + "; tz=" + sdf.getTimeZone().getDisplayName());
Fri Jan 26 17:36:42 EET 2024; default tz=Eastern European Standard Time
formatted with default tz: d=26-01-2024 17:36:920 +0200; tz=Eastern European Standard Time
formatted with Pacific tz: d=26-01-2024 07:36:920 -0800; tz=Pacific Standard Time
Подход,использующий классы для работы с датой/временем из пакета java.util типа Date, Calendar, TimeZone и т.п., а также форматирование при помощи DateFormat / SimpleDateFormat является устаревшим почти 10 лет со времени выхода Java 8 c обновлённым Java Date/Time API в пакете java.time.
Например, существует класс ZonedDateTime, поддерживающий информацию о часовом поясе (в отличие от LocalDateTime):
ZonedDateTime zonedDateTimeDefault = ZonedDateTime.now();
System.out.println("ZonedDateTime (default): " + zonedDateTimeDefault);
ZonedDateTime zonedDateTimePacific = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println("ZonedDateTime (Pacific): " +zonedDateTimePacific);
Вывод будет несколько отличаться от вышеприведённого.
ZonedDateTime (default): 2024-01-26T17:45:07.778344200+02:00[Europe/Helsinki]
ZonedDateTime (Pacific): 2024-01-26T07:45:07.780342800-08:00[America/Los_Angeles]
Что касается дополнительного вопроса "как можно использовать информацию о часовом поясе в классе Calendar", то изменение часового пояса повлияет на значение поля, которое возвращается для константы Calendar.ZONE OFFSET.
Также эта информация может использоваться в конкретных реализациях календаря типа GregorianCalendar для перевода между локальным и GMT часовыми поясами (сам по себе класс Calendar является абстрактным):
Код класса Calendar в JDK 8
/** * The <code>TimeZone</code> used by this calendar. <code>Calendar</code> * uses the time zone data to translate between locale and GMT time. * @serial */ private TimeZone zone;
К примеру, в классе GregorianCalendar, начиная с JDK 8, имеется метод GregorianCalendar::toZonedDateTime, который позволит получить информацию в корректном часовом поясе:
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
GregorianCalendar gc = (GregorianCalendar) c;
System.out.println(c.getTime() + "; tz=" + c.getTimeZone().getDisplayName());
System.out.println("ZONE OFFSET в поясе GMT: " + c.get(Calendar.ZONE_OFFSET)/3600_000L);
System.out.println("GregorianCalendar.zonedDateTime GMT: " + gc.toZonedDateTime());
// изменяем часовой пояс
c.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println("ZONE OFFSET в поясе PST: " + c.get(Calendar.ZONE_OFFSET)/3600_000L);
System.out.println("GregorianCalendar.zonedDateTime PST: " + gc.toZonedDateTime());
Результат:
Sun Jan 28 11:47:03 GMT 2024; tz=Greenwich Mean Time
ZONE OFFSET в поясе GMT: 0
GregorianCalendar.zonedDateTime GMT: 2024-01-28T11:47:03.073Z[GMT]
ZONE OFFSET в поясе PST: -8
GregorianCalendar.zonedDateTime PST: 2024-01-28T03:47:03.073-08:00[America/Los_Angeles]
И опять-таки следует подчеркнуть, что класс Calendar, его реализации являются устаревшими и их не следует использовать в новом коде.