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

发布于 2024-09-02 02:58:38 字数 454 浏览 3 评论 0原文

我从 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 中操作时间的经验?关于如何解决这个问题的更好的想法?

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:

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

牵强ㄟ 2024-09-09 02:58:38

我可怜那些必须用 Java 进行约会的傻瓜。

在夏令时转换期间,您所做的几乎肯定会出错。最好的方法可能是创建一个新的 Calendar 对象,在其上设置时区,然后单独设置所有字段,例如年,月,日,小时,分钟,秒,从 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));
}

这样您就不会使用任何已弃用的方法。

I pity the fool who has to do dates in 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.

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.

随风而去 2024-09-09 02:58:38

我要感谢这个人的回复 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() );
}

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.

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.

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() );
}
倚栏听风 2024-09-09 02:58:38

但是,我不确定这个解决方案是否有效
还考虑了夏令时
帐户。我认为应该如此,因为
所有操作均按 UTC 时间进行。

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

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

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

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

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

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.

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

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

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

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.

岁月蹉跎了容颜 2024-09-09 02:58:38

我通常这样做

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

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 );
○愚か者の日 2024-09-09 02:58:38

您是否从那个混乱的 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

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.

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

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
时光暖心i 2024-09-09 02:58:38
 @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);
 }

运行日历测试

2016-06-30 19:09:16.522 +0300

1467302956522

2016-06-30 19:09:16.522 +0100

1467310156522

测试运行:1,失败:0,错误:0,跳过:0,已用时间:0.137 秒

 @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

三生路 2024-09-09 02:58:38
public Calendar replaceTimezone(String targetTimezoneID, String sourceTimezoneID, Date sourceDate) {
        TimeZone sourceTZ = TimeZone.getTimeZone(sourceTimezoneID);
        int sourceTZOffsetToGMT = sourceTZ.getOffset(sourceDate.getTime());
        
        TimeZone targetTZ = TimeZone.getTimeZone(targetTimezoneID);
        Calendar targetCal = Calendar.getInstance();
        targetCal.setTime(sourceDate);
        targetCal.setTimeZone(targetTZ);
        Date preTargetDate = targetCal.getTime();
        int targetTZOffsetToGMT = targetTZ.getOffset(preTargetDate.getTime());
        
        int adjustOffset = sourceTZOffsetToGMT - targetTZOffsetToGMT;
        targetCal.add(Calendar.MILLISECOND, adjustOffset);
        
        return targetCal;
    }
public Calendar replaceTimezone(String targetTimezoneID, String sourceTimezoneID, Date sourceDate) {
        TimeZone sourceTZ = TimeZone.getTimeZone(sourceTimezoneID);
        int sourceTZOffsetToGMT = sourceTZ.getOffset(sourceDate.getTime());
        
        TimeZone targetTZ = TimeZone.getTimeZone(targetTimezoneID);
        Calendar targetCal = Calendar.getInstance();
        targetCal.setTime(sourceDate);
        targetCal.setTimeZone(targetTZ);
        Date preTargetDate = targetCal.getTime();
        int targetTZOffsetToGMT = targetTZ.getOffset(preTargetDate.getTime());
        
        int adjustOffset = sourceTZOffsetToGMT - targetTZOffsetToGMT;
        targetCal.add(Calendar.MILLISECOND, adjustOffset);
        
        return targetCal;
    }
白芷 2024-09-09 02:58:38

我决定重新解析接收到的带有正确时区设置的日期时间字符串。这还应该考虑夏令时:

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

I've decided to reparse the 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 );
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文