Commit 33910d0a authored by R.W.Majeed's avatar R.W.Majeed
Browse files

DataDialect: timezone for database timestamp with conversions

parent a9d96ac4
package de.sekmi.histream; package de.sekmi.histream;
import java.io.IOException;
import java.time.Instant; import java.time.Instant;
/** /**
...@@ -14,6 +15,7 @@ public interface ObservationExtractor { ...@@ -14,6 +15,7 @@ public interface ObservationExtractor {
/** /**
* Extract observations with a start time stamp between the specified limits. * Extract observations with a start time stamp between the specified limits.
* Only observations with the specified notations are extracted. * Only observations with the specified notations are extracted.
* TODO evaluate change from ObservationException to IOException
* *
* @param start_min minimum time for observation start (inclusive) * @param start_min minimum time for observation start (inclusive)
* @param start_max maximum time for observation start (inclusive) * @param start_max maximum time for observation start (inclusive)
...@@ -21,5 +23,5 @@ public interface ObservationExtractor { ...@@ -21,5 +23,5 @@ public interface ObservationExtractor {
* @return supplier for the extracted observations. Must be closed after use. * @return supplier for the extracted observations. Must be closed after use.
* @throws ObservationException error (e.g. database failure) * @throws ObservationException error (e.g. database failure)
*/ */
ObservationSupplier extract(Instant start_min, Instant start_max, Iterable<String> notations) throws ObservationException; ObservationSupplier extract(Instant start_min, Instant start_max, Iterable<String> notations) throws IOException;
} }
package de.sekmi.histream.i2b2; package de.sekmi.histream.i2b2;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import de.sekmi.histream.AbnormalFlag; import de.sekmi.histream.AbnormalFlag;
import de.sekmi.histream.DateTimeAccuracy;
import de.sekmi.histream.Value; import de.sekmi.histream.Value;
/** /**
* Configuration of exact meaning of values in * Configuration of exact meaning of values in
* {@code observation_fact} table. E.g. how to * {@code observation_fact} table. E.g. how to
* store/interpret null values. * store/interpret null values.
* <p>
* The function calls beginning with {@code encode} produce
* values which are stored in the database. The {@code decode} functions
* are used to decode the database values and produce a usable value.
* *
* @author R.W.Majeed * @author R.W.Majeed
* *
...@@ -18,6 +28,9 @@ public class DataDialect { ...@@ -18,6 +28,9 @@ public class DataDialect {
private String nullModifierCd; private String nullModifierCd;
private String nullValueFlagCd; private String nullValueFlagCd;
private String nullValueTypeCd; private String nullValueTypeCd;
/** Timezone for timestamp / date time columns */
private ZoneId zoneId;
// TODO nullSexCd, nullInOutCd
public DataDialect(){ public DataDialect(){
this.nullUnitCd = "@"; // technically, null is allowed, but the demodata uses both '@' and '' this.nullUnitCd = "@"; // technically, null is allowed, but the demodata uses both '@' and ''
...@@ -28,10 +41,14 @@ public class DataDialect { ...@@ -28,10 +41,14 @@ public class DataDialect {
this.nullValueTypeCd = "@"; // TODO check database this.nullValueTypeCd = "@"; // TODO check database
// null not allowed, use default // null not allowed, use default
this.nullProviderId = "@"; this.nullProviderId = "@";
this.zoneId = ZoneId.systemDefault();
} }
void setDefaultProviderId(String providerId){ void setDefaultProviderId(String providerId){
this.nullProviderId = providerId; this.nullProviderId = providerId;
} }
public void setTimeZone(ZoneId zone){
this.zoneId = zone;
}
public String getDefaultProviderId(){ public String getDefaultProviderId(){
return nullProviderId; return nullProviderId;
} }
...@@ -43,7 +60,9 @@ public class DataDialect { ...@@ -43,7 +60,9 @@ public class DataDialect {
public String getNullLocationCd(){ public String getNullLocationCd(){
return nullLocationCd; return nullLocationCd;
} }
public ZoneId getTimeZone(){
return zoneId;
}
public String getNullModifierCd(){ public String getNullModifierCd(){
return nullModifierCd; return nullModifierCd;
} }
...@@ -53,6 +72,34 @@ public class DataDialect { ...@@ -53,6 +72,34 @@ public class DataDialect {
public String getNullValueTypeCd(){ public String getNullValueTypeCd(){
return nullValueTypeCd; return nullValueTypeCd;
} }
public Timestamp encodeInstant(Instant instant){
if( instant == null ){
return null;
}else{
return Timestamp.from(instant.atZone(zoneId).toLocalDateTime().atOffset(ZoneOffset.UTC).toInstant());
}
}
public Timestamp encodeInstantPartial(DateTimeAccuracy instant){
if( instant == null ){
return null;
}else{
return encodeInstant(instant.toInstantMin());
}
}
public Instant decodeInstant(Timestamp timestamp){
if( timestamp == null ){
return null;
}else{
return timestamp.toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime().atZone(zoneId).toInstant();
}
}
public DateTimeAccuracy decodeInstantPartial(Timestamp timestamp){
if( timestamp == null ){
return null;
}else{
return new DateTimeAccuracy(decodeInstant(timestamp));
}
}
private boolean isNullComparison(String value, String nullValue){ private boolean isNullComparison(String value, String nullValue){
if( value == null ){ if( value == null ){
return true; return true;
......
package de.sekmi.histream.i2b2; package de.sekmi.histream.i2b2;
import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
...@@ -14,10 +15,8 @@ import java.util.logging.Logger; ...@@ -14,10 +15,8 @@ import java.util.logging.Logger;
import javax.sql.DataSource; import javax.sql.DataSource;
import de.sekmi.histream.ObservationException;
import de.sekmi.histream.ObservationExtractor; import de.sekmi.histream.ObservationExtractor;
import de.sekmi.histream.ObservationFactory; import de.sekmi.histream.ObservationFactory;
import de.sekmi.histream.ObservationSupplier;
import de.sekmi.histream.ext.Patient; import de.sekmi.histream.ext.Patient;
import de.sekmi.histream.ext.Visit; import de.sekmi.histream.ext.Visit;
...@@ -64,6 +63,7 @@ public class I2b2ExtractorFactory implements AutoCloseable, ObservationExtractor ...@@ -64,6 +63,7 @@ public class I2b2ExtractorFactory implements AutoCloseable, ObservationExtractor
ds = crc_ds; ds = crc_ds;
dialect = new DataDialect(); dialect = new DataDialect();
} }
public ObservationFactory getObservationFactory(){ public ObservationFactory getObservationFactory(){
return observationFactory; return observationFactory;
} }
...@@ -140,7 +140,7 @@ public class I2b2ExtractorFactory implements AutoCloseable, ObservationExtractor ...@@ -140,7 +140,7 @@ public class I2b2ExtractorFactory implements AutoCloseable, ObservationExtractor
* @throws SQLException error * @throws SQLException error
*/ */
//@SuppressWarnings("resource") //@SuppressWarnings("resource")
public I2b2Extractor extract(Timestamp start_min, Timestamp start_max, Iterable<String> notations) throws SQLException{ I2b2Extractor extract(Timestamp start_min, Timestamp start_max, Iterable<String> notations) throws SQLException{
// TODO move connection and prepared statement to I2b2Extractor // TODO move connection and prepared statement to I2b2Extractor
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
...@@ -204,11 +204,11 @@ public class I2b2ExtractorFactory implements AutoCloseable, ObservationExtractor ...@@ -204,11 +204,11 @@ public class I2b2ExtractorFactory implements AutoCloseable, ObservationExtractor
} }
@Override @Override
public ObservationSupplier extract(Instant start_min, Instant start_max, Iterable<String> notations) throws ObservationException{ public I2b2Extractor extract(Instant start_min, Instant start_max, Iterable<String> notations) throws IOException{
try { try {
return extract(Timestamp.from(start_min), Timestamp.from(start_max), notations); return extract(dialect.encodeInstant(start_min),dialect.encodeInstant(start_max), notations);
} catch (SQLException e) { } catch (SQLException e) {
throw new ObservationException(e); throw new IOException(e);
} }
} }
} }
...@@ -26,7 +26,6 @@ import java.io.IOException; ...@@ -26,7 +26,6 @@ import java.io.IOException;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Objects; import java.util.Objects;
...@@ -55,6 +54,10 @@ import de.sekmi.histream.impl.AbstractObservationHandler; ...@@ -55,6 +54,10 @@ import de.sekmi.histream.impl.AbstractObservationHandler;
* valtype_cd: N numeric, B stored in observation_blob, T text, '@' no value, 'NLP' NLP result xml objects. * valtype_cd: N numeric, B stored in observation_blob, T text, '@' no value, 'NLP' NLP result xml objects.
* Undocumented but used in demodata: D: datetime "YYYY-MM-DD HH:mm" stored in tval_char, "YYYYMMDD.HHmm0" stored in nval_num. * Undocumented but used in demodata: D: datetime "YYYY-MM-DD HH:mm" stored in tval_char, "YYYYMMDD.HHmm0" stored in nval_num.
* <p> * <p>
* Timestamp and datetime values are stored without timezone information. The timezone
* which should be used when reading/writing to database can be specified via
* the {@link DataDialect} param in {@link #open(Connection, DataDialect)}.
* <p>
* The most difficult part is handling the instance_num field. * The most difficult part is handling the instance_num field.
* By default, i2b2 uses a four byte signed integer for instance_num. Incrementing * By default, i2b2 uses a four byte signed integer for instance_num. Incrementing
* instance_num for every record would lead eventually to a number overflow. * instance_num for every record would lead eventually to a number overflow.
...@@ -64,7 +67,7 @@ import de.sekmi.histream.impl.AbstractObservationHandler; ...@@ -64,7 +67,7 @@ import de.sekmi.histream.impl.AbstractObservationHandler;
* in any order. Therefore, we keep track of the maximum instance_num per encounter * in any order. Therefore, we keep track of the maximum instance_num per encounter
* in the visit store (which caches visits anyways) and increase the instance_num only * in the visit store (which caches visits anyways) and increase the instance_num only
* for observations with modifiers. * for observations with modifiers.
* *
* @author R.W.Majeed * @author R.W.Majeed
* *
*/ */
...@@ -88,9 +91,7 @@ public class I2b2Inserter extends AbstractObservationHandler implements Observat ...@@ -88,9 +91,7 @@ public class I2b2Inserter extends AbstractObservationHandler implements Observat
// initialize(config); // initialize(config);
// } // }
public I2b2Inserter(){ public I2b2Inserter(){
} }
private interface Preprocessor{ private interface Preprocessor{
void preprocess(Observation fact)throws SQLException; void preprocess(Observation fact)throws SQLException;
} }
...@@ -273,7 +274,7 @@ public class I2b2Inserter extends AbstractObservationHandler implements Observat ...@@ -273,7 +274,7 @@ public class I2b2Inserter extends AbstractObservationHandler implements Observat
insertFact.setString(4, dialect.encodeProviderId(o.getProviderId())); insertFact.setString(4, dialect.encodeProviderId(o.getProviderId()));
// start_date // start_date
Objects.requireNonNull(o.getStartTime()); Objects.requireNonNull(o.getStartTime());
insertFact.setTimestamp(5, Timestamp.from(o.getStartTime().toInstantMin())); insertFact.setTimestamp(5, dialect.encodeInstant(o.getStartTime().toInstantMin()));
insertFact.setString(6, (m==null)?dialect.getNullModifierCd():m.getConceptId()); insertFact.setString(6, (m==null)?dialect.getNullModifierCd():m.getConceptId());
insertFact.setInt(7, instanceNum); insertFact.setInt(7, instanceNum);
...@@ -324,12 +325,12 @@ public class I2b2Inserter extends AbstractObservationHandler implements Observat ...@@ -324,12 +325,12 @@ public class I2b2Inserter extends AbstractObservationHandler implements Observat
if( o.getEndTime() == null ){ if( o.getEndTime() == null ){
insertFact.setTimestamp(13, null); insertFact.setTimestamp(13, null);
}else{ }else{
insertFact.setTimestamp(13, Timestamp.from(o.getEndTime().toInstantMin())); insertFact.setTimestamp(13, dialect.encodeInstant(o.getEndTime().toInstantMin()));
} }
// location_cd // location_cd
insertFact.setString(14, dialect.encodeLocationCd(o.getLocationId())); insertFact.setString(14, dialect.encodeLocationCd(o.getLocationId()));
// download_date // download_date
insertFact.setTimestamp(15, Timestamp.from(o.getSource().getSourceTimestamp())); insertFact.setTimestamp(15, dialect.encodeInstant(o.getSource().getSourceTimestamp()));
insertFact.setString(16, o.getSource().getSourceId()); insertFact.setString(16, o.getSource().getSourceId());
insertFact.executeUpdate(); insertFact.executeUpdate();
......
...@@ -22,9 +22,6 @@ package de.sekmi.histream.i2b2; ...@@ -22,9 +22,6 @@ package de.sekmi.histream.i2b2;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Timestamp;
import de.sekmi.histream.DateTimeAccuracy;
import de.sekmi.histream.Extension; import de.sekmi.histream.Extension;
/** /**
* Extension with database connectivity. * Extension with database connectivity.
...@@ -90,13 +87,7 @@ public abstract class PostgresExtension<T> implements Extension<T> { ...@@ -90,13 +87,7 @@ public abstract class PostgresExtension<T> implements Extension<T> {
// }else{ // }else{
// return defaultFetchSize; // return defaultFetchSize;
// } // }
// } // }
public static Timestamp inaccurateSqlTimestamp(DateTimeAccuracy dateTime){
if( dateTime == null )return null;
else return Timestamp.from(dateTime.toInstantMin());
}
/** /**
* Write updates to disk. * Write updates to disk.
......
...@@ -29,7 +29,6 @@ import java.sql.PreparedStatement; ...@@ -29,7 +29,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Enumeration; import java.util.Enumeration;
...@@ -104,6 +103,8 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme ...@@ -104,6 +103,8 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
private PreparedStatement selectAllIde; private PreparedStatement selectAllIde;
private PreparedStatement deletePatientSource; private PreparedStatement deletePatientSource;
private PreparedStatement deleteMapSource; private PreparedStatement deleteMapSource;
private DataDialect dialect;
// /** // /**
// * Construct new postgres patient store. In addition to properties // * Construct new postgres patient store. In addition to properties
...@@ -149,12 +150,12 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme ...@@ -149,12 +150,12 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
this.idSourceDefault = "HIVE"; this.idSourceDefault = "HIVE";
this.idSourceSeparator = ':'; this.idSourceSeparator = ':';
this.fetchSize = 1000; this.fetchSize = 1000;
// TODO add methods to change the configuration
} }
public void open(Connection connection, String projectId) throws SQLException{ public void open(Connection connection, String projectId, DataDialect dialect) throws SQLException{
this.db = connection; this.db = connection;
this.projectId = projectId; this.projectId = projectId;
this.dialect = dialect;
// require project id // require project id
Objects.requireNonNull(this.projectId, "non-null projectId required"); Objects.requireNonNull(this.projectId, "non-null projectId required");
// this.autoInsertSourceId = "HS.auto"; // this.autoInsertSourceId = "HS.auto";
...@@ -364,11 +365,11 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme ...@@ -364,11 +365,11 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
private void updateStorage(I2b2Patient patient) throws SQLException { private void updateStorage(I2b2Patient patient) throws SQLException {
synchronized( update ){ synchronized( update ){
update.setString(1, patient.getVitalStatusCd()); update.setString(1, patient.getVitalStatusCd());
update.setTimestamp(2, inaccurateSqlTimestamp(patient.getBirthDate())); update.setTimestamp(2, dialect.encodeInstantPartial(patient.getBirthDate()));
update.setTimestamp(3, inaccurateSqlTimestamp(patient.getDeathDate())); update.setTimestamp(3, dialect.encodeInstantPartial(patient.getDeathDate()));
update.setString(4, getSexCd(patient)); update.setString(4, getSexCd(patient));
if( patient.getSourceTimestamp() != null ){ if( patient.getSourceTimestamp() != null ){
update.setTimestamp(5, Timestamp.from(patient.getSourceTimestamp())); update.setTimestamp(5, dialect.encodeInstant(patient.getSourceTimestamp()));
}else{ }else{
update.setTimestamp(5, null); update.setTimestamp(5, null);
} }
...@@ -423,19 +424,10 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme ...@@ -423,19 +424,10 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
// make sure that non-null vital code contains at least one character // make sure that non-null vital code contains at least one character
if( vital_cd == null || vital_cd.length() == 0 )vital_cd = null; if( vital_cd == null || vital_cd.length() == 0 )vital_cd = null;
DateTimeAccuracy birthDate = null;
DateTimeAccuracy deathDate = null;
// birth date // birth date
Timestamp ts = rs.getTimestamp(3); DateTimeAccuracy birthDate = dialect.decodeInstantPartial(rs.getTimestamp(3));
if( ts != null ){ DateTimeAccuracy deathDate = dialect.decodeInstantPartial(rs.getTimestamp(4));
birthDate = new DateTimeAccuracy(ts.toInstant()); ;
}
// death date
ts = rs.getTimestamp(4);
if( ts != null ){
deathDate = new DateTimeAccuracy(ts.toInstant());
}
// load sex // load sex
String sex_cd = rs.getString(5); String sex_cd = rs.getString(5);
Sex sex = null; Sex sex = null;
...@@ -456,9 +448,9 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme ...@@ -456,9 +448,9 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
} }
I2b2Patient patient = new I2b2Patient(id, sex, birthDate, deathDate); I2b2Patient patient = new I2b2Patient(id, sex, birthDate, deathDate);
if( rs.getTimestamp(6) != null ) if( rs.getTimestamp(6) != null ){
patient.setSourceTimestamp(rs.getTimestamp(6).toInstant()); patient.setSourceTimestamp(dialect.decodeInstant(rs.getTimestamp(6)));
}
patient.setSourceId(rs.getString(7)); patient.setSourceId(rs.getString(7));
patient.setVitalStatusCd(vital_cd); patient.setVitalStatusCd(vital_cd);
...@@ -504,7 +496,7 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme ...@@ -504,7 +496,7 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
insertIde.setString(2, ids[0]); insertIde.setString(2, ids[0]);
insertIde.setInt(3, patient_num); insertIde.setInt(3, patient_num);
insertIde.setString(4, status); insertIde.setString(4, status);
insertIde.setTimestamp(5, Timestamp.from(source.getSourceTimestamp())); insertIde.setTimestamp(5, dialect.encodeInstant(source.getSourceTimestamp()));
insertIde.setString(6, source.getSourceId()); insertIde.setString(6, source.getSourceId());
insertIde.executeUpdate(); insertIde.executeUpdate();
} }
......
...@@ -29,7 +29,6 @@ import java.sql.PreparedStatement; ...@@ -29,7 +29,6 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Enumeration; import java.util.Enumeration;
...@@ -88,7 +87,9 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements ...@@ -88,7 +87,9 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements
private PreparedStatement selectMappingsAll; private PreparedStatement selectMappingsAll;
private PreparedStatement deleteSource; private PreparedStatement deleteSource;
private PreparedStatement deleteMapSource; private PreparedStatement deleteMapSource;
private DataDialect dialect;
// /** // /**
// * Create a visit store using configuration settings. // * Create a visit store using configuration settings.
// * The project id must be specified with the key {@code project}. // * The project id must be specified with the key {@code project}.
...@@ -125,11 +126,12 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements ...@@ -125,11 +126,12 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements
this.fetchSize = 1000; this.fetchSize = 1000;
this.rejectPatientChange = false; this.rejectPatientChange = false;
} }
public void open(Connection connection, String projectId) throws SQLException{ public void open(Connection connection, String projectId, DataDialect dialect) throws SQLException{
visitCache = new Hashtable<>(); visitCache = new Hashtable<>();
idCache = new Hashtable<>(); idCache = new Hashtable<>();
this.projectId = projectId; this.projectId = projectId;
this.db = connection; this.db = connection;
this.dialect = dialect;
// require project id // require project id
Objects.requireNonNull(this.projectId, "non-null projectId required"); Objects.requireNonNull(this.projectId, "non-null projectId required");
db.setAutoCommit(true); db.setAutoCommit(true);
...@@ -309,11 +311,11 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements ...@@ -309,11 +311,11 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements
private void updateStorage(I2b2Visit visit) throws SQLException { private void updateStorage(I2b2Visit visit) throws SQLException {
synchronized( update ){ synchronized( update ){
update.setString(1, visit.getActiveStatusCd()); update.setString(1, visit.getActiveStatusCd());
update.setTimestamp(2, inaccurateSqlTimestamp(visit.getStartTime())); update.setTimestamp(2, dialect.encodeInstantPartial(visit.getStartTime()));
update.setTimestamp(3, inaccurateSqlTimestamp(visit.getEndTime())); update.setTimestamp(3, dialect.encodeInstantPartial(visit.getEndTime()));
update.setString(4, visit.getInOutCd()); update.setString(4, visit.getInOutCd());
update.setString(5, visit.getLocationId()); update.setString(5, dialect.encodeLocationCd(visit.getLocationId()));
update.setTimestamp(6, Timestamp.from(visit.getSourceTimestamp())); update.setTimestamp(6, dialect.encodeInstant(visit.getSourceTimestamp()));
update.setString(7, visit.getSourceId()); update.setString(7, visit.getSourceId());
// where encounter_num=visit.getNum() // where encounter_num=visit.getNum()
...@@ -338,7 +340,7 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements ...@@ -338,7 +340,7 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements
synchronized( insert ){ synchronized( insert ){
insert.setInt(1, visit.getNum() ); insert.setInt(1, visit.getNum() );
insert.setInt(2, visit.getPatientNum()); insert.setInt(2, visit.getPatientNum());
insert.setTimestamp(3, Timestamp.from(visit.getSourceTimestamp())); insert.setTimestamp(3, dialect.encodeInstant(visit.getSourceTimestamp()));
insert.setString(4, visit.getSourceId()); insert.setString(4, visit.getSourceId());
insert.executeUpdate(); insert.executeUpdate();
// other fields are not written, don't clear the dirty flag // other fields are not written, don't clear the dirty flag
...@@ -355,7 +357,7 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements ...@@ -355,7 +357,7 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements
insertMapping.setString(5, ids[0]); // patient_ide_source insertMapping.setString(5, ids[0]); // patient_ide_source
insertMapping.setTimestamp(6, Timestamp.from(visit.getSourceTimestamp())); insertMapping.setTimestamp(6, dialect.encodeInstant(visit.getSourceTimestamp()));
insertMapping.setString(7, visit.getSourceId()); insertMapping.setString(7, visit.getSourceId());