Изменение часового пояса без изменения времени в Java - PullRequest
11 голосов
/ 20 мая 2010

Я получаю дату и время от веб-службы SOAP без информации о Timzone. Следовательно, десериализатор Оси предполагает 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? Лучшие идеи о том, как решить эту проблему?

Ответы [ 7 ]

18 голосов
/ 20 мая 2010

Мне жаль дурака, который должен делать свидания на Яве.

То, что вы сделали, почти наверняка пойдет не так при переходе на летнее время. Лучший способ сделать это, вероятно, создать новый объект Calendar, установить для него часовой пояс, а затем установить все поля по отдельности, например, год, месяц, день, час, минута, секунда, получить значения из объекта 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));
}

Тогда вы не будете использовать устаревшие методы.

5 голосов
/ 24 мая 2013

Я хочу поблагодарить человека за ответ 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() );
}
3 голосов
/ 20 мая 2010

Однако я не уверен, что это решение также принимает летнее время в учетная запись. Я думаю, что должно, потому что все операции выполняются по времени UTC.

Да, вы должны учитывать летнее время, так как оно влияет на смещение по UTC.

Есть ли у вас опыт манипулирования временем в Java? Лучшие идеи о том, как решить эту проблему?

Joda-Time - лучшее время API. Может быть, вам поможет следующий фрагмент:

DateTimeZone zone; // TODO : get zone
DateTime fixedTimestamp = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond, zone);

Типы JodaTime являются неизменяемыми, что также является преимуществом.

2 голосов
/ 11 февраля 2012

Я обычно так делаю

Calendar trade_date_utc = trade.getTradeDateTime();
TimeZone est_tz = TimeZone.getTimeZone("Australia/Sydney");
Calendar trade_date = Calendar.GetInstance(est_tz);
trade_date.setTimeInMillis( millis );
1 голос
/ 30 июня 2016
 @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 с

0 голосов
/ 25 января 2014

Получаете ли вы строку стиля 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
0 голосов
/ 20 мая 2010

Я решил обработать строку даты и времени, полученную с правильным установленным часовым поясом. Это также должно учитывать летнее время:

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