diff --git a/plugins/org.jkiss.dbeaver.data.transfer/OSGI-INF/l10n/bundle.properties b/plugins/org.jkiss.dbeaver.data.transfer/OSGI-INF/l10n/bundle.properties
index 7e4fd9e225eefa489c8af7d47ce3b0ffcd1133a2..0a2a90d9c485d9218a529f279594763b869c85cc 100644
--- a/plugins/org.jkiss.dbeaver.data.transfer/OSGI-INF/l10n/bundle.properties
+++ b/plugins/org.jkiss.dbeaver.data.transfer/OSGI-INF/l10n/bundle.properties
@@ -146,7 +146,11 @@ dataTransfer.producer.stream.processor.csv.property.emptyStringNull.description
dataTransfer.producer.stream.processor.csv.property.nullString.name = NULL value mark
dataTransfer.producer.stream.processor.csv.property.nullString.description = String literal used as NULL value mark. \nSuch strings will be converted into NULL during data import
dataTransfer.producer.stream.processor.csv.property.timestampFormat.name = Date/time format
-dataTransfer.producer.stream.processor.csv.property.timestampFormat.description = Date/time format pattern. Use this to clarify the date format present\n in the data source, not to change output data
+dataTransfer.producer.stream.processor.csv.property.timestampFormat.description = Date/time format pattern. Use this to clarify the date format in CSV file, not to change output data.\nSearch for 'java DateTimeFormatter' for format details.
+dataTransfer.producer.stream.processor.csv.property.timestampZone.name = Timezone ID
+dataTransfer.producer.stream.processor.csv.property.timestampZone.description = Timezone ID. By default local machine timezone is used.\n3 ways to specify zone:\n\t-Local zone offset (+3, -04:30)\n\t-Specific zone offset (GMT+2, UTC+01:00)\n\t-Region based (UTC, ECT, PST, etc)
+
+
task.category.name.common = Common
task.category.description.common = Common database tasks
task.name.export = Data export
diff --git a/plugins/org.jkiss.dbeaver.data.transfer/plugin.xml b/plugins/org.jkiss.dbeaver.data.transfer/plugin.xml
index b92c935aa69e038216f54429ee2a2fc879fed75c..4d51a4d28fb457de30722d6ec705fe21be88d49d 100644
--- a/plugins/org.jkiss.dbeaver.data.transfer/plugin.xml
+++ b/plugins/org.jkiss.dbeaver.data.transfer/plugin.xml
@@ -51,6 +51,7 @@
+
diff --git a/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/StreamTransferResultSet.java b/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/StreamTransferResultSet.java
index 3d5927c2b710788827ce9289b782b1d01367de27..accfad51a170d9e61705a661a1b396372853ff8c 100644
--- a/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/StreamTransferResultSet.java
+++ b/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/StreamTransferResultSet.java
@@ -28,6 +28,7 @@ import org.jkiss.utils.CommonUtils;
import java.sql.Timestamp;
import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
@@ -50,6 +51,7 @@ public class StreamTransferResultSet implements DBCResultSet {
private Object[] streamRow;
private final List attributeMappings;
private DateTimeFormatter dateTimeFormat;
+ private ZoneId dateTimeZoneId;
public StreamTransferResultSet(DBCSession session, DBCStatement statement, StreamEntityMapping entityMapping) {
this.session = session;
@@ -97,6 +99,14 @@ public class StreamTransferResultSet implements DBCResultSet {
value = java.util.Date.from(zdt.toInstant());
} catch (Exception e) {
LocalDateTime localDT = LocalDateTime.from(ta);
+ if (dateTimeZoneId != null) {
+ // Shift LocalDateTime to specified zone
+ // https://stackoverflow.com/questions/42280454/changing-localdatetime-based-on-time-difference-in-current-time-zone-vs-eastern
+ localDT = localDT
+ .atZone(ZoneId.systemDefault())
+ .withZoneSameInstant(dateTimeZoneId)
+ .toLocalDateTime();
+ }
// We use java.sql.Timestamp.valueOf because classic date/time conversion turns "pre-historic" Gregorian
// dates into incorrect SQL timestamps (in Julian calendar). E.g. 0001-01-01->0001-01-03
value = Timestamp.valueOf(localDT);
@@ -160,7 +170,16 @@ public class StreamTransferResultSet implements DBCResultSet {
return dateTimeFormat;
}
- public void setDateTimeFormat(DateTimeFormatter dateTimeFormat) {
+ public void setDateTimeFormat(DateTimeFormatter dateTimeFormat, ZoneId dateTimeZoneId) {
this.dateTimeFormat = dateTimeFormat;
+ this.dateTimeZoneId = dateTimeZoneId;
+ if (this.dateTimeFormat != null && this.dateTimeZoneId != null) {
+ // Set zone to the format.
+ // FIXME: it looks like a good idea but in fact iti s not. We can't convert ZonedDateTime into
+ // FIXME: proper SQL timestamp for pre-historic (pre-Gregorian) dates.
+ // FIXME: so we will shift LocalDateTime in getAttributeValue instead
+ // FIXME: https://stackoverflow.com/questions/23975205/why-does-converting-java-dates-before-1582-to-localdate-with-instant-give-a-diff
+ //this.dateTimeFormat = this.dateTimeFormat.withZone(dateTimeZoneId);
+ }
}
}
diff --git a/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/importer/DataImporterCSV.java b/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/importer/DataImporterCSV.java
index caaf000dc556a99aa78c2730d47370fdfb747d32..eb3e0fa9dc0024604aee32435cd3fe8a1a44ef97 100644
--- a/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/importer/DataImporterCSV.java
+++ b/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/importer/DataImporterCSV.java
@@ -49,7 +49,6 @@ public class DataImporterCSV extends StreamImporterAbstract {
private static final String PROP_NULL_STRING = "nullString";
private static final String PROP_EMPTY_STRING_NULL = "emptyStringNull";
private static final String PROP_ESCAPE_CHAR = "escapeChar";
- private static final String PROP_TIMESTAMP_FORMAT = "timestampFormat";
enum HeaderPosition {
none,
@@ -135,7 +134,7 @@ public class DataImporterCSV extends StreamImporterAbstract {
consumer.fetchStart(producerSession, resultSet, -1, -1);
- applyTransformHints(resultSet, consumer, getTimeStampFormat(properties, PROP_TIMESTAMP_FORMAT));
+ applyTransformHints(resultSet, consumer, properties, PROP_TIMESTAMP_FORMAT, PROP_TIMESTAMP_ZONE);
try (Reader reader = openStreamReader(inputStream, properties)) {
try (CSVReader csvReader = openCSVReader(reader, properties)) {
diff --git a/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/importer/StreamImporterAbstract.java b/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/importer/StreamImporterAbstract.java
index b0c0e92af82e6b1e175c6e718bb8fc36cd9b0134..cb51e51b14b15cfc12635087112e3bec9383de7e 100644
--- a/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/importer/StreamImporterAbstract.java
+++ b/plugins/org.jkiss.dbeaver.data.transfer/src/org/jkiss/dbeaver/tools/transfer/stream/importer/StreamImporterAbstract.java
@@ -31,6 +31,7 @@ import org.jkiss.dbeaver.tools.transfer.stream.StreamDataImporterColumnInfo;
import org.jkiss.dbeaver.tools.transfer.stream.StreamTransferResultSet;
import org.jkiss.utils.CommonUtils;
+import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Map;
@@ -41,6 +42,9 @@ public abstract class StreamImporterAbstract implements IStreamDataImporter {
private static final Log log = Log.getLog(StreamImporterAbstract.class);
+ protected static final String PROP_TIMESTAMP_FORMAT = "timestampFormat";
+ protected static final String PROP_TIMESTAMP_ZONE = "timestampZone";
+
private IStreamDataImporterSite site;
public IStreamDataImporterSite getSite()
@@ -61,10 +65,10 @@ public abstract class StreamImporterAbstract implements IStreamDataImporter {
}
@Nullable
- protected DateTimeFormatter getTimeStampFormat(Map properties, String propName) {
+ protected DateTimeFormatter getTimeStampFormat(Map properties, String formatPropName) {
DateTimeFormatter tsFormat = null;
- String tsFormatPattern = CommonUtils.toString(properties.get(propName));
+ String tsFormatPattern = CommonUtils.toString(properties.get(formatPropName));
if (!CommonUtils.isEmpty(tsFormatPattern)) {
try {
tsFormat = DateTimeFormatter.ofPattern(tsFormatPattern);
@@ -75,9 +79,17 @@ public abstract class StreamImporterAbstract implements IStreamDataImporter {
return tsFormat;
}
- protected void applyTransformHints(StreamTransferResultSet resultSet, IDataTransferConsumer consumer, DateTimeFormatter tsFormat) throws DBException {
+ protected void applyTransformHints(StreamTransferResultSet resultSet, IDataTransferConsumer consumer, Map properties, String formatPropName, String zoneIdPropName) throws DBException {
+ DateTimeFormatter tsFormat = formatPropName == null ? null : getTimeStampFormat(properties, formatPropName);
+ ZoneId tsZoneId = null;
+ if (zoneIdPropName != null) {
+ String zoneId = CommonUtils.toString(properties.get(zoneIdPropName));
+ if (!CommonUtils.isEmpty(zoneId)) {
+ tsZoneId = ZoneId.of(zoneId);
+ }
+ }
if (tsFormat != null) {
- resultSet.setDateTimeFormat(tsFormat);
+ resultSet.setDateTimeFormat(tsFormat, tsZoneId);
}
// Try to find source/target attributes