Java: приведите пример на тему «Как установить другой часовой пояс для нескольких потоков?» - PullRequest
0 голосов
/ 27 марта 2020

Я только что столкнулся с ситуацией, когда я получаю java .util.Date объект, и я очень уверен, что это неправильно.

Сценарий:

В настоящее время я в системе, имеющей UT C в качестве часового пояса по умолчанию и пытающейся преобразовать дату IST (индийское стандартное время) в UT C, но она выводит неверное значение с разницей во времени на выходе 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 минут.

1 Ответ

1 голос
/ 27 марта 2020

Редактировать: IST неоднозначен

Из вашего комментария:

, например, если системный часовой пояс находится в UT C, а RSS-канал содержит ОпубликовалДата as Сб, 28 марта 2020 13:42:38 IST , библиотека разбирает его на Сб 28 марта 11:42:38 GMT 2020

Мне потребовалось немного расследования, но я воспроизвел поведение. Проблема в том, что IST неоднозначен: это может означать Iri sh Летнее время, Стандартное время Израиля, Стандартное время Индии, а также несколько других возможных сокращений часовых поясов. Подобная неоднозначность существует для очень многих сокращений часовых поясов, поэтому мы не должны полагаться на них. Поскольку вы просили разницу в 5 часов 30 минут, я полагаю, что вы рассчитывали на стандартное время Индии. Однако Java интерпретирует IST как стандартное время Израиля, которое по смещению +02: 00 от UTC / GMT. Это объясняет вашу наблюдаемую разницу в 2 часа. Он делает это, даже если Израиль не использует IST в это время года, он использует Израильское летнее время, IDT (по смещению +03: 00). Однако, когда вашим часовым поясом по умолчанию является Азия / Калькутта (стандартное время Индии), Java действительно означает, что IST означает это. Это объясняет, почему вы получили ожидаемый и правильный результат в этом случае.

Хотя исправить эту проблему в своем собственном коде не составит особого труда, убедить вашу библиотеку в том, что вы хотите, - это другой вопрос. На ум приходит пара вариантов:

  1. Лучшее решение - это убедить издателя своего RSS-канала не использовать IST в качестве часового пояса. Согласно спецификации RSS 2.0 дата публикации должна быть в формате RF C 822, а в соответствии с RF C 822 IST не является действительным часовым поясом. Итак, у вас есть аргументы. Из того, что я видел, GMT очень часто используется здесь и согласуется со спецификациями.
  2. Я не знаю rometools, поэтому могут быть возможности, о которых я не знаю, и которые вы следует расследовать, если вы можете. Конечно, вы также можете подать отчет об ошибке разработчикам библиотеки.
  3. Вы можете попробовать установить часовой пояс по умолчанию на Азия / Калькутта в тот момент, когда rometools создает экземпляр средства форматирования, которое он использует для анализа. Делает ли это это при инициализации, при первом вызове или при каждом вызове - проведите несколько экспериментов или, если хотите убедиться, изучите исходный код на GitHub. Если вам нужно, вы всегда можете установить часовой пояс по умолчанию обратно в UT C. Это очень плохой взлом, и не без риска случайных неверных результатов.
  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
    

    Теперь у вас есть разница 5 ч 30 мин между 13:42:38 в строке RSS и напечатанной 08:12:38. Обратите внимание, что если дата публикации в RSS-каналах указана в другом часовом поясе, Date, вероятно, будет правильным, а наша «коррекция» сделает ее ошибочной. Так что это тоже подход fr agile.

Оригинальный ответ

Первый ответ: не полагайтесь на часовой пояс по умолчанию вашей JVM. Укажите явный часовой пояс для ваших операций с датой и временем.

Вот совет, который можно использовать: используйте java .time, современный Java API даты и времени. Работать с ней намного приятнее, чем со старыми классами Date, TimeZone и друзьями. Эти классы не только старые, они, как правило, плохо разработаны, и они давно устарели. Кроме того, 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 и вы хотите, чтобы он был напечатан в UT C независимо от настройки часового пояса вашей JVM:

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

UT C время: 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]

Ссылки

...