Java将GMT/UTC转换为本地时间不';I don’我没有按预期工作

Java将GMT/UTC转换为本地时间不';I don’我没有按预期工作,java,simpledateformat,utc,date-formatting,timestamp-with-timezone,Java,Simpledateformat,Utc,Date Formatting,Timestamp With Timezone,为了展示一个可复制的场景,我将执行以下操作 获取当前系统时间(本地时间) 将当地时间转换为UTC//在这里之前都可以正常工作 将UTC时间反转回当地时间。遵循3种不同的方法(如下所列),但所有3种方法仅保留UTC时间 { } 我强烈建议使用Joda TimeJoda Time 更新:项目现在已经开始,团队建议迁移到类。看 请参阅使用业界领先的java.time类 通常,我们认为STACKOFFROW网站上的一个坏的形式是通过提出一种替代技术来回答一个特定的问题。但是对于与Java7和更早版本

为了展示一个可复制的场景,我将执行以下操作

  • 获取当前系统时间(本地时间)

  • 将当地时间转换为UTC//在这里之前都可以正常工作

  • 将UTC时间反转回当地时间。遵循3种不同的方法(如下所列),但所有3种方法仅保留UTC时间

    {

    }


  • 我强烈建议使用Joda Time

    Joda Time
    更新:项目现在已经开始,团队建议迁移到类。看

    请参阅使用业界领先的java.time类


    通常,我们认为STACKOFFROW网站上的一个坏的形式是通过提出一种替代技术来回答一个特定的问题。但是对于与Java7和更早版本捆绑在一起的date、time和calendar类,这些类在设计和执行方面都非常糟糕,因此我不得不建议使用第三方库

    乔达时间通过创造来工作。因此,我们不需要改变DateTime对象的时区,而只需使用指定的不同时区实例化一个新的DateTime

    在Joda time中,使用本地时间和UTC时间的核心问题非常简单,只需要3行代码

        org.joda.time.DateTime now = new org.joda.time.DateTime();
        System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
        System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
    
    在北美西海岸运行时的输出可能为:

    ISO 8601格式的当地时间:2013-10-15T02:45:30.801-07:00

    UTC(祖鲁)时区:2013-10-15T09:45:30.801Z

    下面是一个包含几个示例和进一步注释的类。使用Joda Time 2.5

    /**
     * Created by Basil Bourque on 2013-10-15.
     * © Basil Bourque 2013
     * This source code may be used freely forever by anyone taking full responsibility for doing so.
     */
    public class TimeExample {
        public static void main(String[] args) {
            // Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 8 and earlier.
            // http://www.joda.org/joda-time/
    
            // Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
            // JSR 310 was inspired by Joda-Time but is not directly based on it.
            // http://jcp.org/en/jsr/detail?id=310
    
            // By default, Joda-Time produces strings in the standard ISO 8601 format.
            // https://en.wikipedia.org/wiki/ISO_8601
            // You may output to strings in other formats.
    
            // Capture one moment in time, to be used in all the examples to follow.
            org.joda.time.DateTime now = new org.joda.time.DateTime();
    
            System.out.println( "Local time in ISO 8601 format: " + now + " in zone: " + now.getZone() );
            System.out.println( "UTC (Zulu) time zone: " + now.toDateTime( org.joda.time.DateTimeZone.UTC ) );
    
            // You may specify a time zone in either of two ways:
            // • Using identifiers bundled with Joda-Time
            // • Using identifiers bundled with Java via its TimeZone class
    
            // ----|  Joda-Time Zones  |---------------------------------
    
            // Time zone identifiers defined by Joda-Time…
            System.out.println( "Time zones defined in Joda-Time : " + java.util.Arrays.toString( org.joda.time.DateTimeZone.getAvailableIDs().toArray() ) );
    
            // Specify a time zone using DateTimeZone objects from Joda-Time.
            // http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeZone.html
            org.joda.time.DateTimeZone parisDateTimeZone = org.joda.time.DateTimeZone.forID( "Europe/Paris" );
            System.out.println( "Paris France (Joda-Time zone): " + now.toDateTime( parisDateTimeZone ) );
    
            // ----|  Java Zones  |---------------------------------
    
            // Time zone identifiers defined by Java…
            System.out.println( "Time zones defined within Java : " + java.util.Arrays.toString( java.util.TimeZone.getAvailableIDs() ) );
    
            // Specify a time zone using TimeZone objects built into Java.
            // http://docs.oracle.com/javase/8/docs/api/java/util/TimeZone.html
            java.util.TimeZone parisTimeZone = java.util.TimeZone.getTimeZone( "Europe/Paris" );
            System.out.println( "Paris France (Java zone): " + now.toDateTime(org.joda.time.DateTimeZone.forTimeZone( parisTimeZone ) ) );
    
        }
    }
    
    我还建议使用前面提到的Joda

    使用标准Java
    Date
    对象解决问题的方法如下:

        // **** YOUR CODE **** BEGIN ****
        long ts = System.currentTimeMillis();
        Date localTime = new Date(ts);
        String format = "yyyy/MM/dd HH:mm:ss";
        SimpleDateFormat sdf = new SimpleDateFormat(format);
    
        // Convert Local Time to UTC (Works Fine)
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        Date gmtTime = new Date(sdf.format(localTime));
        System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:"
                + gmtTime.toString() + "," + gmtTime.getTime());
    
        // **** YOUR CODE **** END ****
    
        // Convert UTC to Local Time
        Date fromGmt = new Date(gmtTime.getTime() + TimeZone.getDefault().getOffset(localTime.getTime()));
        System.out.println("UTC time:" + gmtTime.toString() + "," + gmtTime.getTime() + " --> Local:"
                + fromGmt.toString() + "-" + fromGmt.getTime());
    
    输出:

    Local:Tue Oct 15 12:19:40 CEST 2013,1381832380522 --> UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000
    UTC time:Tue Oct 15 10:19:40 CEST 2013,1381825180000 --> Local:Tue Oct 15 12:19:40 CEST 2013-1381832380000
    

    您有一个带有已知时区(此处为
    Europe/Madrid
    )和目标时区(
    UTC
    )的日期

    您只需要两个SimpleDataFormat:

    长ts=System.currentTimeMillis(); 日期本地时间=新日期(ts); SimpleDataFormat sdfLocal=新的SimpleDataFormat(“yyyy/MM/dd HH:MM:ss”); sdfLocal.setTimeZone(TimeZone.getTimeZone(“欧洲/马德里”); SimpleDataFormat sdfUTC=新的SimpleDataFormat(“yyyy/MM/dd HH:MM:ss”); sdfUTC.setTimeZone(TimeZone.getTimeZone(“UTC”); //将本地时间转换为UTC dateutctime=sdfLocal.parse(sdfUTC.format(localTime)); System.out.println(“本地:“+localTime.toString()+”,“+localTime.getTime()+”-->UTC时间:“+utcTime.toString()+”-“+utcTime.getTime()”); //将UTC时间反向转换为区域设置时间 localTime=sdfUTC.parse(sdfLocal.format(utcTime)); System.out.println(“UTC:+utcTime.toString()+”,“+utcTime.getTime()+”-->本地时间:“+localTime.toString()+”-“+localTime.getTime()”); 因此,在看到它工作后,您可以将此方法添加到UTIL:

    public Date convertDate(Date-dateFrom、String-fromTimeZone、String-toTimeZone)引发异常{ 字符串模式=“yyyy/MM/dd HH:MM:ss”; SimpleDataFormat sdfFrom=新的SimpleDataFormat(模式); setTimeZone(TimeZone.getTimeZone(fromTimeZone)); SimpleDataFormat sdfTo=新SimpleDataFormat(模式); 设置时区(TimeZone.getTimeZone(toTimeZone)); Date dateTo=sdfFrom.parse(sdfTo.format(dateFrom)); 返回日期至; }
    我加入合唱团,建议你跳过早已过时的课程《日期》、《日历》、《简化格式》和《朋友》。我特别警告不要使用
    Date
    类中不推荐使用的方法和构造函数,比如您使用的
    Date(String)
    构造函数。它们被弃用,因为它们不能跨时区可靠地工作,所以不要使用它们。是的,该类的大多数构造函数和方法都不推荐使用

    当你问这个问题的时候,Joda time(据我所知)显然是一个更好的选择,时间又一次前进了。如今,Joda Time已经基本完成了一个项目,其开发人员建议您使用现代java日期和时间API
    java.Time
    。我会告诉你怎么做

        ZonedDateTime localTime = ZonedDateTime.now(ZoneId.systemDefault());
    
        // Convert Local Time to UTC 
        OffsetDateTime gmtTime
                = localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
        System.out.println("Local:" + localTime.toString() 
                + " --> UTC time:" + gmtTime.toString());
    
        // Reverse Convert UTC Time to Local time
        localTime = gmtTime.atZoneSameInstant(ZoneId.systemDefault());
        System.out.println("Local Time " + localTime.toString());
    
    对于初学者,请注意,不仅代码只有您的一半长,而且阅读起来更清晰

    在我的计算机上,代码打印:

    Local:2017-09-02T07:25:46.211+02:00[Europe/Berlin] --> UTC time:2017-09-02T05:25:46.211Z
    Local Time 2017-09-02T07:25:46.211+02:00[Europe/Berlin]
    
    我省略了纪元中的毫秒。您总是可以从
    System.currentTimeMillis()获取它们
    我犹豫地保留了您的变量名
    localTime
    。我认为这是个好名字。现代API有一个名为
    LocalTime
    的类,因此对没有类型
    LocalTime
    的对象使用该名称(仅不大写)可能会使一些人感到困惑(a
    LocalTime
    不保存时区信息,我们需要将其保存在此处才能进行正确的转换;它还只保存一天中的时间,而不是日期)

    您从当地时间到UTC的转换不正确且不可能

    过时的
    Date
    类不包含任何时区信息(您可能会说它在内部总是使用UTC),因此不存在将
    Date
    从一个时区转换为另一个时区的情况。当我刚刚在我的计算机上运行您的代码时,它打印的第一行是:

    Local:Sat Sep 02 07:25:45 CEST 2017,1504329945967 --> UTC time:Sat Sep 02 05:25:45 CEST 2017-1504322745000
    
    07:25:45 CEST
    当然是正确的。正确的UTC时间应该是
    05:25:45 UTC
    ,但它再次显示
    CEST
    ,这是不正确的

    现在你再也不需要
    Date
    类了,:-)但如果你要去,必读的内容将出现在Jon Skeet的编码博客上

    问题:我可以在Java版本中使用现代API吗?

    如果至少使用Java6,则可以

    • 在Java8和更高版本中,新的API是内置的
    • Local:2017-09-02T07:25:46.211+02:00[Europe/Berlin] --> UTC time:2017-09-02T05:25:46.211Z
      Local Time 2017-09-02T07:25:46.211+02:00[Europe/Berlin]
      
      Local:Sat Sep 02 07:25:45 CEST 2017,1504329945967 --> UTC time:Sat Sep 02 05:25:45 CEST 2017-1504322745000
      
      Instant.now()                           // Capture the current moment in UTC.
      .atZone( ZoneId.systemDefault() )       // Adjust into the JVM's current default time zone. Same moment, different wall-clock time. Produces a `ZonedDateTime` object.
      .toInstant()                            // Extract a `Instant` (always in UTC) object from the `ZonedDateTime` object.
      .atZone( ZoneId.of( "Europe/Paris" ) )  // Adjust the `Instant` into a specific time zone. Renders a `ZonedDateTime` object. Same moment, different wall-clock time.
      .toInstant()                            // And back to UTC again.
      
      Instant instant = Instant.now() ;  // Capture the current moment in UTC.
      
      ZoneId z = ZoneId.of( "America/Montreal" ) ;
      ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, different wall-clock time.
      
      ZoneId z = ZoneId.of( "America/Montreal" ) ;
      ZonedDateTime zdt = ZonedDateTime.now( z ) ;
      
      ZoneId z = ZoneId.of( "America/Montreal" ) ;
      ZonedDateTime zdt = ZonedDateTime.now( z ) ;
      Instant instant = zdt.toInstant() ;
      
      Instant instant = Instant.now() ;  // Capture current moment in UTC.
      
      ZoneId zDefault = ZoneId.systemDefault() ;  // The JVM's current default time zone.
      ZonedDateTime zdtDefault = instant.atZone( zDefault ) ;
      
      ZoneId zTunis = ZoneId.of( "Africa/Tunis" ) ;  // The JVM's current default time zone.
      ZonedDateTime zdtTunis = instant.atZone( zTunis ) ;
      
      ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;  // The JVM's current default time zone.
      ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;
      
      Instant instant = zdtAuckland.toInstant() ;