0

I am using timer to perform certain tasks in certain period. This method requires Date type if you want to make the task start from that date and repeat in every period.

I have been trying to find solution for this for hours but couldn't come up with anything. I create calendar and set a timezone for it. Then when I get date from that calendar this date contains my LOCAL TIME. I tried getting string from Date using SimpleDataFormat and it worked because you set timezone for SimpleDataFormat separately. Then even tried parsing that string into the date but it still didn't work. I'm kinda hopeless at this moment. What's the point of setting timezone for calendar if I'm not able to use it. I could still do something if it wasn't getting my local time but instead it was getting UTC. But that's not the case either. I don't want to make this application run based on the computer's local time. That's absurd it should be able to get local time and turn it into the time zone I want and use it like that.

Code:

    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.

Sample Output:

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
Vsky
  • 59
  • 1
  • 9
  • Rather than the outdated `Calendar` I would recommend the modern `ZonedDateTime` for initializing your `Date` (and yes, `java.util.Timer` does need a `Date`). When you print your `ZonedDateTime`, you can see the time zone is right. Use `Date.from(yourZonedDateTime.toInstant())` for converting to a `Date`. – Ole V.V. Nov 27 '17 at 12:20
  • 1
    The `Date` class has many design flaws, one of them being the `toString` method applying the JVM’s current default time zone while generating a String to represent its value. This class, along with `Calendar` is now legacy and should be avoided, supplanted by the java.time classes. To convert between the legacy and modern classes, look to new methods added to the old classes, specifically `Date.from( instant )`. – Basil Bourque Nov 27 '17 at 15:59

2 Answers2

3

java.util.Date objects don't contain time zone information. They represent an instant in the time continuum that is the same instant for everyone regardless of whether they so happen to be in Paris or New York at the moment.

However, when you try to print a Date object with System.out.println() it needs to try and describe itself. So it will need to describe the date and time it represents, but to do that it first needs to choose a time zone to represent this date and time in, since it doesn't have one. For convenience it chooses the user's system time zone.

You're not going to use your Date by calling System.out.println() with it, so you shouldn't care what System.out.println() does with it.

kumesana
  • 2,495
  • 1
  • 9
  • 10
  • No I won't be using it with system.out.println() I'll use it in timer instead. And when I send this date into timer, timer will get the date depending on my local time too. That is the problem here. For instance I want a timer that will start at 22.00 UTC+1 and repeat every 24 hour. I want another timer that will start at 22.00 UTC+7 and repeat every 24 hour. How do I do this if Date is getting my local time. I want it to get UTC+1 and UTC+7 times. – Vsky Nov 27 '17 at 11:52
  • Again, Date is not getting any time zone at all. Things that use Date may, or may not. Depends if they need a time zone to use the Date. You're saying that your Timer will use your local time, but a typical timer just wants to know the instant at which it needs to run, and the Date does describe this instant entirely, therefore it doesn't need a time zone at all. How do you know that the Timer you're using is atypical and will try and use a time zone? – kumesana Nov 27 '17 at 12:00
  • You are right I see now. Well this is my cue to sleep I guess. Thanks for your answer. – Vsky Nov 27 '17 at 12:11
1

I wish I could tell you to stay away from the long outdated Date class. Today we have so much better in java.time, the modern Java date and time API. However, you are correct that java.util.Timer does need a Date for scheduling a task at a specific date and time. Still, since the modern API is so much nicer to work with, I recommend you use it for initializing your Date:

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

I appears to me you aren’t really using time zones, just offsets from GMT (or UTC). So I put in some ZoneOffsets in the code. The result of printing euTime just now was

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

You can see that the offset is +01:00 as expected. Then, as kumesana already said in the other answer, don’t print the resulting Date, just trust that it’s OK.

If you do prefer real time zones, use for example:

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

The rest of the code is the same. That’s true: OffsetDateTime.now() accepts either a ZoneOffset or a ZoneId, so you can even mix both types. Of the above time zones, Europe/Brussels uses summer time (DST), so you will get an offset of +02:00 during 7 months of the year.

Avoid the three letter time zone abbreviations like EST. They are often ambiguous. Do prefer to specify your time zones in the region/city format as I do in my code.

EDIT: Here’s an example run from my computer:

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

As you can see, myTask ran 239 milliseconds after the specified firstTime for the scheduled task. Such latency should be expected. In a second run, the difference was down to 130 ms.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Well you tell me to ignore print result of Date but it is not okay. Did you run that code? It does not start at current time for EU. Anyway I took different approach with combination of LocalTime, LocalDateTime and ZonedDateTime. I now get the epoch seconds and use it as initial delay. I'm using ScheduledExecutorService instead of timer now but I think timer also has that kind of scheduler (with initial delay and period). – Vsky Nov 28 '17 at 17:26
  • Yes, I ran the code. I also printed the `Date`, but my time zone is also UTC+1 in winter, so not surprisingly I got the current time and the same offset that I had put in (+01:00) (this will not be the case on JVMs in other time zones). `SchedulesExecutorService` is a nice alternative, you are right that it is similar to `Timer,` but more modern and with more possibilities. – Ole V.V. Nov 28 '17 at 17:57