1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.dbunit.dataset.datatype;
23
24 import java.math.BigInteger;
25 import java.sql.PreparedStatement;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
28 import java.sql.Timestamp;
29 import java.sql.Types;
30 import java.time.LocalDateTime;
31 import java.time.format.DateTimeParseException;
32 import java.util.Calendar;
33 import java.util.TimeZone;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36
37 import org.dbunit.dataset.ITable;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44
45
46
47 public class TimestampDataType extends AbstractDataType
48 {
49 private static final BigInteger ONE_BILLION = new BigInteger("1000000000");
50 private static final Pattern TIMEZONE_REGEX =
51 Pattern.compile("(.*)(?:\\W([+-][0-2][0-9][0-5][0-9]))");
52
53
54
55
56 private static final Logger logger =
57 LoggerFactory.getLogger(TimestampDataType.class);
58
59 TimestampDataType()
60 {
61 super("TIMESTAMP", Types.TIMESTAMP, Timestamp.class, false);
62 }
63
64
65
66
67 @Override
68 public Object typeCast(final Object value) throws TypeCastException
69 {
70 logger.debug("typeCast(value={}) - start", value);
71
72 if (value == null || value == ITable.NO_VALUE)
73 {
74 return null;
75 }
76
77 if (value instanceof java.sql.Timestamp)
78 {
79 return value;
80 }
81
82 if (value instanceof java.util.Date)
83 {
84 final java.util.Date date = (java.util.Date) value;
85 return new java.sql.Timestamp(date.getTime());
86 }
87
88 if (value instanceof Long)
89 {
90 final Long date = (Long) value;
91 return new java.sql.Timestamp(date);
92 }
93
94 if (value instanceof String)
95 {
96 String stringValue = value.toString();
97
98 if (isExtendedSyntax(stringValue))
99 {
100
101 try
102 {
103 final LocalDateTime datetime =
104 RELATIVE_DATE_TIME_PARSER.parse(stringValue);
105 return java.sql.Timestamp.valueOf(datetime);
106 } catch (IllegalArgumentException | DateTimeParseException e)
107 {
108 throw new TypeCastException(value, this, e);
109 }
110 }
111
112 String zoneValue = null;
113
114 final Matcher tzMatcher = TIMEZONE_REGEX.matcher(stringValue);
115 if (tzMatcher.matches() && tzMatcher.group(2) != null)
116 {
117 stringValue = tzMatcher.group(1);
118 zoneValue = tzMatcher.group(2);
119 }
120
121 Timestamp ts = null;
122 if (stringValue.length() == 10)
123 {
124 try
125 {
126 final long time =
127 java.sql.Date.valueOf(stringValue).getTime();
128 ts = new java.sql.Timestamp(time);
129 } catch (final IllegalArgumentException e)
130 {
131
132 }
133 }
134 if (ts == null)
135 {
136 try
137 {
138 ts = java.sql.Timestamp.valueOf(stringValue);
139 } catch (final IllegalArgumentException e)
140 {
141 throw new TypeCastException(value, this, e);
142 }
143 }
144
145
146 if (zoneValue != null)
147 {
148 final long tsTime = ts.getTime();
149
150 final TimeZone localTZ = java.util.TimeZone.getDefault();
151 final int offset = localTZ.getOffset(tsTime);
152 final BigInteger localTZOffset = BigInteger.valueOf(offset);
153 BigInteger time = BigInteger.valueOf(tsTime / 1000 * 1000)
154 .add(localTZOffset).multiply(ONE_BILLION)
155 .add(BigInteger.valueOf(ts.getNanos()));
156 final int hours = Integer.parseInt(zoneValue.substring(1, 3));
157 final int minutes = Integer.parseInt(zoneValue.substring(3, 5));
158 final BigInteger offsetAsSeconds =
159 BigInteger.valueOf((hours * 3600) + (minutes * 60));
160 final BigInteger offsetAsNanos =
161 offsetAsSeconds.multiply(BigInteger.valueOf(1000))
162 .multiply(ONE_BILLION);
163 if (zoneValue.charAt(0) == '+')
164 {
165 time = time.subtract(offsetAsNanos);
166 } else
167 {
168 time = time.add(offsetAsNanos);
169 }
170 final BigInteger[] components =
171 time.divideAndRemainder(ONE_BILLION);
172 ts = new Timestamp(components[0].longValue());
173 ts.setNanos(components[1].intValue());
174 }
175
176 return ts;
177 }
178
179 throw new TypeCastException(value, this);
180 }
181
182 @Override
183 public boolean isDateTime()
184 {
185 logger.debug("isDateTime() - start");
186
187 return true;
188 }
189
190 @Override
191 public Object getSqlValue(final int column, final ResultSet resultSet)
192 throws SQLException, TypeCastException
193 {
194 logger.debug("getSqlValue(column={}, resultSet={}) - start", column,
195 resultSet);
196 final Timestamp rawValue = resultSet.getTimestamp(column);
197 final Timestamp value = resultSet.wasNull() ? null : rawValue;
198 logger.debug("getSqlValue: column={}, value={}", column, value);
199 return value;
200 }
201
202 @Override
203 public void setSqlValue(final Object value, final int column,
204 final PreparedStatement statement)
205 throws SQLException, TypeCastException
206 {
207 logger.debug("setSqlValue(value={}, column={}, statement={}) - start",
208 value, column, statement);
209 final Timestamp ts = (Timestamp) typeCast(value);
210 if (value instanceof String)
211 {
212 final String stringValue = (String) value;
213 setSqlValueFromString(stringValue, column, statement, ts);
214 } else
215 {
216 statement.setTimestamp(column, ts);
217 }
218 }
219
220 private void setSqlValueFromString(final String value, final int column,
221 final PreparedStatement statement, final Timestamp ts)
222 throws SQLException
223 {
224 final Matcher timezoneMatcher = TIMEZONE_REGEX.matcher(value);
225 if (timezoneMatcher.matches() && timezoneMatcher.group(2) != null)
226 {
227 final Calendar cal = makeCalendar(timezoneMatcher);
228 statement.setTimestamp(column, ts, cal);
229 } else
230 {
231 statement.setTimestamp(column, ts);
232 }
233 }
234
235 private Calendar makeCalendar(final Matcher timezoneMatcher)
236 {
237 final String zoneValue = timezoneMatcher.group(2);
238 final String sign = zoneValue.substring(0, 1);
239 final int hours = Integer.parseInt(zoneValue.substring(1, 3));
240 final int minutes = Integer.parseInt(zoneValue.substring(3, 5));
241 final String timezoneId =
242 String.format("GMT%s%02d:%02d", sign, hours, minutes);
243 final TimeZone timeZone = TimeZone.getTimeZone(timezoneId);
244 final Calendar cal = Calendar.getInstance(timeZone);
245 return cal;
246 }
247 }