Java将GMT / UTC转换为本地时间不能按预期工作

为了展示一个可重现的场景,我正在做以下工作

  1. 获取当前系统时间(当地时间)

  2. 将当地时间转换为UTC //直到此处为止

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

    {

    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()); // Reverse Convert UTC Time to Locale time (Doesn't work) Approach 1 sdf.setTimeZone(TimeZone.getDefault()); localTime = new Date(sdf.format(gmtTime)); System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime()); // Reverse Convert UTC Time to Locale time (Doesn't work) Approach 2 using DateFormat DateFormat df = new SimpleDateFormat (format); df.setTimeZone(TimeZone.getDefault()); localTime = df.parse((df.format(gmtTime))); System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + gmtTime.toString() + "-" + gmtTime.getTime()); // Approach 3 Calendar c = new GregorianCalendar(TimeZone.getDefault()); c.setTimeInMillis(gmtTime.getTime()); System.out.println("Local Time " + c.toString()); 

    }

我也推荐使用前面提到的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 

通常情况下,我们认为这是StackOverflow.com上糟糕的forms,通过build议替代技术来回答具体问题。 但是对于与Java 7及更早版本捆绑在一起的date,时间和日历类,这些类在devise和执行方面都非常糟糕,所以我不得不build议使用第三方库: Joda-Time 。

Joda-Time通过创build不可变的对象来工作 。 因此,我们只是实例化一个具有不同时区的新date时间,而不是更改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 ) ); 

在北美西海岸运行时的产量可能是:

Local time in ISO 8601 format: 2013-10-15T02:45:30.801-07:00

UTC (Zulu) time zone: 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 ) ) ); } } 

我正在join合唱团,build议您跳过现在已经过时的过时课程DateCalendarSimpleDateFormat和朋友。 特别是我会警告使用Date类的不推荐使用的方法和构造函数,如使用的Date(String)构造函数。 它们被弃用,因为它们不能跨时区可靠地工作,所以不要使用它们。 是的,该类的大部分构造函数和方法都被弃用了。

当你问这个问题的时候,Joda-Time是(我所知道的)一个更好的select,时间又一次地移动了。 今天,Joda-Time是一个基本完成的项目,开发人员build议您使用java.time (现代Javadate和时间API)。 我会告诉你如何。

  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(); 就像你的问题一样,他们独立于时区,所以我没有在这里find他们。

我犹豫地保持你的variables名localTime 。 我认为这是一个好名字。 现代的API有一个名为LocalTime的类,所以使用这个名称,只有大写,没有typesLocalTime的对象可能会混淆一些( LocalTime不包含时区信息,我们需要保留在这里能够做出正确的转换;它也只能保持时间,而不是date)。

从本地时间到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.util.Date 。

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

如果至less使用Java 6 ,您可以。

  • 在Java 8及更高版本中,新的API是内置的。
  • 在Java 6和7中,得到ThreeTen Backport ,这是新类的后端(对于JSR-310来说,ThreeTen是第一个定义现代API的地方)。
  • 在Android上,使用Android版的ThreeTen Backport。 这就是所谓的ThreeTenABP,我认为这个问题有一个很好的解释:如何在Android项目中使用ThreeTenABP 。

我强烈build议使用Joda Time http://joda-time.sourceforge.net/faq.html

你有一个已知的时区(这里Europe/Madrid )和一个目标时区( UTC

你只需要两个SimpleDateFormats:

         long ts = System.currentTimeMillis();
        datelocalTime =新date(ts);

         SimpleDateFormat sdfLocal = new SimpleDateFormat(“yyyy / MM / dd HH:mm:ss”);
         sdfLocal.setTimeZone(TimeZone.getTimeZone( “欧洲/马德里”));

         SimpleDateFormat sdfUTC = new SimpleDateFormat(“yyyy / MM / dd HH:mm:ss”);
         sdfUTC.setTimeZone(TimeZone.getTimeZone( “UTC”));

         //将本地时间转换为UTC
        dateutcTime = sdfLocal.parse(sdfUTC.format(localTime));
         System.out.println(“Local:”+ localTime.toString()+“,”+ localTime.getTime()+“ - > UTC time:”+ utcTime.toString()+“ - ”+ utcTime.getTime ));

         //将UTC时间逆转换为区域设置时间
         localTime = sdfUTC.parse(sdfLocal.format(utcTime));
         System.out.println(“UTC:”+ utcTime.toString()+“,”+ utcTime.getTime()+“ - > Local time:”+ localTime.toString()+“ - ”+ localTime.getTime ));

所以在看到它工作之后,你可以把这个方法添加到你的utils中:

    公共dateconvertDate(datedateFrom,stringfromTimeZone,stringtoTimeZone)抛出ParseException {
         String pattern =“yyyy / MM / dd HH:mm:ss”;
         SimpleDateFormat sdfFrom = new SimpleDateFormat(pattern);
         sdfFrom.setTimeZone(TimeZone.getTimeZone(fromTimeZone));

         SimpleDateFormat sdfTo = new SimpleDateFormat(pattern);
         sdfTo.setTimeZone(TimeZone.getTimeZone(toTimeZone));

        datedateTo = sdfFrom.parse(sdfTo.format(dateFrom));
        返回dateTo;
     }