【问题标题】:Setting and formatting time zone with Calendar object in Java and then returning a Date object在 Java 中使用 Calendar 对象设置和格式化时区,然后返回 Date 对象
【发布时间】:2017-07-03 17:17:58
【问题描述】:

我有一个函数,我需要获取当前日期,设置为另一个时区,并将转换/格式化的日期作为Date 对象返回。我有有效的代码,但是,Date 对象没有设置为新转换的日期,它返回当前日期。

代码如下:

public static Date getCurrentLocalDateTime() {

    Calendar currentdate = Calendar.getInstance();
    DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    TimeZone obj = TimeZone.getTimeZone("America/Denver");
    formatter.setTimeZone(obj);

    Logger.info("Local:: " + currentdate.getTime());

    String formattedDate = formatter.format(currentdate.getTime());

    Logger.info("America/Denver:: "+ formattedDate);

    Date finalDate = null;
    try {
        finalDate = formatter.parse(formattedDate);
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    Logger.info("finalDate:: " + finalDate);

    return finalDate;
}

从我查看并尝试过的示例来看,这应该可以正常工作。其中一个问题是我需要返回 Date 对象,以便它与当前代码一起使用。

输出如下:

2017-07-03 17:08:24,499 [INFO] from application in application-akka.actor.default-dispatcher-3 -
                Local:: Mon Jul 03 17:08:24 UTC 2017
2017-07-03 17:08:24,501 [INFO] from application in application-akka.actor.default-dispatcher-3 -
                America/Denver:: 2017-07-03 11:08:24
2017-07-03 17:08:24,502 [INFO] from application in application-akka.actor.default-dispatcher-3 -
                finalDate:: Mon Jul 03 17:08:24 UTC 2017

如您所见,它将日期正确格式化为山区时区,但随后将其设置回 Calendar 时间。


编辑---代码解决方案:

public static Date getCurrentLocalDateTime() {
    Calendar currentdate = Calendar.getInstance();
    ZonedDateTime converted = currentdate.toInstant().atZone(ZoneId.of("America/Denver"))
            .withZoneSameLocal(ZoneOffset.UTC);
    Date finalDate = Date.from(converted.toInstant());
    return finalDate;
}

【问题讨论】:

  • 为什么会有不同的结果?
  • 根据上面的日志输出,我希望返回的日期是 Mon Jul 03 11:08:24 UTC 2017 而不是 Mon Jul 03 17:08:24 UTC 2017 - 因为我将 Calendar 对象格式化为 Mountain Time。
  • 恕我直言,有些人没有正确地完成他们的需求规范。在 2017 年,您不应被要求返回早已过时的 Date 类的实例。 Java 日期和时间 API(从 Java 8 开始,向后移植到 Java 6 和 7)对于您和任何接收您的返回值的人来说都更好更好地使用。如果你可以返回一个ZonedDateTime 对象,它就会有你选择的时区。

标签: java date datetime time timezone


【解决方案1】:

java.util.Date 对象 has no timezone information。它只有一个long 值,即距离1970-01-01T00:00:00Z 的毫秒数(也称为"unix epoch" 或简称​​"epoch")。此值绝对独立于时区(您也可以说“它是 UTC”)。

当你调用Logger.info("finalDate:: " + finalDate);时,它调用了java.util.DatetoString()方法,而且这个方法在后台使用系统默认的时区,给人的感觉是日期对象本身有时区-但是它没有

检查 finalDate.getTime()currentdate.getTimeInMillis() 的值,您会发现它们几乎相同 - “几乎”,因为 SimpleDateFormat 没有 ,所以你失去了毫秒精度(format 方法创建一个没有毫秒的String,当字段不存在时parse 方法将它设置为零)。但是,如果我将格式化程序更改为此:

// using ".SSS" to don't lose milliseconds when formatting
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

输出是:

本地:: 2017 年 7 月 3 日星期一 17:34:34 UTC
美国/丹佛:: 2017-07-03 11:34:34.508
finalDate:: 2017 年 7 月 3 日星期一 17:34:34 UTC

finalDate.getTime()currentdate.getTimeInMillis() 将具有完全相同的值(请注意,Date.toString() 不会打印毫秒,因此您无法知道它们的值是什么 - 只能通过比较您知道的 getTime() 值如果它们相同)。

结论:只需将格式化程序更改为使用毫秒 (.SSS),解析/格式化就可以了。它显示另一个时区的日期是一个实现细节(toString() 方法使用系统的默认时区),但毫秒值是正确的。

如果您想在 UTC 获得 11 小时,您必须创建另一个格式化程序并将其时区设置为 UTC:

DateFormat parser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
finalDate = parser.parse(formattedDate);

那么,finalDate 的时间在 UTC 时的值为 11 小时:

finalDate:: 2017 年 7 月 3 日星期一 11:34:34 UTC


新的 Java 日期/时间 API

旧的类(DateCalendarSimpleDateFormat)有 lots of problemsdesign issues,它们正在被新的 API 取代。

如果您使用的是 Java 8,请考虑使用 new java.time API。更简单,less bugged and less error-prone than the old APIs

如果您使用的是 Java ,则可以使用 ThreeTen Backport,它是 Java 8 新日期/时间类的一个很好的向后移植。对于Android,还有ThreeTenABP(更多关于如何使用它here)。

下面的代码适用于两者。 唯一的区别是包名(在 Java 8 中是 java.time,而在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp),但类和方法 names 是相同的。

要满足您的需要,您可以使用 ZonedDateTime(日期和时间 + 时区)并转换为保持相同日期/时间值的另一个时区:

// current date/time in Denver
ZonedDateTime denverNow = ZonedDateTime.now(ZoneId.of("America/Denver"));
// convert to UTC, but keeping the same date/time values (like 11:34)
ZonedDateTime converted = denverNow.withZoneSameLocal(ZoneOffset.UTC);
System.out.println(converted); // 2017-07-03T11:34:34.508Z

输出将是:

2017-07-03T11:34:34.508Z

如果您想要不同的格式,请使用DateTimeFormatter

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // pattern for day/hour
    .appendPattern("EEE MMM dd HH:mm:ss ")
    // UTC offset
    .appendOffset("+HHMM", "UTC")
    // year
    .appendPattern(" yyyy")
    // create formatter
    .toFormatter(Locale.ENGLISH);
System.out.println(fmt.format(converted));

输出将是:

2017 年 7 月 3 日星期一 11:34:34 UTC


如果您仍需要使用java.util.Date,您可以轻松地从/转换到新的 API。

在 Java 中 >= 8:

// convert your Calendar object to ZonedDateTime
converted = currentdate.toInstant()
               .atZone(ZoneId.of("America/Denver"))
               .withZoneSameLocal(ZoneOffset.UTC);
// converted is equals to 2017-07-03T11:34:34.508Z

// from ZonedDateTime to Date and Calendar (date will be 11:34 UTC)
Date d = Date.from(converted.toInstant());
Calendar cal = Calendar.getInstance();
cal.setTime(d);

// to get a Date that corresponds to 11:34 in Denver
Date d = Date.from(converted.withZoneSameLocal(ZoneId.of("America/Denver")).toInstant());
Calendar cal = Calendar.getInstance();
cal.setTime(d);

在 Java org.threeten.bp.DateTimeUtils 类:

// convert Calendar to ZonedDateTime 
converted = DateTimeUtils.toInstant(currentdate)
                .atZone(ZoneId.of("America/Denver"))
                .withZoneSameLocal(ZoneOffset.UTC);
// converted is equals to 2017-07-03T11:34:34.508Z

// convert ZonedDateTime to Date (date will be 11:34 UTC)
Date d = DateTimeUtils.toDate(converted.toInstant());
Calendar c = DateTimeUtils.toGregorianCalendar(converted);

// to get a Date that corresponds to 11:34 in Denver
Date d = DateTimeUtils.toDate(converted.withZoneSameLocal(ZoneId.of("America/Denver")).toInstant());
Calendar c = DateTimeUtils.toGregorianCalendar(converted.withZoneSameLocal(ZoneId.of("America/Denver")));

【讨论】:

  • 谢谢 - 这会返回 Mon Jul 03 11:34:34 UTC 2017 的 Date 对象吗?我需要时间是上午 11:00,而不是下午 5:00 (17:00)。
  • Date 对象包含对应于 America/Denver 中的 11:34 的值(相当于 UTC 中的 17:34)。如果您想要 11:34 的 String,请使用设置了丹佛时区的格式化程序(就像您已经在使用的格式化程序一样)。还是你真的想要“11:34 UTC”?
  • 如果可能,我希望返回 11:34 UTC。
  • @Dan 我已经用 2 个替代方法更新了答案:使用 SimpleDateFormat 或使用新的日期/时间 API - 如果您可以使用这个新 API,我建议使用它,因为它太多了更好更容易(和 IMO,更清楚它在做什么,比如方法 withZoneSameLocal,直接进行转换,无需以晦涩的方式格式化和解析)
  • 完美运行!我已经用代码更新了我的帖子。
猜你喜欢
  • 2020-12-23
  • 2016-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-13
  • 2023-04-07
  • 1970-01-01
相关资源
最近更新 更多