更改时区而不更改 Java 中的时间

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2873119/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 23:13:56  来源:igfitidea点击:

Changing timezone without changing time in Java

javadatetimecalendartimezone

提问by Paul Wagland

I'm receiving a datetime from a SOAP webservice without timzone information. Hence, the Axis deserializer assumes UTC. However, the datetime really is in Sydney time. I've solved the problem by substracting the timezone offset:

我从没有时区信息的 SOAP 网络服务接收日期时间。因此,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 );

However, I'm not sure if this solution also takes daylight saving into account. I think it should, because all operations are on UTC time. Any experiences with manipulating time in Java? Better ideas on how to solve this problem?

但是,我不确定此解决方案是否也考虑了夏令时。我认为应该,因为所有操作都在 UTC 时间。在 Java 中操作时间的任何经验?关于如何解决这个问题的更好的想法?

采纳答案by Basil Bourque

I've decided to reparsethe datetime string received with the correct time zone set. This should also consider daylight saving:

我决定使用正确的时区集重新解析收到的日期时间字符串。这也应该考虑夏令时:

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

回答by Paul Wagland

I pity the fool who has to do dates in Java.

我可怜那个必须用 Java 做约会的傻瓜。

What you have done will almost certainly go wrong around the daylight savings transitions. The best way to to it is probably to create a new Calendar object, set the Timezone on it, and then set all of the fields individually, so year, month, day, hour, minute, second, getting the values from the Date object.

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

Edit:
To keep the everyone happy, you should probably do this:

编辑:
为了让每个人都开心,你应该这样做:

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

Then you won't be using any deprecated methods.

那么您将不会使用任何已弃用的方法。

回答by Mark Elliot

I want to thank the person for responce 6. This was a great start for me and an approach I did not consider. There are some addtional steps required to bring it to production code level. In particular observe the steps required for DST_OFFSET and ZONE_OFFSET. I want to share the solution I came up with.

我要感谢回复 6 的人。这对我来说是一个很好的开始,也是我没有考虑过的方法。将它带到生产代码级别需要一些额外的步骤。特别要注意 DST_OFFSET 和 ZONE_OFFSET 所需的步骤。我想分享我想出的解决方案。

This takes the time from the input Calendar object, copies it to the output time, sets the new time zone to the output. This is used when taking time literally from the database and setting the Time Zone without changing the time.

这从输入 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() );
}

回答by Timo Westk?mper

However, I'm not sure if this solution also takes daylight saving into account. I think it should, because all operations are on UTC time.

但是,我不确定此解决方案是否也考虑了夏令时。我认为应该,因为所有操作都在 UTC 时间。

Yes, you should take the daylight saving into account, since it affects the offset to UTC.

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

Any experiences with manipulating time in Java? Better ideas on how to solve this problem?

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

Joda-Timeis a better time API. Maybe the following snippet could be of help :

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

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

JodaTime types are immutable which is also a benefit.

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

回答by razedk

I normally do it this way

我通常这样做

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

回答by Olesia

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


Running CalendarTest
2016-06-30 19:09:16.522 +0300
1467302956522
2016-06-30 19:09:16.522 +0100
1467310156522
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.137 sec


运行 CalendarTest
2016-06-30 19:09:16.522 +0300
1467302956522
2016-06-30 19:09:16.522 +0100
1467310156522
测试运行:0, 1 秒,错误时间:0, 1 秒,错误

回答by Basil Bourque

Are you getting an ISO 8601 style string from that messed-up Web Service? If so, the Joda-Time 2.3 library makes this very easy.

您是否从那个混乱的 Web 服务中获得了 ISO 8601 样式的字符串?如果是这样,Joda-Time 2.3 库使这变得非常容易。

If you are getting an ISO 8601 string without any time zone offset, you pass a time zone object to the DateTime constructor.

如果您获得没有任何时区偏移量的 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 );

Dump to console…

转储到控制台...

System.out.println( "dateTime: " + dateTime );

When run…

运行时…

dateTime: 2014-01-02T03:00:00.000+11:00