2013-11-07 3 views
4

Наше приложение хранит все даты в часовой пояс UTC. Однако наша основная бизнес-единица находится в часовом поясе «Европа/Берлин» (+2, +1 в зависимости от летнего времени). Поэтому, когда мы определяем, какой «месяц» определенный период времени должен равняться, мы хотим использовать THAT Timezone.Переключатель Часовой пояс для расчета

I.e. Период задается в начале Thursday, 31 October 2013, 23:00:0 UTC и в конце Friday, 29 November 2013, 23:00:00 UTC должны пчелы globaly известный как Nov 13, как и для нашего Крупной Business Unit, Таймс будет Friday, 1 November 2013, 00:00:00 к Friday, 30 November 2013, 00:00:00

Сервер сам работает в UTC, поэтому мы должны переключить часовой пояс , при расчете периодов. Мы используем Joda DATETIME на внутреннем интерфейсе, и я всегда могу сделать что-то вроде:

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

Однако там много вычислений происходит, и я интересно, если есть лучший способ, чтобы сделать все расчеты в рамках определенный часовой пояс?

Если бы в одиночку стенд приложение, можно было бы использовать что-то вроде этого:

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

//do all work 

DateTimeZone.setDefault(old); 

Однако поскольку это серверная среда, изменение значения по умолчанию Timezone может привести к ошибочным результатам на других расчетов, происходящих на других потоки.

так, интересно, если нет ничего похожего на директиву C#, использующую java? Помимо неправильного синтаксиса я искал что-то вроде этого:

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

Edit: Некоторые Маленький тест показывает проблему: Даже при работе на 2 нитки, «по умолчанию» TimeZone используется в обоих потоках. Итак, независимо от того, какой часовой пояс установлен, LAST будет использоваться для обоих потоков. (Конечно, это то, что «по умолчанию» Timezone для.)

@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) { 

      } 
     } 

    } 

неоспоримым возможность использовать различные значения по умолчанию на разных потоках помогло бы также.

Другая мысль была продлить JodaDateTime и переопределить в строке() метод со всеми его перегрузками:

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 является "окончательным" ... :-(

Итак, на данный момент я всегда должен обрабатывать его вручную при генерации имен периодов (годы, кварталы, месяцы, недели, ...)

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 + ")"; 
     } 
    } 
+0

Не уверен, что ИСПОЛЬЗОВАНИЕ или его эквивалент в java является правильным ответом на эту проблему: http: // stackoverflow.com/questions/2943542/using-keyword-in-java –

+0

@VladimirBozic Вот почему я написал * поиск чего-то вроде * - не уверен, если это возможно. Было бы также полезно начать поток в определенный часовой пояс или что-либо подобное с таким же эффектом. – dognose

+0

Глобальный часовой пояс для Joda статичен и изменчив: как вы покажете его в своем примере, значение доступно для всех потоков. – zenbeni

ответ

1

Я бы, вероятно, не посмотрел на настройку по умолчанию, потому что тогда вы теряете какую-либо многословность в своем коде, которая поможет вам узнать, что происходит на самом деле.

Это очень простая рекомендация, но, возможно, вам стоит просто использовать локальную переменную.

... 
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 ... 

Глядя на ваш код, я вижу много избыточности, когда некоторые другие простые локальные переменные еще лучше очистят ситуацию.

+0

Образец кода был всего лишь примером того, что мне нужно было сделать, не устанавливая значение по умолчанию. То, что вы описываете - установка его только один раз - это то, что я хочу достичь, но без использования настройки по умолчанию :) – dognose

+0

ах, я вижу, что вы там делали - ну, используя 'tz', как это всего лишь еще одна версия что я сделал. не изменит тот факт, что я НЕОБХОДИМО следить за настройкой tz, когда это необходимо. Фактически, он сохраняется в свойстве класса, просто перемещает его в тело метода, поэтому мне не нужно предоставлять весь класс. (То же самое для шаблонов ofc.) – dognose

+0

Я согласен с Мэттом Джонсоном. Вы можете передать эту переменную ('tz') в конструктор нового DateTime:' new org.joda.time.DateTime (tz) '. Когда вы явно хотите использовать UTC, Joda-Time имеет встроенную константу '.UTC', которую здесь передают конструктору:' new org.joda.time.DateTime (org.joda.time.DateTimeZone.UTC) ' , Вы можете легко конвертировать между часовыми поясами: 'myUtcDateTime.toDateTime (tz)' или напротив 'myBerlinDateTime.toDateTime (org.joda.time.DateTimeZone.UTC)'. Совет. Сделайте привычку всегда передавать желаемый часовой пояс для конструктора, поэтому ваш код будет успешным, даже если зона сервера не установлена ​​в UTC. –

Смежные вопросы