37

为了显示可重现的场景,我正在执行以下操作

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

    }

4

6 回答 6

60

我还建议使用前面提到的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
于 2013-10-15T10:19:58.597 回答
4

乔达时间


更新:Joda-Time项目现在处于维护模式,团队建议迁移到java.time类。请参阅Oracle 教程

使用行业领先的java.time类查看我的其他答案。


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

Joda-Time 通过创建不可变对象来工作。因此,我们无需更改 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 ) );

在北美西海岸运行时的输出可能是:

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

    }
}
于 2013-10-15T10:00:32.077 回答
4

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

你只需要两个 SimpleDateFormats:

        长 ts = System.currentTimeMillis();
        日期 localTime = new 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
        日期 utcTime = sdfLocal.parse(sdfUTC.format(localTime));
        System.out.println("Local:" + 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( ));

因此,在看到它工作后,您可以将此方法添加到您的实用程序中:

    公共日期转换日期(日期 dateFrom,字符串 fromTimeZone,字符串 toTimeZone)抛出 ParseException {
        字符串模式 = "yyyy/MM/dd HH:mm:ss";
        SimpleDateFormat sdfFrom = new SimpleDateFormat(模式);
        sdfFrom.setTimeZone(TimeZone.getTimeZone(fromTimeZone));

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

        日期 dateTo = sdfFrom.parse(sdfTo.format(dateFrom));
        返回日期至;
    }
于 2016-08-01T09:04:26.570 回答
2
于 2018-06-17T07:38:49.223 回答
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一个LocalTimeLocalTime叫做正确的转换;它也只保存时间,而不是日期)。

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

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

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 6,则可以。

于 2017-09-02T05:33:22.760 回答
1

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

于 2013-10-15T07:21:28.550 回答