Ответ 1
Использовать 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");
Я проанализировал java.util.Date
из String
, но он устанавливает локальный часовой пояс как часовой пояс объекта date
.
Часовой пояс не указан в String
, из которого анализируется date
. Я хочу установить определенный часовой пояс объекта date
.
Как я могу это сделать?
Использовать 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");
Имейте в виду, что объекты java.util.Date
сами не содержат информацию о часовом поясе - вы не можете установить часовой пояс для объекта Date
. Единственное, что содержит объект Date
, - это количество миллисекунд с момента "эпохи" - 1 января 1970 года, 00:00:00 по UTC.
Как показывает ZZ Coder, вы устанавливаете часовой пояс для объекта DateFormat
, чтобы сообщить ему, в какой часовой пояс вы хотите отображать дату и время.
... проанализировано... из строки... часовой пояс не указан... Я хочу установить определенный часовой пояс
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 по умолчанию при генерации представления String.
По этой и многим другим причинам вам следует избегать использования встроенного java.util.Date &.Calendar & java.text.SimpleDateFormat. Они общеизвестно хлопотно.
Вместо этого используйте пакет java.time в комплекте с Java 8.
Классы java.time могут представлять момент на временной шкале тремя способами:
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
.
Если вы уверены в предполагаемом часовом поясе, назначьте 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.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-Time все еще активно поддерживается, его создатели сказали нам перейти на java.time, как только это будет удобно. Я оставляю этот раздел нетронутым в качестве ссылки, но вместо этого предлагаю использовать раздел java.time
выше.
В Joda-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 нет часового пояса". Смешение? Да. Еще одна причина, чтобы избежать этих усталых старых классов.
Вы также можете установить часовой пояс на уровне 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
java.util.Calendar
- это обычный способ обработки часовых поясов с использованием только классов JDK. Apache Commons содержит дополнительные альтернативы/утилиты, которые могут быть полезны. Изменить Заметка Spong напомнила мне, что я слышал действительно хорошие вещи о Joda-Time (хотя у меня нет использовал его сам).
Если вам нужно работать только со стандартными классами 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;
}
Кредит идет на этот пост.
Если кому-то это понадобится, если вам нужно преобразовать часовой пояс 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
Преобразуйте Date в String и сделайте это с помощью 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);
Этот код был полезен в приложении, над которым я работаю:
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);