在不改变Java时间的情况下更改时区

时间:2010-05-20 11:14:47

标签: java datetime calendar timezone

我从没有timzone信息的SOAP Web服务接收日期时间。因此,Axis解串器采用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 个答案:

答案 0 :(得分:18)

我怜悯那些必须用Java做日期的傻瓜。

在夏令时过渡期间,您所做的几乎肯定会出错。最好的方法是创建一个新的Calendar对象,在其上设置Timezone,然后单独设置所有字段,如年,月,日,小时,分钟,秒,从Date对象获取值

修改:
为了让每个人都高兴,你应该这样做:

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

然后你不会使用任何弃用的方法。

答案 1 :(得分:5)

我要感谢此人的回应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() );
}

答案 2 :(得分:3)

  

但是,我不确定这个解决方案   也需要夏令时   帐户。我认为应该,因为   所有操作都是在UTC时间。

是的,您应该考虑夏令时,因为它会影响到UTC的偏移。

  

在Java中操纵时间的任何经验?关于如何解决这个问题的更好的想法?

Joda-Time是一个更好的时间API。也许下面的代码片段可能会有所帮助:

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

JodaTime类型是不可变的,这也是一种好处。

答案 3 :(得分:2)

我通常这样做

Calendar trade_date_utc = trade.getTradeDateTime();
TimeZone est_tz = TimeZone.getTimeZone("Australia/Sydney");
Calendar trade_date = Calendar.GetInstance(est_tz);
trade_date.setTimeInMillis( millis );

答案 4 :(得分:1)

 @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秒

答案 5 :(得分:0)

您是否从该混乱的Web服务获得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

答案 6 :(得分:-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 );
    }
}