关于java:如何在特定时区设置日期?

How to set a date in certain timezone?

本问题已经有最佳答案,请猛点这里访问。

我正在使用计时器在某段时间内执行某些任务。如果要使任务从该日期开始并在每个时间段内重复,则此方法需要日期类型。

我一直试图找到解决方案几个小时,但无法想出任何东西。我创建日历并为其设置时区。然后,当我从该日历获取日期时,此日期包含我的本地时间。我尝试使用SimpleDataFormat从Date获取字符串并且它起作用,因为您分别为SimpleDataFormat设置时区。然后甚至尝试将该字符串解析为日期,但它仍然无效。这一刻我有点无望。如果我无法使用日历,那么为日历设置时区有什么意义呢。如果没有得到我当地的时间,我仍然可以做一些事情,而是获得UTC。但事实并非如此。我不想根据计算机的本地时间运行此应用程序。这是荒谬的,它应该能够获得当地时间并将其转换为我想要的时区并使用它。

码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    TimeZone eu = TimeZone.getTimeZone("GMT+1");
    TimeZone america = TimeZone.getTimeZone("EST");
    TimeZone asia = TimeZone.getTimeZone("GMT+7");
    SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
    df.setTimeZone(eu);
    Calendar euCal = Calendar.getInstance(eu);
    Date dateEu = euCal.getTime();
    System.out.println(dateEu); //Prints local time date
    euCal.setTimeZone(eu);
    System.out.println(dateEu); //Still prints local time date
    String formattedDateEu = df.format(dateEu);
    System.out.println(formattedDateEu); //This one works fine but it is string

    try {
        dateEu = df.parse(formattedDateEu);
    } catch (Exception e) {
        e.printStackTrace();
    }

    System.out.println(dateEu); //Fun part. Although I don't get exception dateEu is still not in the time zone I want.

样本输出:

1
2
3
4
Mon Nov 27 14:37:21 MSK 2017
Mon Nov 27 14:37:21 MSK 2017
27/11/2017 12:37:21
Mon Nov 27 14:37:21 MSK 2017


java.util.Date对象不包含时区信息。它们代表了时间连续体中的瞬间,对于每个人来说都是同一时刻,无论它们恰好恰好在巴黎或纽约。

但是,当您尝试使用System.out.println()打印Date对象时,它需要尝试描述自己。所以它需要描述它所代表的日期和时间,但要做到这一点,首先需要选择一个时区来表示这个日期和时间,因为它没有。为方便起见,它选择用户的系统时区。

你不会通过调用System.out.println()来使用你的日期,所以你不应该关心System.out.println()对它做什么。


我希望我能告诉你远离过时的Date课程。今天,我们在java.time,现代Java日期和时间API方面做得更好。但是,您确定java.util.Timer确实需要Date来在特定日期和时间安排任务。尽管如此,由于现代API使用起来非常好,我建议您使用它来初始化Date

1
2
3
4
5
6
7
8
9
10
    ZoneOffset eu = ZoneOffset.ofHours(1);
    ZoneOffset america = ZoneOffset.ofHours(-5);
    ZoneOffset asia = ZoneOffset.ofHours(7);

    OffsetDateTime euTime = OffsetDateTime.now(eu);
    System.out.println(euTime);
    Date dateEu = Date.from(euTime.toInstant());

    // schedule to start at the specified date-time and repeat weekly
    myJavaUtilTimer.scheduleAtFixedRate(myTask, dateEu, TimeUnit.DAYS.toMillis(7));

我觉得你并没有真正使用时区,只是偏离GMT(或UTC)。所以我在代码中添加了一些ZoneOffset。刚刚打印euTime的结果是

1
2017-11-27T15:56:03.213+01:00

您可以看到偏移量是+01:00。然后,正如kumesana在另一个答案中已经说过的那样,不要打印出结果Date,只要相信它没关系。

如果您确实喜欢实时区域,请使用例如:

1
2
3
    ZoneId eu = ZoneId.of("Europe/Brussels");
    ZoneId america = ZoneId.of("America/Jamaica");
    ZoneId asia = ZoneId.of("Asia/Krasnoyarsk");

其余的代码是一样的。这是真的:OffsetDateTime.now()接受ZoneOffsetZoneId,所以你甚至可以混合这两种类型。在上述时区中,欧洲/布鲁塞尔使用夏令时(DST),因此您将在一年中的7个月内获得+02:00的偏移。

避免像EST一样使用三个字母的时区缩写。他们往往含糊不清。我喜欢像我在代码中那样以区域/城市格式指定您的时区。

编辑:这是从我的电脑运行的示例:

1
2
2017-11-28T19:04:22.917+01:00
myTask running @ 2017-11-28T19:04:23.156+01:00[Europe/Oslo]

如您所见,myTask在计划任务的指定firstTime之后运行了239毫秒。应该预期这种延迟。在第二次运行中,差异降至130毫秒。