Изменение часового пояса без изменения времени в Java
Я получаю datetime из веб-службы SOAP без информации о часовом поясе. Следовательно, десериализатор оси принимает UTC. Тем не менее, дата-время действительно находится в Сиднее. Я решил проблему, вычитая смещение часового пояса:
Calendar trade_date = trade.getTradeDateTime();
TimeZone est_tz = TimeZone.getTimeZone("Australia/Sydney");
long millis = trade_date.getTimeInMillis() - est_tz.getRawOffset();
trade_date.setTimeZone( est_tz );
trade_date.setTimeInMillis( millis );
Однако я не уверен, что это решение также учитывает летнее время. Я думаю, что это нужно, потому что все операции выполняются в UTC. Любой опыт манипулирования временем в Java? Лучшие идеи о том, как решить эту проблему?
Ответы
Ответ 1
Я решил переписать строку даты и времени, полученную с заданным часовым поясом. Это также должно учитывать летнее время:
public class DateTest {
private static SimpleDateFormat soapdatetime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
/**
* @param args
*/
public static void main(String[] args) {
TimeZone oztz = TimeZone.getTimeZone("Australia/Sydney");
TimeZone gmtz = TimeZone.getTimeZone("GMT");
Calendar datetime = Calendar.getInstance( gmtz );
soapdatetime.setTimeZone( gmtz );
String soap_datetime = soapdatetime.format( datetime.getTime() );
System.out.println( soap_datetime );
soapdatetime.setTimeZone( oztz );
datetime.setTimeZone( oztz );
try {
datetime.setTime(
soapdatetime.parse( soap_datetime )
);
} catch (ParseException e) {
e.printStackTrace();
}
soapdatetime.setTimeZone( gmtz );
soap_datetime = soapdatetime.format( datetime.getTime() );
System.out.println( soap_datetime );
}
}
Ответ 2
Мне жаль дурака, который должен делать даты на Java.
То, что вы сделали, почти наверняка будет ошибочным в переходах перехода на летнее время. Лучший способ сделать это, вероятно, создать новый объект Calendar, установить на нем Timezone, а затем установить все поля отдельно, так что год, месяц, день, час, минута, секунда, получение значений из объекта Date.
Edit:
Чтобы все были счастливы, вы должны, вероятно, сделать это:
Calendar utcTime = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Calendar sydneyTime = Calendar.getInstance(TimeZone.getTimeZone("Australia/Sydney");
utcTime.setTime(trade_date);
for (int i = 0; i < Calendar.FIELD_COUNT; i++) {
sydneyTime.set(i, utcTime.get(i));
}
Тогда вы не будете использовать устаревшие методы.
Ответ 3
Я хочу поблагодарить человека за ответ 6. Это было отличное начало для меня и подход, который я не рассматривал. Для приведения его в уровень производственного кода необходимы некоторые дополнительные шаги. В частности, обратите внимание на шаги, необходимые для DST_OFFSET и ZONE_OFFSET. Я хочу поделиться решением, которое я придумал.
Это занимает время от входного объекта Calendar, копирует его на время вывода, устанавливает новый часовой пояс на выход. Это используется, когда требуется время от времени в базе данных и настройка часового пояса без изменения времени.
public static Calendar setNewTimeZoneCopyOldTime( Calendar inputTime,
TimeZone timeZone ) {
if( (inputTime == null) || (timeZone == null) ) { return( null ); }
Calendar outputTime = Calendar.getInstance( timeZone );
for( int i = 0; i < Calendar.FIELD_COUNT; i++ ) {
if( (i != Calendar.ZONE_OFFSET) && (i != Calendar.DST_OFFSET) ) {
outputTime.set(i, inputTime.get(i));
}
}
return( (Calendar) outputTime.clone() );
}
Ответ 4
Однако я не уверен, что это решение также переносит летнее время в Счет. Я думаю, это должно, потому что все операции выполняются в режиме UTC.
Да, вы должны учитывать летнее время, так как оно влияет на смещение на UTC.
Есть ли опыт манипулирования временем в Java? Лучшие идеи о том, как решить эту проблему?
Joda-Time - это лучший API времени. Возможно, следующий фрагмент может помочь:
DateTimeZone zone; // TODO : get zone
DateTime fixedTimestamp = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond, zone);
Типы JodaTime неизменяемы, что также является преимуществом.
Ответ 5
Я обычно делаю это так
Calendar trade_date_utc = trade.getTradeDateTime();
TimeZone est_tz = TimeZone.getTimeZone("Australia/Sydney");
Calendar trade_date = Calendar.GetInstance(est_tz);
trade_date.setTimeInMillis( millis );
Ответ 6
Вы получаете строку стиля ISO 8601 из этой испорченной веб-службы? Если это так, библиотека Joda-Time 2.3 делает это очень просто.
Если вы получаете строку ISO 8601 без какого-либо смещения часового пояса, вы передаете объект часового пояса конструктору DateTime.
DateTimeZone timeZone = DateTimeZone.forID( "Australia/Sydney" );
String input = "2014-01-02T03:00:00"; // Note the lack of time zone offset at end.
DateTime dateTime = new DateTime( input, timeZone );
Дамп для консоли...
System.out.println( "dateTime: " + dateTime );
При запуске...
dateTime: 2014-01-02T03:00:00.000+11:00
Ответ 7
@Test
public void tzTest() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z");
TimeZone tz1 = TimeZone.getTimeZone("Europe/Moscow");
Calendar cal1 = Calendar.getInstance(tz1);
long l1 = cal1.getTimeInMillis();
df.setTimeZone(tz1);
System.out.println(df.format(cal1.getTime()));
System.out.println(l1);
TimeZone tz2 = TimeZone.getTimeZone("Africa/Douala");
Calendar cal2 = Calendar.getInstance(tz2);
long l2 = l1 + tz1.getRawOffset() - tz2.getRawOffset();
cal2.setTimeInMillis(l2);
df.setTimeZone(tz2);
System.out.println(df.format(cal2.getTime()));
System.out.println(l2);
assertNotEquals(l2, l1);
}
Запуск CalendarTest
2016-06-30 19: 09: 16.522 +0300
1467302956522
2016-06-30 19: 09: 16.522 +0100
1467310156522
Выполнение тестов: 1, Ошибки: 0, Ошибки: 0, Пропущено: 0, Истекшее время: 0.137 сек