Я столкнулся с неожиданной проблемой перехода на летнее время в коде, который я считал чисто UTC. Я использую Java 1.6, iBatis SQL mapper (2.3.3) и Oracle XE (eval-версия Oracle 10.2) с тонким драйвером Oracle.
База данных содержит таблицу, которая представляет расписание телевизионного вещания. Каждый «актив» (программа) имеет время начала и время окончания. Вот соответствующий срез:
create table Asset
(
asset_id integer not null, -- The unique id of the Asset.
[...]
start_time timestamp, -- The start time.
end_time timestamp, -- The end time.
[...]
constraint asset_primary_key primary key (asset_id),
constraint asset_time check (end_time >= start_time)
);
Ограничение оракула asset_time
действует для программ, которые выходят за рамки централизованного регулирования летнего времени США в это предстоящее воскресное утро, 11 января 2009 года.
У меня есть этот объект передачи данных (Даты - java.util.Dates):
public class Asset
{
protected Long asset_id;
[...]
protected Date start_time;
protected Date end_time;
public Date getStart_time() { return start_time; }
public Date getEnd_time() { return end_time; }
public void setStart_time(Date start_time) { this.start_time = start_time; }
public void setEnd_time(Date end_time) { this.end_time = end_time; }
[...]
}
И на карте iBatis SQL у меня есть такой оператор, который вставляет DTO Asset в таблицу Oracle Asset:
<insert id="Asset.insert" parameterClass="com.acme.Asset">
insert into Asset
( asset_id, [...] start_time, end_time )
values
( #asset_id#, [...] #start_time#, #end_time# )
</insert>
На стороне Java я убедился, что я даю iBatis правильный ввод даты UTC через это утверждение перед вставкой, которое не брошено:
System.err.println("Inserting asset " + program_id);
System.err.println(" "+asset.getStart_time_str()+"--"+asset.getEnd_time_str());
if ( !asset.getEnd_time().after(asset.getStart_time())) {
System.err.println("Invalid datetime range in asset.");
throw new AssertionError("Invalid datetime range in asset.");
}
Непосредственно перед ошибкой ограничения Oracle печатается приведенный выше код:
Inserting asset EP011453960004
2009-11-01T06:30:00Z--2009-11-01T07:00:00Z
Я нахожусь в центральном часовом поясе США, GMT -5: 00, поэтому эта программа начинается в 1:30 и заканчивается в 2:00. Изменение летнего времени наступает в 2:00 утра и переводит часы обратно в 1:00 утра.
iBatis сообщает об ошибке ограничения Oracle (отредактировано):
2009-10-30 22:58:42,238 [...] Executing Statement:
insert into Asset ( asset_id, [...] start_time, end_time )
values ( ?, [...] ?, ? )
2009-10-30 22:58:42,238 [...] Parameters:
[EP011453960004, [...] 2009-11-01 01:30:00.0, 2009-11-01 01:00:00.0]
2009-10-30 22:58:42,238 [..] Types:
[java.lang.Long, [...] java.sql.Timestamp, java.sql.Timestamp]
2009-10-30 22:58:42,285 [...] - Failed with a SQLException:
--- The error occurred in com/acme/data/dao/Asset-Write.xml.
--- The error occurred while applying a parameter map.
--- Check the Asset.insert-InlineParameterMap.
--- Check the statement (update failed).
--- Cause: java.sql.SQLException: ORA-02290: check constraint (ACME.ASSET_TIME)
violated
Вы заметите, что на стороне Oracle он видит start_time / end_time с настройкой перехода на летнее время, поэтому что-то в логике отображения iBatis или в драйвере Oracle не выполняет то, что я ожидал. Драйвер ojdbc14.jar, тонкий драйвер:
JDBCReadWrite.Driver = oracle.jdbc.OracleDriver
JDBCReadWrite.ConnectionURL = jdbc:oracle:thin:@localhost:1521:XE
Какой правильный способ гарантировать, что этот код является чисто UTC?
Заранее спасибо!