DateFormat解析-不返回UTC日期 [英] DateFormat parse - not return date in UTC

查看:92
本文介绍了DateFormat解析-不返回UTC日期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的Java代码,试图获取Android设备上UTC的当前日期:

public static Date getCurrentDateUTC() {
    try {
        TimeZone timeZoneUTC = TimeZone.getTimeZone("UTC");
        Date localTime = new Date();
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss Z");
        dateFormat.setTimeZone(timeZoneUTC);
        String dateUTCAsString = dateFormat.format(localTime);
        Debug.d(TAG, "getCurrentDateUTC: dateUTCAsString = " + dateUTCAsString);
        Date dateResult = dateFormat.parse(dateUTCAsString);
        Debug.d(TAG, "getCurrentDateUTC: dateResult = " + dateResult);
        return dateResult;
    } catch (ParseException e) {
        Debug.e(TAG, "getCurrentDateUTC: ", e);
        return null;
    }
}

结果:

dateUTCAsString = 2017-11-15T12:54:25 +0000
dateResult = Wed Nov 15 14:54:25 EET 2017

如您所见, dateUTCAsString 是正确的,以UTC显示当前日期,但是在parse之后, dateResult 不正确.为什么?

解决方案

不好意思,我怀疑您的代码中没有问题,只是混乱.如果您认为旧的Date类的行为令人困惑,请允许我成为第一个同意您的人.解决此问题的好方法是停止使用Date,而开始使用现代的Java日期和时间API.

由于您正在为Android编写代码,因此第一步是要获得ThreeTenABP,这是提供现代API的Android库(如果您使用的是Java 8或9,则可以跳过此步骤,因为将构建现代API在). 此问题:如何在Android Project中使用ThreeTenABP .现在您可以执行以下操作:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss Z");
    String dateUTCAsString = "2017-11-15T12:54:25 +0000";
    Instant dateResult = OffsetDateTime.parse(dateUTCAsString, formatter).toInstant();
    System.out.println(dateResult);

在我的计算机上,这只是打印出来的:

2017-11-15T12:54:25Z

最后的Z表示祖鲁时区或UTC.

您可能知道,System.out.println(dateResult)隐式调用dateResult对象的toString方法.据我了解,您想要的是Instant类的对象始终以UTC生成上述格式.在大多数情况下,Instant类是老式Date类的自然替代.在内部,Instant保留自纪元以来的秒数和纳秒数,该纪元定义为1970年1月1日UTC午夜0:00.我鼓励您将其视为无关的实现细节. Instant是时间线上的一个点.

出了什么问题?

您要求使用UTC约会.取决于您的外观,您可以拥有或不能拥有它.

  • 一方面,Date被实现为距该纪元以来的秒数和毫秒数,因此,如果使用上述纪元定义,您可能会说它始终是
  • 另一方面,您不必担心实施细节.从概念上讲,Date(如Instant)是时间线上的一个点,并且没有也不能具有时区或偏移量.它不能使用UTC.为了使事情更加混乱,在执行"getCurrentDateUTC: dateResult = " + dateResult时,会隐式调用dateResult.toString().此方法获取JVM的时区设置,并将日期时间转换为该时区,并将其用于生成的字符串(无需修改Date对象).这就是为什么无论您尝试打印哪个Date,您都可以在计算机或设备上的EET中看到时间的原因.

java.time或JSR-310

现代的日期和时间API被称为java.time或JSR-310. Oracle教程.

Here my java code that try to get current date in UTC on Android device:

public static Date getCurrentDateUTC() {
    try {
        TimeZone timeZoneUTC = TimeZone.getTimeZone("UTC");
        Date localTime = new Date();
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss Z");
        dateFormat.setTimeZone(timeZoneUTC);
        String dateUTCAsString = dateFormat.format(localTime);
        Debug.d(TAG, "getCurrentDateUTC: dateUTCAsString = " + dateUTCAsString);
        Date dateResult = dateFormat.parse(dateUTCAsString);
        Debug.d(TAG, "getCurrentDateUTC: dateResult = " + dateResult);
        return dateResult;
    } catch (ParseException e) {
        Debug.e(TAG, "getCurrentDateUTC: ", e);
        return null;
    }
}

Result:

dateUTCAsString = 2017-11-15T12:54:25 +0000
dateResult = Wed Nov 15 14:54:25 EET 2017

As you can see the dateUTCAsString IS CORRECT show current date in UTC, but after parse the dateResult is not correct. Why?

解决方案

Excuse me for mentioning it, I suspect that there is no problem in your code, there’s only confusion. If you think the old Date class is behaving confusingly, allow me to be the first of many to agree with you. The good and sound solution to this problem is you stop using Date and start using the modern Java date and time API instead.

Since you are coding for Android, you first step is to get the ThreeTenABP, the library for Android that offers the modern API (if you were using Java 8 or 9, you could skip this step since the modern API wold be built in). The details are described in this question: How to use ThreeTenABP in Android Project. Now you can do:

    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss Z");
    String dateUTCAsString = "2017-11-15T12:54:25 +0000";
    Instant dateResult = OffsetDateTime.parse(dateUTCAsString, formatter).toInstant();
    System.out.println(dateResult);

On my computer this just printed:

2017-11-15T12:54:25Z

The Z at the end means Zulu time zone or UTC.

As you may know, System.out.println(dateResult) implicitly calls the toString method of the dateResult object. Objects of the class Instant produce the above format, always in UTC, as I understood you wanted. The Instant class is the natural replacement for the old-fashioned Date class for most purposes. Internally the Instant holds the number of seconds and nanoseconds since the epoch, which is defined as January 1 1970 at 0:00 midnight UTC. I encourage you to consider this an irrelevant implementation detail. An Instant is a point on the time-line.

What went wrong?

You asked for a date in UTC. Depending on how you look at it, you can or cannot have this.

  • On one hand a Date is implemented as the number of seconds and milliseconds since the epoch, so if you use the above definition of the epoch, you may say that it is always in UTC.
  • On the other hand you shouldn’t worry about implementation details. Conceptually a Date (like an Instant) is a point on the time-line, and does not and cannot have a time zone or offset; it cannot be in UTC. To make matters more confusing, when you do "getCurrentDateUTC: dateResult = " + dateResult, then dateResult.toString() is implicitly called. This method grabs your JVM’s time zone setting and converts the date-time to this zone and uses it for the generated string (without modifying the Date object). This is why you will see the time in EET on your computer or device no matter which Date you try to print.

java.time or JSR-310

The modern date and time API is know as java.time or JSR-310. One good source for learning to use it is the Oracle tutorial.

这篇关于DateFormat解析-不返回UTC日期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆