25

,以显示可再现的情况下不工作,我做了以下的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()); 
    

    }

回答

51

我还建议使用乔达如前所述。

解决在使用标准的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 
+3

一个很好的答案!但是,应该有TimeZone.getDefault()。getOffset(gmtTime.getTimeInMillis()))而不是TimeZone.getDefault()。getOffset(localTime.getTime())。否则,如果您的UTC时间为冬季,但夏令时为夏令时,则可能会遇到DaylightSaving的问题。 – Dime

+3

'新的日期(字符串字符串)'已弃用:( –

+0

你是一个救生员T_T这是唯一的方法来解决冲突java.util.date(@temporal)和timestamp(postgresql)的GMT与UTC – Sarief

1

我强烈建议使用约达时间http://joda-time.sourceforge.net/faq.html

+0

你能否提供一个使用#joda实现相同的例子? –

+0

我不介意使用joda,但想了解为什么代码没有按预期运行。 –

5

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

Joda-Time的作品创建immutable objects。因此,我们只是实例化一个具有不同时区的新日期时间,而不是更改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))); 

    } 
} 
1

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

你只需要两个SimpleDateFormats:

 
     long ts = System.currentTimeMillis(); 
     Date localTime = new Date(ts); 

     SimpleDateFormat sdfLocal = new SimpleDateFormat ("yyyy/MM/dd HH:mm:ss"); 
     sdfLocal.setTimeZone(TimeZone.getTimeZone("Europe/Madrid")); 

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

     // Convert Local Time to UTC 
     Date utcTime = sdfLocal.parse(sdfUTC.format(localTime)); 
     System.out.println("Local:" + localTime.toString() + "," + localTime.getTime() + " --> UTC time:" + utcTime.toString() + "-" + utcTime.getTime()); 

     // Reverse Convert UTC Time to Locale time 
     localTime = sdfUTC.parse(sdfLocal.format(utcTime)); 
     System.out.println("UTC:" + utcTime.toString() + "," + utcTime.getTime() + " --> Local time:" + localTime.toString() + "-" + localTime.getTime()); 

所以以后看到它的工作,你可以将这个方法添加到您的utils的:

 
    public Date convertDate(Date dateFrom, String fromTimeZone, String toTimeZone) throws 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)); 

     Date dateTo = sdfFrom.parse(sdfTo.format(dateFrom)); 
     return dateTo; 
    } 
2

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

当你问这个问题的时候,Joda-Time是(我所知道的)一个明显更好的选择,时间又一次移动了。今天,Joda-Time是一个基本完成的项目,开发人员建议您使用java.time,即现代Java日期和时间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();中获取它们,并且它们与时区无关,所以我没有在这里找到它们。

我犹豫地保留了你的变量名称localTime。我认为这是个好名字。现代的API有一个名为LocalTime的类,所以使用该名称,只有大写,没有类型LocalTime的对象可能会混淆一些(一个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的编码博客上的必读内容是All about java.util.Date

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

如果至少使用Java ,您可以。