将 java 日历对象从本地时间转换为 UTC

Convert java Calendar object from local time to UTC

如何将 Java Calendar 对象从本地时间转换为 UTC?这是我尝试过的:

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
    calendar.setTime(localDateTime.getTime());
    SimpleDateFormat outputFmt = new SimpleDateFormat("MMM dd, yyy h:mm a zz");
    String dateAsString = outputFmt.format(calendar.getTime());
    System.out.println(dateAsString)

时间始终以 MT 而非 GMT 显示。我想将日历以 UTC 格式存储在数据库中(我不关心格式化或显示时间)。

您需要在 SimpleDateFormat 呼叫 setTimeZone,而不是 Calendar

//getting local time 
Calendar calendar = Calendar.getInstance(Locale.getDefault());

SimpleDateFormat outputFmt = new SimpleDateFormat("MMM dd, yyy h:mm a zz");
outputFmt.setTimeZone(TimeZone.getTimeZone("GMT")); //set timezone here

//shows calendar time correctly set    
System.out.println(outputFmt.format(calendar.getTime()));

以下是我将正常日期转换为 UTC 的方法。因此,首先将您的日历对象转换为日期对象:

public static String convertToUTC(Date date) {
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
    dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
    return dateFormat.format(date);
}

不要将日期时间转换为字符串以存储到 SQL 数据库中。使用日期时间对象。

我假设您要存储到数据类型为 datetimetimestamp 且没有时区的数据库列中,并且该列将包含 UTC 值(推荐做法)。在这种情况下,进一步假设您至少使用 Java 8 和至少 JDBC 4.2,将 UTC 格式的 LocalDateTime 存储到数据库中。

    ZonedDateTime dateTime = ZonedDateTime.of(
            2018, 7, 1, 9, 0, 0, 0, ZoneId.of("America/Denver"));
    LocalDateTime dateTimeInUtc = dateTime.withZoneSameInstant(ZoneOffset.UTC)
            .toLocalDateTime();
    System.out.println("UTC: " + dateTimeInUtc);

    PreparedStatement ps = yourDatabaseConnection.prepareStatement(
            "insert into your_table(date_time) values (?)");
    ps.setObject(1, dateTimeInUtc);
    ps.executeUpdate();

代码示例采用 7 月 1 日上午 9 点在丹佛并将其转换并打印:

UTC: 2018-07-01T15:00

然后将打印的UTC时间存储到数据库中。注意setObject方法的使用。

您使用的 Calendar class 早就过时了,而且设计总是很糟糕。如果您从遗留 API 中获得了一个您现在无法更改或不想更改的 Calendar 对象,请先将其转换为 Instant,然后从那里进行进一步转换:

    Calendar localDateTime = // …;
    LocalDateTime dateTimeInUtc = localDateTime.toInstant()
            .atOffset(ZoneOffset.UTC)
            .toLocalDateTime();

我应该在这里使用 LocalDateTime 吗?

编辑: 这取决于您在数据库中使用的数据类型和 JDBC 驱动程序的要求。正如我所说,我假设 you 知道数据库中的值是 UTC 并且您使用的数据类型没有时区,所以数据库 doesn' t知道。如果是这种情况,使用带有时区或偏移量信息的 Java 类型是没有意义的,因为在您存储它时无论如何都会丢失。

有人会说,在这种情况下,您已经在数据库中使用了错误的类型,因此建议您使用 timestamp with timezone 列,这样数据库也知道时间是 UTC 时间。在这种情况下,您当然应该在存储的值中包含偏移量信息。例如,PostgreSQL JDBC 驱动程序在这种情况下需要 OffsetDateTime,这是合乎逻辑的(因为它存储的是 UTC 偏移量,而不是实时时区)。

根据您的 DBMS 的功能,可能还有其他选择。我在底部包含了几个链接,您可以搜索更多链接。

你的代码出了什么问题?

您对 Calendar 的转换工作正常,并为您提供了相同的 UTC 时间点。当您尝试将其格式化为 String 时,问题才开始出现:当您采用 calendar.getTime() 时,您将得到一个包含相同时间点(仍然是正确的时间点)的 Date 对象,但是 Date 不能保存时区信息,所以你丢失了关于 UTC 的信息。接下来,您使用 SimpleDateFormat 格式化了 DateSimpleDateFormat 有一个时区,如果你不自己设置它,它会使用你的 JVM 的时区设置,这可能是山地时间的某种变体。因此你的时间是用 MT 打印的。

链接

这里是我自己准备的Calender对象,设置时区为UTC,然后获取Date对象。获得日期对象后,您可以获得以毫秒为单位的时间,这是自纪元以来的毫秒数。

private static Date internalDate(int year, int month, int day, int hour, int minute, int second) {
  Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
  calendar.set(Calendar.YEAR, year);
  calendar.set(Calendar.MONTH, month - 1);
  calendar.set(Calendar.DAY_OF_MONTH, day);
  calendar.set(Calendar.HOUR_OF_DAY, hour);
  calendar.set(Calendar.MINUTE, minute);
  calendar.set(Calendar.SECOND, second);
 // get the Date Object and now your time is in UTC and you can easily convert it to 
 // local or UTC by getting the milli seconds which is date.gerTime(); 
  return calendar.getTime();
}