【发布时间】:2010-11-05 21:02:36
【问题描述】:
我们有一个时间很关键的应用程序。我们使用 joda 进行时间转换并以 UTC 时间存储所有数据。我们已经生产了一段时间,一切都很完美,但是......
现在我们注意到在时间更改前几个小时发生的事件已经转换得太早了!事实上,保存到数据库的 UTC 时间相差一个小时。
这是一个例子。我的活动发生在太平洋夏令时间 2010 年 11 月 6 日晚上 9 点,通常会保存为 2010 年 11 月 7 日凌晨 4 点。但是,由于夏令时在 7 日结束(大概是凌晨 2 点),所以这个时间会被转移并存储为 2010 年 11 月 7 日@凌晨 5 点。
我们需要在太平洋标准时间凌晨 2 点实际发生在 PST 区域中之前不记录 DST 更改。我认为 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;
}
编辑:下面的 cmets 代码示例
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);
}
}
【问题讨论】:
-
您能否提供一个简短但完整的程序来演示问题,而不是提供单一方法?
标签: java timezone utc jodatime