TimestampDataType.java

  1. /*
  2.  *
  3.  * The DbUnit Database Testing Framework
  4.  * Copyright (C)2002-2004, DbUnit.org
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2.1 of the License, or (at your option) any later version.
  10.  *
  11.  * This library is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this library; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  *
  20.  */

  21. package org.dbunit.dataset.datatype;

  22. import java.math.BigInteger;
  23. import java.sql.PreparedStatement;
  24. import java.sql.ResultSet;
  25. import java.sql.SQLException;
  26. import java.sql.Timestamp;
  27. import java.sql.Types;
  28. import java.time.LocalDateTime;
  29. import java.time.format.DateTimeParseException;
  30. import java.util.TimeZone;
  31. import java.util.regex.Matcher;
  32. import java.util.regex.Pattern;

  33. import org.dbunit.dataset.ITable;
  34. import org.slf4j.Logger;
  35. import org.slf4j.LoggerFactory;

  36. /**
  37.  * @author Manuel Laflamme
  38.  * @author Last changed by: $Author$
  39.  * @version $Revision$ $Date$
  40.  * @since 1.0 (Feb 19, 2002)
  41.  */
  42. public class TimestampDataType extends AbstractDataType
  43. {
  44.     private static final BigInteger ONE_BILLION = new BigInteger("1000000000");
  45.     private static final Pattern TIMEZONE_REGEX =
  46.             Pattern.compile("(.*)(?:\\W([+-][0-2][0-9][0-5][0-9]))");

  47.     /**
  48.      * Logger for this class
  49.      */
  50.     private static final Logger logger =
  51.             LoggerFactory.getLogger(TimestampDataType.class);

  52.     TimestampDataType()
  53.     {
  54.         super("TIMESTAMP", Types.TIMESTAMP, Timestamp.class, false);
  55.     }

  56.     ////////////////////////////////////////////////////////////////////////////
  57.     // DataType class

  58.     @Override
  59.     public Object typeCast(final Object value) throws TypeCastException
  60.     {
  61.         logger.debug("typeCast(value={}) - start", value);

  62.         if (value == null || value == ITable.NO_VALUE)
  63.         {
  64.             return null;
  65.         }

  66.         if (value instanceof java.sql.Timestamp)
  67.         {
  68.             return value;
  69.         }

  70.         if (value instanceof java.util.Date)
  71.         {
  72.             final java.util.Date date = (java.util.Date) value;
  73.             return new java.sql.Timestamp(date.getTime());
  74.         }

  75.         if (value instanceof Long)
  76.         {
  77.             final Long date = (Long) value;
  78.             return new java.sql.Timestamp(date);
  79.         }

  80.         if (value instanceof String)
  81.         {
  82.             String stringValue = value.toString();

  83.             if (isExtendedSyntax(stringValue))
  84.             {
  85.                 // Relative date.
  86.                 try
  87.                 {
  88.                     final LocalDateTime datetime =
  89.                             RELATIVE_DATE_TIME_PARSER.parse(stringValue);
  90.                     return java.sql.Timestamp.valueOf(datetime);
  91.                 } catch (IllegalArgumentException | DateTimeParseException e)
  92.                 {
  93.                     throw new TypeCastException(value, this, e);
  94.                 }
  95.             }

  96.             String zoneValue = null;

  97.             final Matcher tzMatcher = TIMEZONE_REGEX.matcher(stringValue);
  98.             if (tzMatcher.matches() && tzMatcher.group(2) != null)
  99.             {
  100.                 stringValue = tzMatcher.group(1);
  101.                 zoneValue = tzMatcher.group(2);
  102.             }

  103.             Timestamp ts = null;
  104.             if (stringValue.length() == 10)
  105.             {
  106.                 try
  107.                 {
  108.                     final long time =
  109.                             java.sql.Date.valueOf(stringValue).getTime();
  110.                     ts = new java.sql.Timestamp(time);
  111.                 } catch (final IllegalArgumentException e)
  112.                 {
  113.                     // Was not a java.sql.Date, let Timestamp handle this value
  114.                 }
  115.             }
  116.             if (ts == null)
  117.             {
  118.                 try
  119.                 {
  120.                     ts = java.sql.Timestamp.valueOf(stringValue);
  121.                 } catch (final IllegalArgumentException e)
  122.                 {
  123.                     throw new TypeCastException(value, this, e);
  124.                 }
  125.             }

  126.             // Apply zone if any
  127.             if (zoneValue != null)
  128.             {
  129.                 final long tsTime = ts.getTime();

  130.                 final TimeZone localTZ = java.util.TimeZone.getDefault();
  131.                 final int offset = localTZ.getOffset(tsTime);
  132.                 final BigInteger localTZOffset = BigInteger.valueOf(offset);
  133.                 BigInteger time = BigInteger.valueOf(tsTime / 1000 * 1000)
  134.                         .add(localTZOffset).multiply(ONE_BILLION)
  135.                         .add(BigInteger.valueOf(ts.getNanos()));
  136.                 final int hours = Integer.parseInt(zoneValue.substring(1, 3));
  137.                 final int minutes = Integer.parseInt(zoneValue.substring(3, 5));
  138.                 final BigInteger offsetAsSeconds =
  139.                         BigInteger.valueOf((hours * 3600) + (minutes * 60));
  140.                 final BigInteger offsetAsNanos =
  141.                         offsetAsSeconds.multiply(BigInteger.valueOf(1000))
  142.                                 .multiply(ONE_BILLION);
  143.                 if (zoneValue.charAt(0) == '+')
  144.                 {
  145.                     time = time.subtract(offsetAsNanos);
  146.                 } else
  147.                 {
  148.                     time = time.add(offsetAsNanos);
  149.                 }
  150.                 final BigInteger[] components =
  151.                         time.divideAndRemainder(ONE_BILLION);
  152.                 ts = new Timestamp(components[0].longValue());
  153.                 ts.setNanos(components[1].intValue());
  154.             }

  155.             return ts;
  156.         }

  157.         throw new TypeCastException(value, this);
  158.     }

  159.     @Override
  160.     public boolean isDateTime()
  161.     {
  162.         logger.debug("isDateTime() - start");

  163.         return true;
  164.     }

  165.     @Override
  166.     public Object getSqlValue(final int column, final ResultSet resultSet)
  167.             throws SQLException, TypeCastException
  168.     {
  169.         logger.debug("getSqlValue(column={}, resultSet={}) - start", column,
  170.                 resultSet);
  171.         final Timestamp rawValue = resultSet.getTimestamp(column);
  172.         final Timestamp value = resultSet.wasNull() ? null : rawValue;
  173.         logger.debug("getSqlValue: column={}, value={}", column, value);
  174.         return value;
  175.     }

  176.     @Override
  177.     public void setSqlValue(final Object value, final int column,
  178.             final PreparedStatement statement)
  179.             throws SQLException, TypeCastException
  180.     {
  181.         logger.debug("setSqlValue(value={}, column={}, statement={}) - start",
  182.                 value, column, statement);

  183.         statement.setTimestamp(column, (java.sql.Timestamp) typeCast(value));
  184.     }
  185. }