【问题标题】:Converting a UTC Date into a Date of different timezone将 UTC 日期转换为不同时区的日期
【发布时间】:2019-09-07 15:36:13
【问题描述】:

我收到一个代表 UTC 日期时间的日期。让我们说: 2019 年 6 月 21 日 10:00

我想将此日期时间转换为时区“欧洲/维也纳”,期望: 2019 年 6 月 21 日 12:00

我不明白,为什么我下面的代码显示的时间相同

        Date utcFinish = new Date(new Date().getYear(), Calendar.JUNE, 21);
        TimeZone europeVienna = TimeZone.getTimeZone("Europe/Vienna");
        Calendar finishInViennaTime = Calendar.getInstance(europeVienna);
        finishInViennaTime.setTime(utcFinish);

        System.out.println(format.format(utcFinish));
        System.out.println(format.format(finishInViennaTime.getTime()));

输出:

2019-06-21 00:00
2019-06-21 00:00

什么是最好的仅限 java7(没有 joda,localdate pls)解决方案!?谢谢

编辑:我也试过了:

        SimpleDateFormat formatWithTimezone = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        formatWithTimezone.setTimeZone(TimeZone.getTimeZone("Europe/Vienna"));

        SimpleDateFormat formatonly = new SimpleDateFormat("yyyy-MM-dd HH:mm");

        Date utcDate = new Date(new Date().getYear(), Calendar.JUNE, 21);

        System.out.println(formatonly.format(utcDate));
        System.out.println(formatWithTimezone.format(utcDate));

输出:

2019-06-21 00:00
2019-06-21 00:00

解决方案

感谢所有解决方案。最后的问题是默认时区。这是我目前的解决方案(欢迎提供更多反馈!):

        // Unfortunately this date has the wrong time zone (Local Time Zone),
        // because Date assumes Local Time Zone the database stores timestamps 
        // in utc that's why I now convert to a datestring and reparse

        Date finishTimeWrongTimeZone = new Date(new Date().getYear(), Calendar.JUNE, 21);
        // in reality i call the db here like getFinishTime();

        // get the plain date string without time shifting
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MMM-dd HH:mm");
        String dateWithoutTimeZone = formatter.format(finishTimeWrongTimeZone);

        // add the timezone to the formatter and reinterpret the datestring
        // effectively adding the correct time zone the date should be in
        formatter.setTimeZone(TimeZone.getTimeZone("UTC"));

        String finishTime = null;
        try {

            Date dateWithCorrectTimeZone = formatter.parse(dateWithoutTimeZone);

            // Convert to expected local time zone (europe/vienna)
            formatter.setTimeZone(TimeZone.getTimeZone("Europe/Vienna"));
            finishTime = formatter.format(dateWithCorrectTimeZone);

        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(finishTime);

【问题讨论】:

  • 日期没有时区。从概念上讲,它是一个瞬间(它只在 Unich Epoch 之后包装几毫秒),无论您身在何处。仅当将其表示为 String 时,您才附加时区,因此 DateFormat 对象的工作是进行转换。
  • @Shakka,上述 spi 的评论正是为什么你的“没有 joda 或 localdate”的方法是错误的。这些新的做事方式之所以存在,正是因为旧版 JDK 日期类型至少命名不佳,并且难以正确使用。
  • 旁注:数据类已过时,设计不佳,如果可能,某些方法已弃用,请使用 java.time api、ZonedDateTime 类和您的案例 getYear() 已弃用
  • 很确定new Date(...)(顺便说一句已弃用)会生成本地时区的时间,而不是UTC。
  • @TiiJ7,如上所述,java.util.Date 只是纪元以来的毫秒数,因此实际上并不包含任何时区信息。但是,该特定构造函数确实使用默认系统时区来推断如何将小时-分钟值(在本例中为 0-0)转换为纪元秒值。

标签: java date java-7


【解决方案1】:

在执行格式之前更改时区。日期将相应地“转换”,但正如我们已经告诉你的,这种旧的编码风格有很多缺陷:

public static void main(String[] x) {
    Date instant = new Date(new Date().getYear(), Calendar.JUNE, 21); // this call assumes the Timezone is your current default (system dependant).
    DateFormat sdf = SimpleDateFormat.getDateTimeInstance();

    sdf.setTimeZone(TimeZone.getTimeZone("Europe/Vienna"));
    System.out.println(sdf.format(instant)); //21 juin 2019 00:00:00

    sdf.setTimeZone(TimeZone.getTimeZone("Europe/Greenwich"));
    System.out.println(sdf.format(instant)); // 20 juin 2019 22:00:00

}

【讨论】:

  • 啊,我明白了……我的测试是错误的。新日期使用本地时区...如果这解决了我的问题,我会报告!
【解决方案2】:

我使用一个格式化程序对象并更改其上的时区

SimpleDateFormat 格式 = new SimpleDateFormat("dd-MM-yyyy HH:mm"); format.setTimeZone(TimeZone.getTimeZone("UTC"));

Date dUtc = format.parse("21-06-2019 10:00");
System.out.println(dUtc);

TimeZone europeVienna = TimeZone.getTimeZone("europe/vienna");

format.setTimeZone(europeVienna);
String sVienna = format.format(dUtc);
System.out.println(sVienna);

【讨论】:

    【解决方案3】:

    现代 (java.time) 解决方案

    我有一个旧 API 的版本,就像你问的那样,但为了完整起见,我还将提供更现代的解决方案。如果无法更新 Java,我建议您查看 ThreeTen-Backport:

    ZonedDateTime zdt = LocalDateTime.of(
        Year.now().getValue(), Month.JUNE, 21, 10, 0, 0
    ).atZone(ZoneOffset.UTC);
    
    System.out.println(
        zdt.withZoneSameInstant(ZoneId.of("Europe/Vienna"))
          .format(DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm"))
    );
    

    旧的 (java.util) 解决方案

    new Date(...) 已弃用,您不应使用它。如果您真的需要坚持使用旧 API;你需要使用Calendar:

    Calendar utcFinish = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
    utcFinish.set(Calendar.MONTH, Calendar.JUNE);
    utcFinish.set(Calendar.DATE, 21);
    utcFinish.set(Calendar.HOUR_OF_DAY, 10);
    utcFinish.set(Calendar.MINUTE, 0);
    

    然后使用 DateFormat 和您实际希望打印的时区:

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    TimeZone europeVienna = TimeZone.getTimeZone("Europe/Vienna");
    format.setTimeZone(europeVienna);
    
    System.out.println(format.format(utcFinish.getTime()));
    

    输出

    两种解决方案都应输出(在撰写本文时,2019 年):

    2019-06-21 12:00
    

    【讨论】:

    • 谢谢你这行得通,我唯一的问题是我从数据库中得到一个日期,如果我这样调用日历:utcFinish.setTime(myDate) -> 我再次得到相同的行为(意思是我得到了UTC时间)
    • 嗯,问题可能在于您如何从数据库中检索日期。当我不得不从数据库中获取日期时,我也遇到了这样的问题。我必须使用getDate 方法,该方法需要一个额外的日历作为输入,并将一个带有UTC 时区的日历传递给它。不确定这是否会为您解决问题,但值得一试。
    • 这是改进我的新解决方案的好方法(见上文)。感谢您的 cmets,您将我带到了可怕的默认时区细节!
    【解决方案4】:

    请使用 SimpleDateFormat 转换时区

    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    Date utcFinish = new Date(new Date().getYear(), Calendar.JUNE, 21);
    TimeZone europeVienna = TimeZone.getTimeZone("Europe/Vienna");
    format.setTimeZone(europeVienna);
    System.out.println(format.format(utcFinish));
    

    【讨论】:

    • 对不起,这对我不起作用,我得到相同的日期时间!
    • 回想起来:这不起作用,因为 new Date() 构造函数将默认 Locale 设置为时区,因此我在时间上没有任何变化
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-13
    • 2011-08-30
    • 2021-03-12
    • 1970-01-01
    • 2021-07-03
    • 2011-11-06
    相关资源
    最近更新 更多