У меня есть значение Timestamp, полученное из моего приложения. Пользователь может находиться в любом локальном часовом поясе.
Поскольку эта дата используется для WebService, который предполагает, что время всегда указано в GMT, мне необходимо преобразовать параметр пользователя, скажем, (EST) в (GMT). И вот что интересно: пользователь не обращает внимания на свою TZ. Он вводит дату создания, которую хочет отправить в WS, поэтому мне нужно:
Пользователь вводит: 01.05.2008 18:12 (EST)
Параметр WS должен быть : 01.05.2008 18:12 (GMT)
Я знаю, что отметки времени всегда должны быть в GMT по умолчанию, но при отправке параметра, даже если я создал свой календарь из TS (который должен быть в GMT), часы всегда отключены, если пользователь не находится в GMT. Что мне не хватает?
Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate");
Calendar issueDate = convertTimestampToJavaCalendar(issuedDate);
...
private static java.util.Calendar convertTimestampToJavaCalendar(Timestamp ts_) {
java.util.Calendar cal = java.util.Calendar.getInstance(
GMT_TIMEZONE, EN_US_LOCALE);
cal.setTimeInMillis(ts_.getTime());
return cal;
}
В результате с предыдущим кодом я получаю следующее (краткий формат для удобства чтения):
[1 мая 2008 г., 23:12]
Ответы:
public static Calendar convertToGmt(Calendar cal) { Date date = cal.getTime(); TimeZone tz = cal.getTimeZone(); log.debug("input calendar has date [" + date + "]"); //Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT long msFromEpochGmt = date.getTime(); //gives you the current offset in ms from GMT at the current date int offsetFromUTC = tz.getOffset(msFromEpochGmt); log.debug("offset is " + offsetFromUTC); //create a new calendar in GMT timezone, set to this date and add the offset Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.setTime(date); gmtCal.add(Calendar.MILLISECOND, offsetFromUTC); log.debug("Created GMT cal with date [" + gmtCal.getTime() + "]"); return gmtCal; }
Вот результат, если я передаю текущее время ("12:09:05 EDT" от
Calendar.getInstance()
) в:12:09:05 GMT - это 8:09:05 EDT.
Непонятная часть здесь заключается в том, что
Calendar.getTime()
вы возвращаетесьDate
в вашем текущем часовом поясе, а также в том, что нет метода для изменения часового пояса календаря, а также для изменения базовой даты. В зависимости от того, какой тип параметра принимает ваш веб-сервис, вы можете просто захотеть заключить сделку с WS в миллисекундах от эпохи.источник
offsetFromUTC
а не прибавлять? Используя ваш пример, если 12:09 GMT - это 8:09 EDT (что верно), и пользователь вводит «12:09 EDT», алгоритм должен вывести «16:09 GMT», на мой взгляд.Спасибо всем за ответ. После дальнейшего расследования я пришел к правильному ответу. Как упоминал Skip Head, TimeStamped, который я получал из моего приложения, корректировался в соответствии с часовым поясом пользователя. Поэтому, если пользователь ввел 18:12 (EST), я получил бы 14:12 (GMT). Мне нужен был способ отменить преобразование, чтобы время, введенное пользователем, было временем, которое я отправил на запрос WebServer. Вот как я этого добился:
// Get TimeZone of user TimeZone currentTimeZone = sc_.getTimeZone(); Calendar currentDt = new GregorianCalendar(currentTimeZone, EN_US_LOCALE); // Get the Offset from GMT taking DST into account int gmtOffset = currentTimeZone.getOffset( currentDt.get(Calendar.ERA), currentDt.get(Calendar.YEAR), currentDt.get(Calendar.MONTH), currentDt.get(Calendar.DAY_OF_MONTH), currentDt.get(Calendar.DAY_OF_WEEK), currentDt.get(Calendar.MILLISECOND)); // convert to hours gmtOffset = gmtOffset / (60*60*1000); System.out.println("Current User's TimeZone: " + currentTimeZone.getID()); System.out.println("Current Offset from GMT (in hrs):" + gmtOffset); // Get TS from User Input Timestamp issuedDate = (Timestamp) getACPValue(inputs_, "issuedDate"); System.out.println("TS from ACP: " + issuedDate); // Set TS into Calendar Calendar issueDate = convertTimestampToJavaCalendar(issuedDate); // Adjust for GMT (note the offset negation) issueDate.add(Calendar.HOUR_OF_DAY, -gmtOffset); System.out.println("Calendar Date converted from TS using GMT and US_EN Locale: " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT) .format(issueDate.getTime()));
Вывод кода: (Пользователь ввел 01.05.2008 18:12 (EST)
Часовой
пояс текущего пользователя: EST Текущее смещение от GMT (в часах): - 4 (Обычно -5, кроме корректировки DST)
TS из ACP: 2008-05-01 14: 12: 00.0
Календарная дата преобразована из TS с использованием локали GMT и US_EN : 01.05.08 18:12 (GMT)
источник
Вы говорите, что дата используется в связи с веб-службами, поэтому я предполагаю, что в какой-то момент она сериализуется в строку.
Если это так, вам следует взглянуть на метод setTimeZone класса DateFormat. Это определяет, какой часовой пояс будет использоваться при печати отметки времени.
Простой пример:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); formatter.setTimeZone(TimeZone.getTimeZone("UTC")); Calendar cal = Calendar.getInstance(); String timestamp = formatter.format(cal.getTime());
источник
Вы можете решить это с помощью Joda Time :
Date utcDate = new Date(timezoneFrom.convertLocalToUTC(date.getTime(), false)); Date localDate = new Date(timezoneTo.convertUTCToLocal(utcDate.getTime()));
Java 8:
LocalDateTime localDateTime = LocalDateTime.parse("2007-12-03T10:15:30"); ZonedDateTime fromDateTime = localDateTime.atZone( ZoneId.of("America/Toronto")); ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant( ZoneId.of("Canada/Newfoundland"));
источник
Похоже, что ваш TimeStamp установлен на часовой пояс исходной системы.
Это не рекомендуется, но должно работать:
Не рекомендуемый способ - использовать
Calendar.get(Calendar.ZONE_OFFSET) + Calendar.get(Calendar.DST_OFFSET)) / (60 * 1000)
но это нужно делать на стороне клиента, поскольку эта система знает, в каком часовом поясе она находится.
источник
Метод конвертации из одного часового пояса в другой (возможно, работает :)).
/** * Adapt calendar to client time zone. * @param calendar - adapting calendar * @param timeZone - client time zone * @return adapt calendar to client time zone */ public static Calendar convertCalendar(final Calendar calendar, final TimeZone timeZone) { Calendar ret = new GregorianCalendar(timeZone); ret.setTimeInMillis(calendar.getTimeInMillis() + timeZone.getOffset(calendar.getTimeInMillis()) - TimeZone.getDefault().getOffset(calendar.getTimeInMillis())); ret.getTime(); return ret; }
источник
Объекты Date и Timestamp не обращают внимания на часовой пояс: они представляют определенное количество секунд, прошедших с эпохи, без привязки к конкретной интерпретации этого момента как часов и дней. Часовые пояса входят в изображение только в GregorianCalendar (не требуется напрямую для этой задачи) и SimpleDateFormat , которым требуется смещение часового пояса для преобразования между отдельными полями и значениями даты (или длинными ).
Проблема OP находится в самом начале его обработки: пользователь вводит часы, которые неоднозначны и интерпретируются в местном часовом поясе, отличном от GMT; в этот момент значение равно «6:12 EST» , которое можно легко распечатать как «11.12 GMT» или любой другой часовой пояс, но оно никогда не изменится на «6.12 GMT» .
Невозможно сделать SimpleDateFormat, который анализирует «06:12», как «ЧЧ: ММ» (по умолчанию - местный часовой пояс) по умолчанию вместо UTC; SimpleDateFormat сам по себе слишком умен.
Однако вы можете убедить любой экземпляр SimpleDateFormat использовать правильный часовой пояс, если явно укажете его во входных данных: просто добавьте фиксированную строку к полученному (и надлежащим образом подтвержденному) «06:12», чтобы проанализировать «06:12 GMT» как «ЧЧ: ММ г» .
Нет необходимости в явной настройке полей GregorianCalendar или в получении и использовании смещения часового пояса и перехода на летнее время.
Настоящая проблема заключается в разделении входов, которые по умолчанию соответствуют местному часовому поясу, входов, которые по умолчанию соответствуют UTC, и входов, которые действительно требуют явного указания часового пояса.
источник
В прошлом у меня работало определение смещения (в миллисекундах) между часовым поясом пользователя и GMT. После того, как у вас есть смещение, вы можете просто добавить / вычесть (в зависимости от того, в каком направлении происходит преобразование), чтобы получить подходящее время в любом часовом поясе. Обычно я делал это, устанавливая поле миллисекунд объекта Calendar, но я уверен, что вы могли бы легко применить его к объекту timestamp. Вот код, который я использую для получения смещения
int offset = TimeZone.getTimeZone(timezoneId).getRawOffset();
timezoneId - идентификатор часового пояса пользователя (например, EST).
источник
java.time
Современный подход использует классы java.time, которые вытеснили унаследованные устаревшие классы даты и времени, входящие в состав самых ранних версий Java.
java.sql.Timestamp
Класс один из этих устаревших классов. Больше не нужен. Вместо этого используйтеInstant
или другие классы java.time непосредственно с вашей базой данных, используя JDBC 4.2 и новее.Instant
Класс представляет собой момент на временной шкале в формате UTC с разрешением наносекунд (до девяти (9) цифр десятичной дроби).Если вам необходимо взаимодействовать с существующим
Timestamp
, немедленно преобразуйте его в java.time с помощью новых методов преобразования, добавленных к старым классам.Чтобы перейти в другой часовой пояс, укажите часовой пояс как
ZoneId
объект. Укажите правильное время имя зоны в форматеcontinent/region
, напримерAmerica/Montreal
,Africa/Casablanca
илиPacific/Auckland
. Никогда не используйте псевдозоны из 3-4 букв, такие какEST
или,IST
поскольку они не являются настоящими часовыми поясами, не стандартизированы и даже не уникальны (!).ZoneId z = ZoneId.of( "America/Montreal" ) ;
Примените к,
Instant
чтобы создатьZonedDateTime
объект.Чтобы сгенерировать строку для отображения пользователю, выполните поиск по запросу Stack Overflow,
DateTimeFormatter
чтобы найти множество обсуждений и примеров.Ваш вопрос действительно о том, чтобы пойти в другом направлении, от ввода данных пользователя до объектов даты и времени. Как правило, лучше всего разбивать ввод данных на две части: дату и время суток.
LocalDate ld = LocalDate.parse( dateInput , DateTimeFormatter.ofPattern( "M/d/uuuu" , Locale.US ) ) ; LocalTime lt = LocalTime.parse( timeInput , DateTimeFormatter.ofPattern( "H:m a" , Locale.US ) ) ;
Ваш вопрос непонятен. Вы хотите интерпретировать дату и время, введенные пользователем, как UTC? Или в другом часовом поясе?
Если вы имели в виду UTC, создать
OffsetDateTime
со смещением , используя константу для UTC,ZoneOffset.UTC
.Если вы имели в виду другой часовой пояс, объедините вместе с объектом часового пояса a
ZoneId
. Но в каком часовом поясе? Вы можете определить часовой пояс по умолчанию. Или, если это критично, вы должны согласовать с пользователем, чтобы быть уверенным в его намерениях.Чтобы получить более простой объект, который по определению всегда находится в формате UTC, извлеките файл
Instant
.…или…
Отправьте в вашу базу данных.
О java.time
Java.time каркас встроен в Java 8 и более поздних версий. Эти классы вытеснять неприятные старые устаревшие классы даты и времени , такие как
java.util.Date
,Calendar
, иSimpleDateFormat
.Проект Joda-Time , находящийся сейчас в режиме обслуживания , рекомендует перейти на классы java.time .
Чтобы узнать больше, см. Oracle Tutorial . И поищите в Stack Overflow множество примеров и объяснений. Спецификация - JSR 310 .
Где взять классы java.time?
Проект ThreeTen-Extra расширяет java.time дополнительными классами. Этот проект является испытательной площадкой для возможных будущих дополнений к java.time. Вы можете найти некоторые полезные классы здесь , такие как
Interval
,YearWeek
,YearQuarter
, и более .источник