0

I just ran into a situation where I am getting java.util.Date object and I am very much sure that it is wrong.

Scenario:

Currently, I am on a system having UTC as default time zone and trying to convert IST(Indian Standard Time) date to UTC, but it is printing me wrong value having a time difference of 2 hours in its output but it should be 5 hr 30 min of difference.

So, I decided to change the thread timezone, but unable to do so.

Please suggest something. Here is the code.

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);
    }

  }

}

I am getting 2 hours of difference here but it should be 5hr and 30 mins.

  • What is the current timezone your application is using? – Tarun Mar 27 '20 at 10:03
  • same as the system i.e UTC. – Dhiraj Singh Mar 27 '20 at 10:13
  • You should post your code converting from IST to UTC timezone. I don't think you need to set timezone explicitly for a thread. – Tarun Mar 27 '20 at 10:19
  • java.util.date automatically converts any date to its local timezone. It is simple to reproduce. There is a 3rd party : [link](http://rometools.github.io/rome/) library that i am using to read rss feed, and getting issues while fetching **publishedDate** . It is automatically converting IST to UTC. – Dhiraj Singh Mar 27 '20 at 10:32
  • 3
    Thread timezone? What do you mean? Timezones are applied to dates, not to threads. Show us your code. – m0skit0 Mar 27 '20 at 10:33
  • I am not saying to apply Timezone on threads. Please read carefully. I can set default timezone for whole JVM by using **TimeZone.setDefault()** , the same way I want it to work for individual threads with different timezone Id. – Dhiraj Singh Mar 27 '20 at 10:45

1 Answers1

2

Edit: IST is ambiguous

From your comment:

for example if the system timezone is in UTC and rss feed contains the publishedDate as Sat, 28 Mar 2020 13:42:38 IST, the libary parses it to Sat Mar 28 11:42:38 GMT 2020

It took me a bit of investigation, but I have reproduced the behaviour. The problem is that IST is ambiguous: it may mean Irish Summer Time, Israel Standard Time, India Standard Time plus a few other possible time zone abbreviations. A similar ambiguity exists for very many time zone abbreviations, so we should not rely on them. Since you asked for a difference of 5 h 30, I take it that you intended India Standard Time. However, Java interprets IST as Israel Standard Time, which is at offset +02:00 from UTC/GMT. This explains your observed 2 hours difference. It does so even though Israel doesn’t use IST at this time of year, it uses Israel Daylight Time, IDT (at offset +03:00). However, when your default time zone is Asia/Kolkata (India Standard Time), Java does interpret IST to mean this. This explains why you got the expected and correct result in this case.

While fixing this problem in one’s own code would not be too hard, how to persuade your library to do as you want is a different matter. A couple of options come to mind:

  1. The best solution is if you can persuade the publisher of your RSS feed not to use IST as time zone. According to the RSS 2.0 specification, the published date should be in RFC 822 format, and according to RFC 822, IST is not a valid time zone. So you have got arguments. From what I have seen, GMT is very often used here and is in agreement with the specs.

  2. I don’t know rometools, so there could be possibilities that I don’t know of and which you should investigate if you can. You may of course also file a bug report with the library developers.

  3. You may try setting your default time zone to Asia/Kolkata at the time when rometools instantiates the formatter that it uses for parsing. Whether it does this on initialization, on the first call or on every call — make some experiments, or if you want to be sure, inspect the source code on GitHub. If you need to, you can always set the default time zone back to UTC afterward. It’s a very bad hack and not without the risk of occasional incorrect results.

  4. If the published date from your RSS feed is always in IST in the sense India Standard Time, you may of course correct the wrong date that you get:

    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);
    

    Example output assuming that rometools started out from parsing 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
    

    Now you’ve got the difference of 5 h 30 min between the 13:42:38 in the RSS string and the 08:12:38 printed. Please note that if the published date in the RSS feeds comes in some other time zone, the Date will probably be correct and our “correction” make it faulty. So it’s a fragile approach too.

Original answer

The first answer is: Do not rely on the default time zone of your JVM. Specify explicit time zone for your date and time operations.

There’s a tip that goes along: Use java.time, the modern Java date and time API. It is so much nicer to work with than the old classes Date, TimeZone and friends. These classes are not only old, they are generally poorly designed and they are long outdated. In addition, java.time generally makes it a lot more natural to provide an explicit time zone for your date and time operations. For example:

    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);

Output is:

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

The difference is 5 hours 30 minutes, as you said it should. The code will give the same output regardless of the default time zone of your JVM.

Edit: If you’ve got a java.util.Date and you want it printed in UTC no matter the time zone setting of your JVM:

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

UTC time: 2020-03-28T10:11:12.345Z

A different time zone for each thread? ThreadLocal

You can have a separate time zone for each thread. What you cannot have is a default time zone per thread. The default time zone of your JVM is exactly that: of your JVM. So if you change it, the change has effect for all threads in the JVM.

If you want, each thread can keep a time zone as a ThreadLocal. It will be the resposibility of the thread to use the ThreadLocal and not the JVM’s default time zone.

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));
        }
    }
    
}

Example output:

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]

Links

Community
  • 1
  • 1
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Thanks, Ole V.V. for a descriptive answer. Currently, my problem is that i have used a library[http://rometools.github.io/rome/](rometools) which is parsing the rss feeds and returning me **publishedDate** (a java.util.date object) and i have not found any other way to change the timezone of it. – Dhiraj Singh Mar 28 '20 at 10:12
  • There is no such thing as changing the time zone of a `java.util.Date`. Even though it looks like it when printing, a `Date` hasn’t got any time zone. See for example [this question](https://stackoverflow.com/questions/1812700/timezone-problem-in-java). – Ole V.V. Mar 28 '20 at 10:19
  • Adding code to the question was a very good idea! When printing the `Date`, it uses the JVM default time zone for rendering the string to be printed (confusing). I don’t understand how you got a time difference of *2 hours*, though. – Ole V.V. Mar 28 '20 at 10:29
  • 1
    Ole V.V , actually the date returned by the library is wrong. – Dhiraj Singh Mar 28 '20 at 10:30
  • for example if the system timezone is in UTC and rss feed contains the **publishedDate** as **Sat, 28 Mar 2020 13:42:38 IST** , the libary parses it to **Sat Mar 28 11:42:38 GMT 2020** – Dhiraj Singh Mar 28 '20 at 10:36
  • 1
    If I change the system timezone to "IST", then the date gets parsed right having 5hrs and 30 mins of difference. – Dhiraj Singh Mar 28 '20 at 10:37
  • Thanks, Ole V.V. I greatly appreciate your help. – Dhiraj Singh Mar 28 '20 at 10:50