Значения параметров Java Calendar не дают ожидаемых дат-времени
У меня есть метка времени, минуты, даты и миллисекунды, и я пытаюсь создать объект Date, представляющий время. Временная метка указана в восточном дневном свете.
При анализе проблемы я создал простой тестовый код, чтобы увидеть, что происходит, и заметил следующее:
Date today = new Date();
int hour = 4, min = 0, sec = 0, ms = 64;
boolean print = true;
Calendar cal = GregorianCalendar.getInstance();
if(print)
System.out.println("After initializing, time is: "+cal.getTime());
cal.clear();
if(print)
System.out.println("After clearing, time is: "+cal.getTime());
cal.setTime(today);
if(print)
System.out.println("After setting date, time is: "+cal.getTime());
cal.set(Calendar.HOUR_OF_DAY,hour);
if(print)
System.out.println("After setting hour, time is: "+cal.getTime());
cal.set(Calendar.MINUTE,min);
if(print)
System.out.println("After setting minute, time is: "+cal.getTime());
cal.set(Calendar.SECOND,sec);
if(print)
System.out.println("After setting second, time is: "+cal.getTime());
cal.set(Calendar.MILLISECOND,ms);
if(print)
System.out.println("After setting milliseconds, time is: "+cal.getTime());
cal.setTimeZone(TimeZone.getTimeZone("EDT"));
System.out.println("After setting time zone, time is: "+cal.getTime());
Это приводит к выходу:
After initializing, time is: Tue Jan 07 16:01:59 EST 2014
After clearing, time is: Thu Jan 01 00:00:00 EST 1970
After setting date, time is: Tue Jan 07 16:01:59 EST 2014
After setting hour, time is: Tue Jan 07 04:01:59 EST 2014
After setting minute, time is: Tue Jan 07 04:00:59 EST 2014
After setting second, time is: Tue Jan 07 04:00:00 EST 2014
After setting milliseconds, time is: Tue Jan 07 04:00:00 EST 2014
After setting time zone, time is: Tue Jan 07 04:00:00 EST 2014
Однако, если я немного изменил код:
boolean print = false;
Я получаю следующий (другой) результат (!)
After setting time zone, time is: Mon Jan 06 23:00:00 EST 2014
Кто-нибудь знает, почему это происходит?
Ответы
Ответ 1
Сначала необходимо установить часовой пояс. См. Определение GregorianCalendar.setTimeZone ниже:
public void setTimeZone(TimeZone value)
{
zone = value;
sharedZone = false;
/* Recompute the fields from the time using the new zone. This also
* works if isTimeSet is false (after a call to set()). In that case
* the time will be computed from the fields using the new zone, then
* the fields will get recomputed from that. Consider the sequence of
* calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
* Is cal set to 1 o'clock EST or 1 o'clock PST? Answer: PST. More
* generally, a call to setTimeZone() affects calls to set() BEFORE AND
* AFTER it up to the next call to complete().
*/
areAllFieldsSet = areFieldsSet = false;
}
Ответ 2
Как упоминалось gtgaxiola: Из Календарь Документация
В разделе Поле Манипуляции:
set (f, value) изменяет поле календаря f на значение. Кроме того, он устанавливает внутреннюю переменную-член, чтобы указать, что поле календаря f было изменено. Хотя поле календаря f изменяется немедленно, значение времени календаря в миллисекундах не пересчитывается до тех пор, пока не будет сделан следующий вызов get(), getTime(), getTimeInMillis(), add() или roll().
Проблема заключается в том, что ваш вызов getTime()
пересчитывает дату, но setTimeZone (..) не устанавливает внутреннюю переменную участника isTimeSet
в false. Таким образом, последняя строка в вашем первом выходе неверна для вас, потому что вы ожидаете, что часовой пояс будет рассмотрен, а это не так.
Ответ 3
Из Документация календаря
В разделе Поле Манипуляции:
set (f, value) изменяет поле календаря f на значение. Кроме того, он устанавливает внутреннюю переменную-член, чтобы указать, что поле календаря f было изменено. Хотя поле календаря f изменяется немедленно, значение времени календаря в миллисекундах не пересчитывается до тех пор, пока не будет сделан следующий вызов get(), getTime(), getTimeInMillis(), add() или roll().
Таким образом, множественные вызовы set() не запускают несколько ненужных вычислений. В результате изменения поля календаря с помощью set() другие поля календаря могут также изменяться в зависимости от поля календаря, значения поля календаря и системы календаря. Кроме того, get (f) не обязательно будет возвращать значение, заданное вызовом методу set после пересчета полей календаря. Специфика определяется конкретным классом календаря.
Ответ 4
Я бы просто сначала установил часовой пояс:
Calendar cal = GregorianCalendar.getInstance();
cal.clear();
cal.setTimeZone(TimeZone.getTimeZone("EDT"));
cal.setTime(today);
cal.set(Calendar.HOUR_OF_DAY,hour);
cal.set(Calendar.MINUTE,min);
cal.set(Calendar.SECOND,sec);
cal.set(Calendar.MILLISECOND,ms);
Однако он делал то, что должен, как сказано в комментариях 4 утра 11 вечера в EST.
И даже лучшее решение было бы не использовать Calendar
вообще, кроме, например, joda-time.
EDIT: Это дает мне подходящее время.
Date today = new Date();
int hour = 4, min = 0, sec = 0, ms = 64;
boolean print = false;
Calendar cal = GregorianCalendar.getInstance();
if(print)
System.out.println("After initializing, time is: "+cal.getTime());
cal.clear();
if(print)
System.out.println("After clearing, time is: "+cal.getTime());
cal.setTimeZone(TimeZone.getTimeZone("EDT"));
if(print)
System.out.println("After setting time zone, time is: "+cal.getTime());
cal.setTime(today);
if(print)
System.out.println("After setting date, time is: "+cal.getTime());
cal.set(Calendar.HOUR_OF_DAY,hour);
if(print)
System.out.println("After setting hour, time is: "+cal.getTime());
cal.set(Calendar.MINUTE,min);
if(print)
System.out.println("After setting minute, time is: "+cal.getTime());
cal.set(Calendar.SECOND,sec);
if(print)
System.out.println("After setting second, time is: "+cal.getTime());
cal.set(Calendar.MILLISECOND,ms);
if(print)
System.out.println("After setting milliseconds, time is: "+cal.getTime());
System.out.println("TIME: "+cal.getTime());
Ответ 5
Вы создаете экземпляр текущей даты в GMT:
Calendar cal = GregorianCalendar.getInstance();
cal.setTime(today);
Затем вы меняете время на 4 часа:
cal.set(Calendar.HOUR_OF_DAY,hour);
cal.set(Calendar.MINUTE,min);
cal.set(Calendar.SECOND,sec);
Затем вы конвертируете дату с GMT в EST, которая составляет 23 00:
cal.setTimeZone(TimeZone.getTimeZone("EDT"));
Отладчик поможет вам увидеть эти изменения на каждом шагу:)