4

Our application is storing all dates in UTC Timezone. Our Major-Business Unit however is in "Europe/Berlin" Timezone (+2, +1 depending on daylight saving). Therefore, when we determine what "month" a certain Timespan should equal, we want to use THAT Timezone.

I.e. The period given by the start Thursday, 31 October 2013, 23:00:0 UTC and end Friday, 29 November 2013, 23:00:00 UTC should bee globaly known as Nov 13, as for our Major Business Unit, the Times would be Friday, 1 November 2013, 00:00:00 to Friday, 30 November 2013, 00:00:00

The Server itself is running in UTC, so we have to switch the timezone, while calculating periodnames. We are using Joda Datetime on the backend, and I always can do something like:

DateTime now = new DateTime();
now.withZone(DateTimeZone.forID("Europe/Berlin"));

However there are a lot of calculations going on, and i wonder if there is a better way, to do ALL calculations within a certain timezone?

If it would be a stand alone application, one could use something like that:

DateTimeZone old = DateTimeZone.getDefault();
DateTimeZone.setDefault(DateTimeZone.forID("Europe/Berlin"));

//do all work

DateTimeZone.setDefault(old);

However since it is a server environment, modifying the default Timezone may lead to wrong results on other calculations happening on other threads.

so, I wonder, if there is nothing like the c# using directive in java? Beside the wrong syntax i'm looking for something like this:

using(DateTimeZone.forID("Europe/Berlin")){
   //do all work, where JUST all DateTime usings inside this using
   //Statement are affected
}

Edit: Some Little Test shows the problem: Even when running on 2 Threads, the "Default" TimeZone is used in both threads. So, whatever timezone is set LAST will be used for both threads. (Of course that's what "default" Timezone is for.)

@Test
    public final void testTimeZoneThreaded() {

        Thread EuropeThread = new Thread(new Runnable() {
            @Override
            public void run() {
                DateTimeZone.setDefault(DateTimeZone.forID("Europe/Berlin"));

                int i = 0;
                while (i++ < 10) {
                    DateTime now = new DateTime();
                    System.out.println("It is now " + now + " in TimeZone " + DateTimeZone.getDefault().toString());

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread UTCThread = new Thread(new Runnable() {
            @Override
            public void run() {
                DateTimeZone.setDefault(DateTimeZone.forID("UTC"));

                int i = 0;
                while (i++ < 10) {
                    DateTime now = new DateTime();
                    System.out.println("It is now " + now + " in TimeZone " + DateTimeZone.getDefault().toString());

                    try {
                        Thread.sleep(800);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });

        EuropeThread.start();
        UTCThread.start();

        while (UTCThread.isAlive()) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {

            }
        }

    }

Beeing able to use different defaults on different threads would help, also.

Another thought was to extend the JodaDateTime and override the to string() method with all of its overloads:

class MyDateTime extends DateTime{
  @Override
  public String toString(){
    return super().withZone(DateTimeZone.for("Europe/Berlin")).toString();
  }

  @Override
  public String toString(String format){
    return super().withZone(DateTimeZone.for("Europe/Berlin")).toString(format);
  }
}
  • DateTime is "final" ... :-(

So, for the moment i always need to handle it manually, when generating period names (either years, quartals, months, weeks, ...)

public String getMonthPeriodName() {
        DateTime firstOfMonth = this.getPeriodStart().withZone(DateTimeZone.forID("Europe/Berlin")).dayOfMonth().withMinimumValue().millisOfDay().withMinimumValue();
    DateTime lastOfMonth = this.getPeriodEnd().withZone(DateTimeZone.forID("Europe/Berlin")).dayOfMonth().withMaximumValue().millisOfDay().withMaximumValue();

        if (firstOfMonth.isEqual(getPeriodStart()) && lastOfMonth.isEqual(getPeriodEnd())) {
            // full month.
            return firstOfMonth.withZone(DateTimeZone.forID("Europe/Berlin")).toString("MMM yyyy", Locale.US);
        } else {
            // just partial month.
            String Basename = firstOfMonth.withZone(DateTimeZone.forID("Europe/Berlin")).toString("MMM yyyy", Locale.US);
            String f = getPeriodStart().withZone(DateTimeZone.forID("Europe/Berlin")).dayOfMonth().getAsShortText();
            String t = getPeriodEnd().withZone(DateTimeZone.forID("Europe/Berlin")).dayOfMonth().getAsShortText();
            return Basename + " (" + f + " to " + t + ")";
        }
    }
Roman C
  • 49,761
  • 33
  • 66
  • 176
dognose
  • 20,360
  • 9
  • 61
  • 107
  • Not sure if the USING or it's equivalent in java is the right answer to this problem: http://stackoverflow.com/questions/2943542/using-keyword-in-java – Vladimir Bozic Nov 07 '13 at 11:18
  • @VladimirBozic That's why I wrote *searching something like* - not sure, if that's possible. It would also be helpfull to start a thread within a certain timezone or anything with the same effect. – dognose Nov 07 '13 at 11:24
  • The global timezone for Joda is static and volatile: as you show it by your example, the value is available for all threads. – zenbeni Nov 07 '13 at 12:58
  • Maybe you should look at classloading stuff and for instance OSGi, in which you can define a bunch of classes independant from your 'classic' classloader / application context. But it indeed seems like overdesign. – zenbeni Nov 07 '13 at 13:05

1 Answers1

1

I would probably not look to setting defaults, because then you loose any verbosity in your code that would help you know what was really going on.

This is a very simple recommendation, but perhaps you should just use a local variable.

...
DateTimeZone tz = DateTimeZone.forID("Europe/Berlin");
DateTime firstOfMonth = this.getPeriodStart().withZone(tz).dayOfMonth().withMinimumValue().millisOfDay().withMinimumValue();
DateTime lastOfMonth = this.getPeriodEnd().withZone(tz).dayOfMonth().withMaximumValue().millisOfDay().withMaximumValue();
... etc ...

Looking at your code, I see many redundancies where some other simple local variables would clean things up even better.

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • The code sample was just an example, what i would need to do, without setting a default. What you are describing - setting it just ONCE - is what i want to achieve - but without using the "default" setting :) – dognose Nov 08 '13 at 09:53
  • ah, i see what you did there - well, using `tz` like this is just another version of what I did. wouldn't change the fact, that i NEED to take care for setting the tz, when required. In fact, it is saved in a class property, just moved it into the method body so i don't need to provide the whole class. (Same for patterns ofc.) – dognose Nov 08 '13 at 10:16
  • I agree with Matt Johnson. You can pass that variable (`tz`) to the constructor of a new DateTime: `new org.joda.time.DateTime( tz )`. When you explicitly want to use UTC, Joda-Time has a built-in constant `.UTC`, seen here passed to a constructor: `new org.joda.time.DateTime( org.joda.time.DateTimeZone.UTC )`. You can easily convert between time zones: `myUtcDateTime.toDateTime( tz )` or opposite `myBerlinDateTime.toDateTime( org.joda.time.DateTimeZone.UTC )`. Tip: Make a habit to always pass desired time zone to constructor, so your code succeeds even if server’s zone is not set to UTC. – Basil Bourque Nov 09 '13 at 06:06