【问题标题】:Java: Please provide an example on "How to set different timezone for multiple thread?"Java:请提供有关“如何为多个线程设置不同的时区?”的示例
【发布时间】:2020-03-27 09:56:20
【问题描述】:

我刚刚遇到了一种情况,我得到了 java.util.Date 对象,我非常确定它是错误的。

场景:

目前,我在以 UTC 作为默认时区并尝试将 IST(印度标准时间)日期转换为 UTC 的系统上,但它在输出中打印出时差为 2 小时的错误值,但它应该相差 5 小时 30 分钟。

所以,我决定更改线程时区,但无法这样做。

请提出一些建议。这是代码。

import com.rometools.rome.feed.rss.Item;
import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.io.SyndFeedInput;
import com.rometools.rome.io.XmlReader;
import java.net.URL;
import java.util.Date;

public class NewsService {

  public static void main(String[] args) {

    NewsService newsService = new NewsService();
    try {
      newsService.printNews("https://timesofindia.indiatimes.com/rssfeeds/296589292.cms");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void printNews(String url) throws Exception {

    // read RSS
    SyndFeedInput in = new SyndFeedInput();
    in.setPreserveWireFeed(true);
    SyndFeed feed = in.build(new XmlReader(new URL(url)));

    for (SyndEntry syndEntry : feed.getEntries()) {
      Object obj = syndEntry.getWireEntry();
      Item item = (Item) obj;

      Date date = ((Item) obj).getPubDate();

      System.out.println(item.getTitle() + " " + date);
    }

  }

}

我在这里得到了 2 小时的差异,但应该是 5 小时 30 分钟。

【问题讨论】:

  • 您的应用程序使用的当前时区是什么?
  • 与系统相同,即UTC。
  • 您应该发布从 IST 转换为 UTC 时区的代码。我认为您不需要为线程明确设置时区。
  • java.util.date 自动将任何日期转换为其本地时区。很容易复制。有一个第 3 方:link 库,我正在使用它来阅读 rss 提要,并在获取 publishedDate 时遇到问题。它会自动将 IST 转换为 UTC。
  • 线程时区?你什么意思?时区应用于日期,而不是线程。向我们展示您的代码。

标签: java multithreading timezone rss rss-reader


【解决方案1】:

编辑:IST 不明确

来自您的评论:

例如,如果系统时区是 UTC 并且 rss 提要包含 publishedDate2020 年 3 月 28 日星期六 13:42:38 IST,库将其解析为 2020 年 3 月 28 日星期六 11:42:38 GMT

我进行了一些调查,但我已经重现了这种行为。问题在于 IST 不明确:它可能表示爱尔兰夏令时间、以色列标准时间、印度标准时间以及其他一些可能的时区缩写。很多时区缩写也存在类似的歧义,因此我们不应依赖它们。由于您要求时差为 5 小时 30 分,因此我认为您打算使用印度标准时间。但是,Java 将 IST 解释为以色列标准时间,它与 UTC/GMT 的偏移量为 +02:00。这解释了您观察到的 2 小时差异。即使以色列在一年中的这个时候不使用 IST,它也会这样做,它使用以色列夏令时,IDT(偏移量 +03:00)。但是,当您的默认时区是亚洲/加尔各答(印度标准时间)时,Java 会将 IST 解释为这个意思。这解释了为什么在这种情况下您得到了预期的正确结果。

虽然在自己的代码中解决这个问题不会太难,但如何说服你的库按照你的意愿去做是另一回事。我想到了几个选项:

  1. 最好的解决方案是说服 RSS 提要的发布者不要使用 IST 作为时区。根据 RSS 2.0 规范,发布日期应为 RFC 822 格式,根据 RFC 822,IST 不是有效时区。所以你有论据。据我所见,GMT 在这里经常使用,并且符合规范。

  2. 我不了解 rometools,因此可能存在我不知道的可能性,如果可以的话,您应该调查一下。当然,您也可以向库开发人员提交错误报告。

  3. 您可以尝试在 rometools 实例化它用于解析的格式化程序时将默认时区设置为亚洲/加尔各答。无论是在初始化、第一次调用还是每次调用时都这样做——做一些实验,或者如果你想确定,检查 GitHub 上的源代码。如果需要,您可以随时将默认时区设置回 UTC。这是一个非常糟糕的 hack,并且存在偶尔出现错误结果的风险。

  4. 如果您的 RSS 提要中的发布日期始终采用印度标准时间意义上的 IST,您当然可以更正错误的日期:

    System.out.println("Incorrect date from rometools: " + javaUtilDateFromRometools);
    ZonedDateTime correctedDateTime = javaUtilDateFromRometools.toInstant()
            .atZone(ZoneOffset.ofHours(2)) // Israel Standard Time, but +02:00 all year, so not Asia/Jerusalem
            .withZoneSameLocal(ZoneId.of("Asia/Kolkata"))
            .withZoneSameInstant(ZoneOffset.UTC);
    System.out.println("UTC time: " + correctedDateTime);
    

    假设 rometools 从解析 Sat, 28 Mar 2020 13:42:38 IST 开始的示例输出:

    Incorrect date from rometools: Sat Mar 28 11:42:38 UTC 2020
    UTC time: 2020-03-28T08:12:38Z
    

    现在您已经得到 RSS 字符串中的 13:42:38 和打印的 08:12:38 之间的 5 小时 30 分钟的差异。请注意,如果 RSS 提要中的发布日期出现在其他时区,Date 可能是正确的,而我们的“更正”会导致错误。所以这也是一种脆弱的方法。

原答案

第一个答案是:不要依赖 JVM 的默认时区。为您的日期和时间操作指定明确的时区。

有一个提示:使用 java.time,现代 Java 日期和时间 API。与旧课程 DateTimeZone 和朋友们一起工作要好得多。这些类不仅陈旧,而且通常设计不佳,而且早已过时。此外,java.time 通常可以更自然地为您的日期和时间操作提供明确的时区。例如:

    ZonedDateTime istTime = ZonedDateTime.of(
            2020, 3, 27, 12, 34, 56, 123456000, ZoneId.of("Asia/Kolkata"));
    System.out.println("IST time: " + istTime);
    ZonedDateTime utcTIme = istTime.withZoneSameInstant(ZoneOffset.UTC);
    System.out.println("UTC time: " + utcTIme);

输出是:

IST time: 2020-03-27T12:34:56.123456+05:30[Asia/Kolkata]
UTC time: 2020-03-27T07:04:56.123456Z

正如您所说,差异是 5 小时 30 分钟。无论您的 JVM 的默认时区如何,代码都会给出相同的输出。

编辑:如果您有一个java.util.Date,并且无论您的 JVM 的时区设置如何,都希望它以 UTC 格式打印:

    OffsetDateTime utcTime = yourJavaUtilDate.toInstant().atOffset(ZoneOffset.UTC);
    System.out.println("UTC time: " + utcTime);

UTC时间:2020-03-28T10:11:12.345Z

每个线程的时区不同? ThreadLocal

您可以为每个线程设置单独的时区。您不能拥有的是每个线程的默认时区。您的 JVM 的默认时区正是:您的 JVM。所以如果你改变它,这个改变对JVM中的所有线程都有影响。

如果需要,每个线程都可以将时区保留为ThreadLocal。使用 ThreadLocal 而不是 JVM 的默认时区将是线程的责任。

public class SetDifferentTimeZonesForThreads {

    public static void main(String[] args) {
        ZonedDateTime zdt = ZonedDateTime.of(
                2020, 3, 27, 23, 30, 9, 0, ZoneId.of("Asia/Kolkata"));
        Thread tUtc = new MyThread(zdt, "Etc/UTC");
        Thread tIst = new MyThread(zdt, "Asia/Kolkata");
        tUtc.start();
        tIst.start();
    }
}

class MyThread extends Thread {
    
    private ZonedDateTime zdtToConvert;
    private ThreadLocal<ZoneId> threadTimeZone;
    
    public MyThread(ZonedDateTime zdt, String zoneIdString) {
        zdtToConvert = zdt;
        threadTimeZone = ThreadLocal.withInitial(() -> ZoneId.of(zoneIdString));
    }
    
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            ZoneId zone = threadTimeZone.get();
            System.out.format("In %-12s: %s%n", zone, zdtToConvert.withZoneSameInstant(zone));
        }
    }
    
}

示例输出:

In Asia/Kolkata: 2020-03-27T23:30:09+05:30[Asia/Kolkata]
In Asia/Kolkata: 2020-03-27T23:30:09+05:30[Asia/Kolkata]
In Etc/UTC     : 2020-03-27T18:00:09Z[Etc/UTC]
In Asia/Kolkata: 2020-03-27T23:30:09+05:30[Asia/Kolkata]
In Etc/UTC     : 2020-03-27T18:00:09Z[Etc/UTC]
In Asia/Kolkata: 2020-03-27T23:30:09+05:30[Asia/Kolkata]
In Asia/Kolkata: 2020-03-27T23:30:09+05:30[Asia/Kolkata]
In Etc/UTC     : 2020-03-27T18:00:09Z[Etc/UTC]
In Etc/UTC     : 2020-03-27T18:00:09Z[Etc/UTC]
In Etc/UTC     : 2020-03-27T18:00:09Z[Etc/UTC]

链接

【讨论】:

  • 谢谢,Ole V.V.以获得描述性答案。目前,我的问题是我使用了一个库 [rometools.github.io/rome/](rometools),它正在解析 rss 提要并返回我 publishedDate (一个 java.util.date 对象),我还没有找到任何其他方法更改它的时区。
  • 没有改变java.util.Date的时区这样的事情。即使打印时看起来像,Date 也没有任何时区。参见例如this question
  • 在问题中添加代码是个好主意!打印Date 时,它使用JVM 默认时区来呈现要打印的字符串(令人困惑)。不过,我不明白您是如何得到 2 小时 的时差的。
  • Ole V.V ,实际上库返回的日期是错误的。
  • 如果我将系统时区更改为“IST”,则日期会被正确解析,相差 5 小时 30 分钟。
猜你喜欢
  • 2023-03-26
  • 2019-10-15
  • 2013-03-18
  • 1970-01-01
  • 2013-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多