У нас есть приложение, в котором время имеет решающее значение. Мы используем joda для преобразования времени и хранения всех данных в формате UTC. Мы были в производстве некоторое время, и все было идеально, НО ...
Теперь мы замечаем, что события, происходящие за несколько часов до изменения времени, уже слишком рано преобразуются! Фактически время UTC, сохраненное в базе данных, отключено на час.
Вот пример. Мое событие происходит в 6.11.2010 в 9 вечера по тихоокеанскому времени и обычно сохраняется как 11.07.2010 в 4 часа утра. Однако, поскольку летнее время закончилось 7-го числа (предположительно, в 2 часа ночи), это время сдвигается и сохраняется как 11/7/2010 @ 5 утра.
Нам нужно, чтобы изменение DST не регистрировалось до тех пор, пока оно фактически не произойдет в области PST, в 2:00 по тихоокеанскому времени. Я предположил, что joda справится с этим, тем более, что это намного лучше, чем стандартная функциональность java.
Любая ваша обратная связь будет полезна, особенно если вы сможете получить ее до завтрашнего дня! После этого это будет академическим, но все же полезным обсуждением.
Вот часть кода, который мы используем для изменения часового пояса и получения результата в виде обычного объекта даты Java.
public Date convertToTimeZone(Date dt, TimeZone from, TimeZone to){
DateTimeZone tzFrom = DateTimeZone.forTimeZone(from);
DateTimeZone tzTo = DateTimeZone.forTimeZone(to);
Date utc = new Date(tzFrom.convertLocalToUTC(dt.getTime(), false));
Date convertedTime = new Date(tzTo.convertUTCToLocal(utc.getTime()));
return convertedTime;
}
Редактировать: Пример кода для комментариев ниже
public Date convert(Date dt, TimeZone from, TimeZone to) {
long fromOffset = from.getOffset(dt.getTime());
long toOffset = to.getOffset(dt.getTime());
long convertedTime = dt.getTime() - (fromOffset - toOffset);
return new Date(convertedTime);
}
Полный пример модульного теста
package com.test.time;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Instant;
import org.junit.Before;
import org.junit.Test;
public class TimeTest {
Calendar nov6;
Calendar nov1;
Calendar nov12;
@Before
public void doBefore() {
// November 1st 2010, 9:00pm (DST is active)
nov1 = Calendar.getInstance();
nov1.setTimeZone(TimeZone.getTimeZone("US/Arizona"));
nov1.set(Calendar.HOUR_OF_DAY, 21);
nov1.set(Calendar.MINUTE, 0);
nov1.set(Calendar.SECOND, 0);
nov1.set(Calendar.YEAR, 2010);
nov1.set(Calendar.MONTH, 10); // November
nov1.set(Calendar.DATE, 1);
// November 6st 2010, 9:00pm (DST is still active until early AM november 7th)
nov6 = Calendar.getInstance();
nov6.setTimeZone(TimeZone.getTimeZone("US/Arizona"));
nov6.set(Calendar.HOUR_OF_DAY, 21);
nov6.set(Calendar.MINUTE, 0);
nov6.set(Calendar.SECOND, 0);
nov6.set(Calendar.YEAR, 2010);
nov6.set(Calendar.MONTH, 10); // November
nov6.set(Calendar.DATE, 6);
// November 12th 2010, 9:00pm (DST has ended)
nov12 = Calendar.getInstance();
nov12.setTimeZone(TimeZone.getTimeZone("US/Arizona"));
nov12.set(Calendar.HOUR_OF_DAY, 21);
nov12.set(Calendar.MINUTE, 0);
nov12.set(Calendar.SECOND, 0);
nov12.set(Calendar.YEAR, 2010);
nov12.set(Calendar.MONTH, 10); // November
nov12.set(Calendar.DATE, 12);
}
@Test
public void test1() {
// System.out.println("test1");
timeTestJava(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJodaOld(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJodaNew(nov1.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJava(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJodaOld(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJodaNew(nov6.getTime(), "equivalent", "US/Arizona", "US/Pacific");
timeTestJava(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific");
timeTestJodaOld(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific");
timeTestJodaNew(nov12.getTime(), "minus1", "US/Arizona", "US/Pacific");
}
private void timeTestJodaOld(Date startTime, String text, String from, String to) {
System.out.println("joda_old: " + startTime);
Date output = convertJodaOld(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to));
System.out.println(text + ": " + output + "\n");
}
private void timeTestJodaNew(Date startTime, String text, String from, String to) {
System.out.println("joda_new: " + startTime);
Date output = convertJodaNew(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to));
System.out.println(text + ": " + output + "\n");
}
private void timeTestJava(Date startTime, String text, String from, String to) {
System.out.println("java: " + startTime);
Date output = convertJava(startTime, TimeZone.getTimeZone(from), TimeZone.getTimeZone(to));
System.out.println(text + ": " + output + "\n");
}
// Initial Joda implementation, works before and after DST change, but not during the period from 2am-7am UTC on the day of the change
public Date convertJodaOld(Date dt, TimeZone from, TimeZone to) {
DateTimeZone tzFrom = DateTimeZone.forTimeZone(from);
DateTimeZone tzTo = DateTimeZone.forTimeZone(to);
Date utc = new Date(tzFrom.convertLocalToUTC(dt.getTime(), false));
Date convertedTime = new Date(tzTo.convertUTCToLocal(utc.getTime()));
return convertedTime;
}
// New attempt at joda implementation, doesn't work after DST (winter)
public Date convertJodaNew(Date dt, TimeZone from, TimeZone to) {
Instant utcInstant = new Instant(dt.getTime());
DateTime datetime = new DateTime(utcInstant);
datetime.withZone(DateTimeZone.forID(to.getID()));
return datetime.toDate();
}
// Java implementation. Works.
public Date convertJava(Date dt, TimeZone from, TimeZone to) {
long fromOffset = from.getOffset(dt.getTime());
long toOffset = to.getOffset(dt.getTime());
long convertedTime = dt.getTime() - (fromOffset - toOffset);
return new Date(convertedTime);
}
}