88import java .sql .*;
99import java .text .DecimalFormat ;
1010import java .text .DecimalFormatSymbols ;
11+ import java .time .DateTimeException ;
1112import java .time .LocalDateTime ;
1213import java .time .ZonedDateTime ;
1314import java .time .format .DateTimeFormatter ;
@@ -144,24 +145,30 @@ public String decodeStringText(
144145 final Context context )
145146 throws SQLDataException {
146147 if (length .get () == 0 ) return buildZeroDate ();
148+ int initialPos = buf .pos ();
147149 int initialLength = length .get ();
148- LocalDateTime ldt = parseText (buf , length );
149- if (ldt == null ) {
150- if (initialLength > 0 ) return buildZeroDate ();
151- return null ;
152- }
153- LocalDateTime modifiedLdt =
154- localDateTimeToZoneDateTime (ldt , providedCal , context ).toLocalDateTime ();
155- String timestampWithoutMicro = dateTimeFormatter .format (modifiedLdt );
156- if (context .getConf ().oldModeNoPrecisionTimestamp ()) {
157- // for compatibility with 2.2.0 and before, micro precision use .0##### format
150+ try {
151+ LocalDateTime ldt = parseText (buf , length );
152+ if (ldt == null ) {
153+ if (initialLength > 0 ) return buildZeroDate ();
154+ return null ;
155+ }
156+ LocalDateTime modifiedLdt =
157+ localDateTimeToZoneDateTime (ldt , providedCal , context ).toLocalDateTime ();
158+ String timestampWithoutMicro = dateTimeFormatter .format (modifiedLdt );
159+ if (context .getConf ().oldModeNoPrecisionTimestamp ()) {
160+ // for compatibility with 2.2.0 and before, micro precision use .0##### format
161+ return timestampWithoutMicro
162+ + oldDecimalFormat .format (((double ) modifiedLdt .getNano ()) / 1000000000 );
163+ }
164+ if (this .decimals == 0 ) return timestampWithoutMicro ;
158165 return timestampWithoutMicro
159- + oldDecimalFormat .format (((double ) modifiedLdt .getNano ()) / 1000000000 );
166+ + "."
167+ + String .format (Locale .US , "%0" + this .decimals + "d" , modifiedLdt .getNano () / 1000 );
168+ } catch (DateTimeException e ) {
169+ buf .pos (initialPos );
170+ return buf .readString (length .get ());
160171 }
161- if (this .decimals == 0 ) return timestampWithoutMicro ;
162- return timestampWithoutMicro
163- + "."
164- + String .format (Locale .US , "%0" + this .decimals + "d" , modifiedLdt .getNano () / 1000 );
165172 }
166173
167174 private String buildZeroDate () {
@@ -181,24 +188,72 @@ public String decodeStringBinary(
181188 final Context context )
182189 throws SQLDataException {
183190 if (length .get () == 0 ) return buildZeroDate ();
191+ int initialPos = buf .pos ();
184192 int initialLength = length .get ();
185- LocalDateTime ldt = parseBinary (buf , length );
186- if (ldt == null ) {
187- if (initialLength > 0 ) return buildZeroDate ();
188- return null ;
189- }
190- LocalDateTime modifiedLdt =
191- localDateTimeToZoneDateTime (ldt , providedCal , context ).toLocalDateTime ();
192- String timestampWithoutMicro = dateTimeFormatter .format (modifiedLdt );
193- if (context .getConf ().oldModeNoPrecisionTimestamp ()) {
194- // for compatibility with 2.2.0 and before, micro precision use .0##### format
193+ try {
194+ LocalDateTime ldt = parseBinary (buf , length );
195+ if (ldt == null ) {
196+ if (initialLength > 0 ) return buildZeroDate ();
197+ return null ;
198+ }
199+ LocalDateTime modifiedLdt =
200+ localDateTimeToZoneDateTime (ldt , providedCal , context ).toLocalDateTime ();
201+ String timestampWithoutMicro = dateTimeFormatter .format (modifiedLdt );
202+ if (context .getConf ().oldModeNoPrecisionTimestamp ()) {
203+ // for compatibility with 2.2.0 and before, micro precision use .0##### format
204+ return timestampWithoutMicro
205+ + oldDecimalFormat .format (((double ) modifiedLdt .getNano ()) / 1000000000 );
206+ }
207+ if (this .decimals == 0 ) return timestampWithoutMicro ;
195208 return timestampWithoutMicro
196- + oldDecimalFormat .format (((double ) modifiedLdt .getNano ()) / 1000000000 );
209+ + "."
210+ + String .format (Locale .US , "%0" + this .decimals + "d" , modifiedLdt .getNano () / 1000 );
211+ } catch (DateTimeException e ) {
212+ buf .pos (initialPos );
213+ int year = buf .readUnsignedShort ();
214+ int month = buf .readByte ();
215+ int dayOfMonth = buf .readByte ();
216+ int hour = 0 ;
217+ int minutes = 0 ;
218+ int seconds = 0 ;
219+ long microseconds = 0 ;
220+
221+ if (length .get () > 4 ) {
222+ hour = buf .readByte ();
223+ minutes = buf .readByte ();
224+ seconds = buf .readByte ();
225+
226+ if (length .get () > 7 ) {
227+ microseconds = buf .readUnsignedInt ();
228+ }
229+ }
230+ StringBuilder sb = new StringBuilder ();
231+ fill (year , 4 , sb );
232+ sb .append ("-" );
233+ fill (month , 2 , sb );
234+ sb .append ("-" );
235+ fill (dayOfMonth , 2 , sb );
236+ sb .append (" " );
237+ fill (hour , 2 , sb );
238+ sb .append (":" );
239+ fill (minutes , 2 , sb );
240+ sb .append (":" );
241+ fill (seconds , 2 , sb );
242+
243+ if (getDecimals () == 0 ) return sb .toString ();
244+ sb .append ("." );
245+ fill ((int ) (microseconds / Math .pow (10 , 6 - getDecimals ())), getDecimals (), sb );
246+ return sb .toString ();
247+ }
248+ }
249+
250+ private void fill (int val , int size , StringBuilder sb ) {
251+ String valSt = String .valueOf (val );
252+ long zeroToAdd = size - valSt .length ();
253+ while (zeroToAdd -- > 0 ) {
254+ sb .append ("0" );
197255 }
198- if (this .decimals == 0 ) return timestampWithoutMicro ;
199- return timestampWithoutMicro
200- + "."
201- + String .format (Locale .US , "%0" + this .decimals + "d" , modifiedLdt .getNano () / 1000 );
256+ sb .append (valSt );
202257 }
203258
204259 @ Override
@@ -320,22 +375,90 @@ public Time decodeTimeBinary(
320375 public Timestamp decodeTimestampText (
321376 final ReadableByteBuf buf , final MutableInt length , Calendar calParam , final Context context )
322377 throws SQLDataException {
323- LocalDateTime ldt = parseText (buf , length );
324- if (ldt == null ) return null ;
325- Timestamp res = new Timestamp (localDateTimeToInstant (ldt , calParam , context ));
326- res .setNanos (ldt .getNano ());
327- return res ;
378+ int [] parts = LocalDateTimeCodec .parseTextTimestamp (buf , length );
379+ if (LocalDateTimeCodec .isZeroTimestamp (parts )) {
380+ length .set (NULL_LENGTH );
381+ return null ;
382+ }
383+
384+ try {
385+ LocalDateTime ldt = LocalDateTime .of (parts [0 ], parts [1 ], parts [2 ], parts [3 ], parts [4 ], parts [5 ])
386+ .plusNanos (parts [6 ]);
387+ Timestamp res = new Timestamp (localDateTimeToInstant (ldt , calParam , context ));
388+ res .setNanos (ldt .getNano ());
389+ return res ;
390+ } catch (DateTimeException e ) {
391+ Timestamp timestamp ;
392+ Calendar cal = calParam == null ? Calendar .getInstance () : calParam ;
393+ synchronized (cal ) {
394+ cal .setLenient (true );
395+ cal .clear ();
396+ cal .set (Calendar .YEAR , parts [0 ]);
397+ cal .set (Calendar .MONTH , parts [1 ] - 1 );
398+ cal .set (Calendar .DAY_OF_MONTH , parts [2 ]);
399+ cal .set (Calendar .HOUR_OF_DAY , parts [3 ]);
400+ cal .set (Calendar .MINUTE , parts [4 ]);
401+ cal .set (Calendar .SECOND , parts [5 ]);
402+ cal .set (Calendar .MILLISECOND , parts [6 ] / 1000000 );
403+ timestamp = new Timestamp (cal .getTime ().getTime ());
404+ }
405+ timestamp .setNanos (parts [6 ]);
406+ return timestamp ;
407+ }
408+
328409 }
329410
330411 @ Override
331412 public Timestamp decodeTimestampBinary (
332413 final ReadableByteBuf buf , final MutableInt length , Calendar calParam , final Context context )
333414 throws SQLDataException {
334- LocalDateTime ldt = parseBinary (buf , length );
335- if (ldt == null ) return null ;
336- Timestamp res = new Timestamp (localDateTimeToInstant (ldt , calParam , context ));
337- res .setNanos (ldt .getNano ());
338- return res ;
415+ if (length .get () == 0 ) {
416+ length .set (NULL_LENGTH );
417+ return null ;
418+ }
419+
420+ int year = buf .readUnsignedShort ();
421+ int month = buf .readByte ();
422+ int dayOfMonth = buf .readByte ();
423+ int hour = 0 ;
424+ int minutes = 0 ;
425+ int seconds = 0 ;
426+ long microseconds = 0 ;
427+
428+ if (length .get () > 4 ) {
429+ hour = buf .readByte ();
430+ minutes = buf .readByte ();
431+ seconds = buf .readByte ();
432+
433+ if (length .get () > 7 ) {
434+ microseconds = buf .readUnsignedInt ();
435+ }
436+ }
437+ try {
438+ LocalDateTime ldt = LocalDateTime .of (year , month , dayOfMonth , hour , minutes , seconds )
439+ .plusNanos (microseconds * 1000 );
440+ if (ldt == null ) return null ;
441+ Timestamp res = new Timestamp (localDateTimeToInstant (ldt , calParam , context ));
442+ res .setNanos (ldt .getNano ());
443+ return res ;
444+ } catch (DateTimeException e ) {
445+ Timestamp timestamp ;
446+ Calendar cal = calParam == null ? Calendar .getInstance () : calParam ;
447+ synchronized (cal ) {
448+ cal .setLenient (true );
449+ cal .clear ();
450+ cal .set (Calendar .YEAR , year );
451+ cal .set (Calendar .MONTH , month - 1 );
452+ cal .set (Calendar .DAY_OF_MONTH , dayOfMonth );
453+ cal .set (Calendar .HOUR_OF_DAY , hour );
454+ cal .set (Calendar .MINUTE , minutes );
455+ cal .set (Calendar .SECOND , seconds );
456+ cal .set (Calendar .MILLISECOND , (int ) (microseconds / 1000000 ));
457+ timestamp = new Timestamp (cal .getTime ().getTime ());
458+ }
459+ timestamp .setNanos ((int ) (microseconds * 1000 ));
460+ return timestamp ;
461+ }
339462 }
340463
341464 private LocalDateTime parseText (final ReadableByteBuf buf , final MutableInt length ) {
@@ -371,18 +494,6 @@ private LocalDateTime parseBinary(final ReadableByteBuf buf, final MutableInt le
371494 microseconds = buf .readUnsignedInt ();
372495 }
373496 }
374-
375- // xpand workaround https://jira.mariadb.org/browse/XPT-274
376- if (year == 0
377- && month == 0
378- && dayOfMonth == 0
379- && hour == 0
380- && minutes == 0
381- && seconds == 0
382- && microseconds == 0 ) {
383- length .set (NULL_LENGTH );
384- return null ;
385- }
386497 return LocalDateTime .of (year , month , dayOfMonth , hour , minutes , seconds )
387498 .plusNanos (microseconds * 1000 );
388499 }
0 commit comments