Как установить часовой пояс java.util.Date?

225

Я проанализировал java.util.Dateс, Stringно он устанавливает местный часовой пояс в качестве часового пояса dateобъекта.

Часовой пояс не указан в том, Stringс которого Dateанализируется. Я хочу установить конкретный часовой пояс dateобъекта.

Как я могу это сделать?

Ятендра Гоэль
источник
8
Хотя это не совсем ответ на ваш вопрос, я использовал Joda Time, увидев, что он упоминался здесь несколько раз. Мне кажется, что он более рациональный, чем стандартные API, и может делать такие вещи довольно легко.
clstrfsck
2
@msandiford В настоящее время используйте классы java.time вместо Joda-Time. Проект Joda-Time сейчас находится в режиме обслуживания , и команда рекомендует перейти на классы java.time . Смотрите Учебник по Oracle .
Василий Бурк

Ответы:

315

Используйте DateFormat. Например,

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
источник
6
если дата создается из класса Calendar, вы можете установить часовой пояс для Calendar.
lwpro2
19
@ lwpro2 это утверждение вводит в заблуждение; Вы можете установить часовой пояс для объекта Calendar, но получение объекта Date из него с помощью метода getTime () вернет объект Date с часовым поясом хост-компьютера.
BrDaHa
У меня проблемы с разбором 20.02.15 14:44. Может ли кто-нибудь помочь
MS
@BrDaHa - это правильно. Вам нужно сделать это TimeZone.setDefault()перед вызовом, getTime()чтобы новый объект даты находился в нужном вам часовом поясе. В JDK 1.8 Calendar.getTime()звонки return new Date(getTimeInMillis());.
jpllosa
182

Имейте в виду, что java.util.Dateобъекты сами по себе не содержат никакой информации о часовом поясе - вы не можете установить часовой пояс для Dateобъекта. Единственное, что Dateсодержит объект - это количество миллисекунд, прошедших с «эпохи» - 1 января 1970 года, 00:00:00 UTC.

Как показывает ZZ Coder, вы устанавливаете часовой пояс для DateFormatобъекта, чтобы сообщить ему, в каком часовом поясе вы хотите отобразить дату и время.

Jesper
источник
77
После Google, экспериментируя и объясняя, я понял, что это точное и полезное дополнение к ответу - и стоит подчеркнуть: Date содержит только значение в миллисекундах . Если вы посмотрите на источник, там есть longполе с именем fastTime. Date.toString()на самом деле использует Calendarдля интерпретации этого миллисекунды. Так выводя Dateделает его появляются иметь ( по умолчанию) часовой пояс, что приводит к понятным вопросы о том , как установить , что часовой пояс.
Дэвид Карбони
3
там есть информация о часовом поясе внутри объектов Date. Но это может быть правдой, вы не можете изменить это.
lwpro2
3
@ Iwpro2, Джеспер утверждает (и я согласен), что объект Date не хранит часовой пояс. Если вы утверждаете, что часовой пояс хранится внутри java.util.Date, укажите ссылку.
Джим
6
@ Джим, это исходный код для java.util.Date. Он содержит fastTime как эпоху Unix, а также cdate BaseCalendar.Date, который используется в пользу fastTime, если он определен. Тот содержит информацию о часовом поясе. Я понимаю, что экземпляр Date может содержать информацию о часовом поясе, но это не так.
эйс
2
@ Джим, я не говорил, что ты можешь установить его, я говорил, что он содержит эту информацию. Я также не вижу способа установить его как пользователя. Я думаю, что OP верен в том смысле, что он всегда является часовым поясом пользователя / системы по умолчанию.
эйс
95

ТЛ; др

... проанализировано ... из строки ... часовой пояс не указан ... Я хочу установить определенный часовой пояс

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.

Нет часового пояса в последнее время

Как указано в других правильных ответах, у java.util.Date нет часового пояса . Он представляет UTC / GMT (без смещения часового пояса). Очень запутанно, потому что его toStringметод применяет часовой пояс JVM по умолчанию при генерации строкового представления.

Избегайте новизны

По этой и многим другим причинам вам следует избегать использования встроенного java.util.Date & .Calendar & java.text.SimpleDateFormat. Они общеизвестно хлопотно.

Вместо этого используйте пакет java.time в комплекте с Java 8 .

java.time

Классы java.time могут представлять момент на временной шкале тремя способами:

  • UTC ( Instant)
  • Со смещением ( OffsetDateTimeс ZoneOffset)
  • С часовым поясом ( ZonedDateTimeс ZoneId)

Instant

В java.time основным строительным блоком является Instantмомент времени на UTC. Используйте Instantобъекты для большей части вашей бизнес-логики.

Instant instant = Instant.now();

OffsetDateTime

Примените смещение-от-UTC, чтобы приспособиться к настенным часам некоторого местоположения .

Применить, ZoneOffsetчтобы получить OffsetDateTime.

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

ZonedDateTime

Лучше применить часовой пояс , смещение плюс правила обработки аномалий, такие как летнее время (DST) .

Применить ZoneIdк, Instantчтобы получить ZonedDateTime. Всегда указывайте правильное название часового пояса . Никогда не используйте 3-4 аббревиатуры, такие как ESTили ISTкоторые не являются ни уникальными, ни стандартизированными.

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

LocalDateTime

Если во входной строке отсутствовал какой-либо индикатор смещения или зоны, анализируйте как LocalDateTime.

Если вы уверены в предполагаемом часовом поясе, назначьте a ZoneIdдля создания ZonedDateTime. Смотрите пример кода выше в разделе tl; dr вверху.

Форматированные строки

Вызовите toStringметод любого из этих трех классов, чтобы сгенерировать строку, представляющую значение даты и времени в стандартном формате ISO 8601 . ZonedDateTimeКласс расширяет стандартный формат путем добавления имени временной зоны в скобках.

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]

Для других форматов используйте DateTimeFormatterкласс. Обычно лучше, чтобы этот класс генерировал локализованные форматы, используя ожидаемый пользовательский язык и культурные нормы. Или вы можете указать конкретный формат.


Таблица всех типов даты и времени в Java, как современных, так и устаревших


О java.time

Java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытеснять неприятные старые устаревшие классы даты и времени , такие как java.util.Date, Calendar, и SimpleDateFormat.

Проект Joda-Time , находящийся сейчас в режиме обслуживания , рекомендует перейти на классы java.time .

Чтобы узнать больше, смотрите Oracle Tutorial . И поиск переполнения стека для многих примеров и объяснений. Спецификация JSR 310 .

Вы можете обмениваться объектами java.time напрямую с вашей базой данных. Используйте драйвер JDBC, соответствующий JDBC 4.2 или более поздней версии . Нет необходимости в строках, нет необходимости в java.sql.*классах.

Где взять классы java.time?

Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является полигоном для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь , такие как Interval, YearWeek, YearQuarter, и более .


Joda времени

В то время как Joda-Time все еще активно поддерживается, его создатели сказали нам перейти на java.time, как только это будет удобно. Я оставляю этот раздел нетронутым в качестве ссылки, но я предлагаю java.timeвместо этого использовать раздел выше.

В Joda-Time объект date-time ( DateTime) действительно знает свой назначенный часовой пояс. Это означает смещение от UTC, а также правила и историю летнего времени (DST) этого часового пояса и другие подобные аномалии.

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

Вызовите toStringметод для генерации строки в формате ISO 8601 .

String output = dateTimeIndia.toString();

Joda-Time также предлагает богатые возможности для генерации всех видов других форматов String.

При необходимости вы можете преобразовать Joda-Time DateTime в java.util.Date.

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

Ищите StackOverflow по запросу "joda date", чтобы найти еще много примеров, некоторые из которых довольно подробны.


На самом деле это часовой пояс встроен в java.util.Date, используется для некоторых внутренних функций (см комментариев по этому Ответу). Но этот внутренний часовой пояс не отображается как свойство и не может быть установлен. Этот внутренний часовой пояс не тот, который используется toStringметодом при генерации строкового представления значения даты и времени; вместо этого текущий часовой пояс JVM по умолчанию применяется на лету. Поэтому в качестве стенографии мы часто говорим: «У juDate нет часового пояса». Смешение? Да. Еще одна причина, чтобы избежать этих усталых старых классов.

Базилик Бурк
источник
3
«Нет часового пояса в juDate» неправильно. В juDate хранится информация о часовом поясе, BaseCalendar.Date cdateесли она установлена. Посмотрите на исходный код здесь . Вы не можете установить часовой пояс объекта juDate, кроме как путем изменения часового пояса JVM по умолчанию с помощью вызова TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"));. Таким образом, существует смещение часового пояса, и вы можете получить его, вызвав устаревший метод juDate.getTimezoneOffset ()
Thai Bui
3
@blquythai Правильно, ты сделал свою домашнюю работу. Как и я, увидев этот исходный код раньше. Там является часовой пояс похоронен там. Но для всех практических целей этот часовой пояс игнорируется. Java.util.Date работает без часового пояса, фактически в UTC, игнорируя этот скрытый часовой пояс. За исключением toStringметода, который применяет текущий часовой пояс JVM по умолчанию; снова игнорируя скрытый часовой пояс. Поэтому для краткости мы говорим, что у java.util.Date нет часового пояса. Как и искусство , это ложь, которая говорит правду.
Василий Бурк
@blquythai Что касается вызова TimeZone.setDefault, вы не устанавливаете часовой пояс объекта java.util.Date - объект Date по-прежнему игнорирует свой скрытый часовой пояс, эффективно действуя в UTC. Вы бы затронули toStringметод Дейта. Установка по умолчанию изменяет часовой пояс JVM по умолчанию, который обычно устанавливается на часовой пояс операционной системы хоста. Этот вызов не рекомендуется, поскольку он влияет на весь код во всех потоках всех приложений, запущенных в этой JVM, и делает это на лету по мере их выполнения. Будучи грубым и опасным, этот призыв следует рассматривать только как последнее средство.
Василий Бурк
5
В то время зона используется очень часто (используется в equals, hashcode, getTime..) Если вы посмотрите на equalsметод, он вызывает , getTime()который вызовы getTimeImpl(), которые вызовы , normalize()если cdateсвойство не нормированы. В normalize()методе последнее условие if пересчитывает миллисекунды с 1/1/70 на основе сохраненной информации о часовом поясе, если часовой пояс cdateотличается от часового пояса текущей среды JVM, в которой он работает. (Взгляните на sun.util.calendar.AbstractCalendar getCalendarDate(long millis, CalendarDate date))
Thai Bui
2
@MichalM По вашему совету, некоторое время назад я добавил послесловие о встроенной зоне.
Василий Бурк
75

Вы также можете установить часовой пояс на уровне 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);

вывод:

Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013
Starmer
источник
Это помогло мне. установка timeZone в SDF не делала
различий
Я думаю, что это более надежно. (+1)
foobar
23
Осторожно: вызов TimeZone.setDefaultдовольно радикальный, поскольку он затрагивает всю JVM, затрагивает все другие объекты и потоки. Посмотрите этот ответ для деталей, включая еще больше сложностей, если вы работаете с SecurityManager. Добавление еще более сложного: это поведение изменилось в различных версиях Java, как обсуждалось в этом Вопросе .
Базилик Бурк
Это устанавливает общий часовой пояс для всех потоков, созданных после этого утверждения, верно?
Джейдев
Это лучший ответ на вопрос, чем принятый.
francogrex
10

Если вам нужно работать только со стандартными классами JDK, вы можете использовать это:

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

Кредит идет на этот пост .

diadyne
источник
1
Смещение часовых поясов не всегда постоянное. На самом деле они могут измениться из-за геополитических причин. TimeZone.getDSTSavings () не учитывает это и всегда возвращает текущее смещение. Это означает, что вы можете получить неправильное преобразование в случае, если вы имеете дело с исторической датой с fromTimeZone / toTimeZone, которая изменила смещения с этой даты.
andrerobot
6

java.util.Calendarэто обычный способ обработки часовых поясов, используя только классы JDK. У Apache Commons есть еще несколько альтернатив / утилит, которые могут оказаться полезными. Редактировать записку Спонга напомнило мне, что я слышал действительно хорошие вещи о Joda-Time (хотя сам не использовал его).

TJ Crowder
источник
+1 за время Йода. Хотя он не предоставляет никакой дополнительной функциональности, которую вы не смогли бы получить из стандартного Java API (который я нашел в любом случае - радует, что его показывают иначе), Joda Time действительно облегчает некоторые задачи.
Джошуа Хатчисон
2
@JoshuaHutchison Joda время имеет тонны дополнительной функциональности. Пример: Представление пролеты времени с классами Period, Durationи Interval. Эти пролеты включают в себя методы сравнения , такие как contains, abuts, overlap, и gap. И PeriodFormatterBuilderможет построить описательные фразы, такие как «15 лет и 8 месяцев».
Базилик Бурк
1

Преобразуйте дату в строку и сделайте это с 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);
avisper
источник
1
Ожидается, что ответы на переполнение стека будут иметь некоторое обсуждение или объяснение. Этот сайт должен быть чем-то большим, чем просто библиотека фрагментов кода.
Василий Бурк
Спасибо @Basil Bourque за ваш комментарий. Я редактирую ответ
avisper
0

Если кому-то это понадобится, если вам нужно преобразовать XMLGregorianCalendarчасовой пояс в ваш текущий часовой пояс из UTC, тогда все, что вам нужно сделать, это установить часовой пояс 0, а затем позвонить toGregorianCalendar()- он останется в том же часовом поясе, но он Dateзнает, как преобразовать его в ваш, так что вы можете получить данные оттуда.

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

Результат:

2015-08-26T12:02:27.183Z
2015-08-26T14:02:27.183+02:00
EpicPandaForce
источник
0

Этот код был полезен в приложении, над которым я работаю:

    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
источник