content: /*
   * Copyright 1999-2017 Alibaba Group Holding Ltd.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.druid.sql.ast.expr;
  
  import com.alibaba.druid.DbType;
  import com.alibaba.druid.sql.SQLUtils;
  import com.alibaba.druid.sql.ast.SQLDataType;
  import com.alibaba.druid.sql.ast.SQLDataTypeImpl;
  import com.alibaba.druid.sql.visitor.SQLASTVisitor;
  import com.alibaba.druid.util.MySqlUtils;
  
  import java.text.ParseException;
  import java.text.SimpleDateFormat;
  import java.util.*;
  
  public class SQLTimestampExpr extends SQLDateTypeExpr {
      public static final SQLDataType DATA_TYPE = new SQLDataTypeImpl(SQLDataType.Constants.TIMESTAMP);
  
      protected String literal;
      protected String timeZone;
      protected boolean withTimeZone;
  
      public SQLTimestampExpr() {
          super(DATA_TYPE);
      }
  
      public SQLTimestampExpr(String value) {
          this();
          this.value = value;
      }
  
      public SQLTimestampExpr(Date date) {
          this();
          setValue(date);
      }
  
      public SQLTimestampExpr(Date date, TimeZone timeZone) {
          this();
          setValue(date, timeZone);
      }
  
      @Override
      public String getValue() {
          return (String) value;
      }
  
      public SQLTimestampExpr clone() {
          SQLTimestampExpr x = new SQLTimestampExpr();
          x.value = value;
          x.timeZone = timeZone;
          x.withTimeZone = withTimeZone;
          return x;
      }
  
      public Date getDate(TimeZone timeZone) {
          if (getValue() == null || getValue().isEmpty()) {
              return null;
          }
  
          return MySqlUtils.parseDate(getValue(), timeZone);
      }
  
      public boolean addDay(int delta) {
          if (getValue() == null) {
              return false;
          }
  
          SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          try {
              Date date = format.parse(getValue());
              Calendar calendar = Calendar.getInstance();
              calendar.setTime(date);
              calendar.add(Calendar.DAY_OF_MONTH, delta);
              String result_chars = format.format(calendar.getTime());
              this.setValue(result_chars);
              return true;
          } catch (ParseException e) {
              // skip
          }
  
          return false;
      }
  
      public boolean addMonth(int delta) {
          if (getValue() == null) {
              return false;
          }
  
          SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          try {
              Date date = format.parse(getValue());
              Calendar calendar = Calendar.getInstance();
              calendar.setTime(date);
              calendar.add(Calendar.MONTH, delta);
              String result_chars = format.format(calendar.getTime());
              this.setValue(result_chars);
              return true;
          } catch (ParseException e) {
              // skip
          }
  
          return false;
      }
  
      public boolean addHour(int delta) {
          if (getValue() == null) {
              return false;
          }
  
          SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          try {
              Date date = format.parse(getValue());
              Calendar calendar = Calendar.getInstance();
              calendar.setTime(date);
              calendar.add(Calendar.HOUR_OF_DAY, delta);
              String result_chars = format.format(calendar.getTime());
              this.setValue(result_chars);
              return true;
          } catch (ParseException e) {
              // skip
          }
  
          return false;
      }
  
      public boolean addMiniute(int delta) {
          if (getValue() == null) {
              return false;
          }
  
          SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          try {
              Date date = format.parse(getValue());
              Calendar calendar = Calendar.getInstance();
              calendar.setTime(date);
              calendar.add(Calendar.MINUTE, delta);
              String result_chars = format.format(calendar.getTime());
              this.setValue(result_chars);
              return true;
          } catch (ParseException e) {
              // skip
          }
  
          return false;
      }
  
      public void setValue(String value) {
          this.value = value;
      }
  
      public void setValue(Date x) {
          setValue(x, null);
      }
  
      public void setValue(Date x, TimeZone timeZone) {
          if (x == null) {
              this.value = null;
              return;
          }
  
          SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
          if (timeZone != null) {
              format.setTimeZone(timeZone);
          }
          this.value = format.format(x);
      }
  
      public String getTimeZone() {
          return this.timeZone;
      }
  
      public void setTimeZone(String timeZone) {
          this.timeZone = timeZone;
      }
  
      public boolean isWithTimeZone() {
          return withTimeZone;
      }
  
      public void setWithTimeZone(boolean withTimeZone) {
          this.withTimeZone = withTimeZone;
      }
  
      @Override
      public int hashCode() {
          final int prime = 31;
          int result = 1;
          result = prime * result + ((value == null) ? 0 : value.hashCode());
          result = prime * result + ((timeZone == null) ? 0 : timeZone.hashCode());
          result = prime * result + (withTimeZone ? 1231 : 1237);
          return result;
      }
  
      @Override
      public boolean equals(Object obj) {
          if (this == obj) {
              return true;
          }
          if (obj == null) {
              return false;
          }
          if (getClass() != obj.getClass()) {
              return false;
          }
          SQLTimestampExpr other = (SQLTimestampExpr) obj;
          if (value == null) {
              if (other.value != null) {
                  return false;
              }
          } else if (!value.equals(other.value)) {
              return false;
          }
          if (timeZone == null) {
              if (other.timeZone != null) {
                  return false;
              }
          } else if (!timeZone.equals(other.timeZone)) {
              return false;
          }
          if (withTimeZone != other.withTimeZone) {
              return false;
          }
          return true;
      }
  
      @Override
      protected void accept0(SQLASTVisitor visitor) {
          if (visitor.visit(this)) {
              acceptChild(visitor, this.dataType);
          }
          visitor.endVisit(this);
      }
  
      public String toString() {
          return SQLUtils.toSQLString(this, (DbType) null);
      }
  
      @Override
      public List getChildren() {
          return Collections.emptyList();
      }
  
      public static boolean check(String str) {
          final int len;
          if (str == null || (len = str.length()) < 14 || len > 23) {
              return false;
          }
  
          final char c0 = str.charAt(0);
          final char c1 = str.charAt(1);
          final char c2 = str.charAt(2);
          final char c3 = str.charAt(3);
          final char c4 = str.charAt(4);
          final char c5 = str.charAt(5);
          final char c6 = str.charAt(6);
          final char c7 = str.charAt(7);
          final char c8 = str.charAt(8);
          final char c9 = str.charAt(9);
          final char c10 = str.charAt(10);
  
          // check year
          if (c0 < '1' | c0 > '9') {
              return false;
          }
          if (c1 < '0' | c1 > '9') {
              return false;
          }
          if (c2 < '0' | c2 > '9') {
              return false;
          }
          if (c3 < '0' | c3 > '9') {
              return false;
          }
  
          int year = (c0 - '0') * 1000 + (c1 - '0') * 100 + (c2 - '0') * 10 + (c3 - '0');
          if (year < 1000) {
              return false;
          }
  
          if (c4 != '-') {
              return false;
          }
  
          final int month, day;
          char M0 = 0, M1 = 0, d0 = 0, d1 = 0;
          if (c8 == ' ') {
              if (c6 != '-') {
                  return false;
              }
  
              M1 = c5;
              d1 = c7;
          } else if (c9 == ' ') {
              if (c6 == '-') {
                  M1 = c5;
                  d0 = c7;
                  d1 = c8;
              } else if (c7 == '-') {
                  M0 = c5;
                  M1 = c6;
                  d1 = c8;
              } else {
                  return false;
              }
          } else if (c10 == ' ') {
              if (c7 != '-') {
                  return false;
              }
  
              M0 = c5;
              M1 = c6;
              d0 = c8;
              d1 = c9;
          } else {
              return false;
          }
  
          if (M0 == 0) {
              if (M1 < '0' || M1 > '9') {
                  return false;
              }
              month = M1 - '0';
          } else {
              if (M0 != '0' && M0 != '1') {
                  return false;
              }
              if (M1 < '0' || M1 > '9') {
                  return false;
              }
              month = (M0 - '0') * 10 + M1 - '0';
          }
  
          if (d0 == 0) {
              if (d1 < '0' || d1 > '9') {
                  return false;
              }
              day = d1 - '0';
          } else {
              if (d0 < '0' || d0 > '9') {
                  return false;
              }
              if (d1 < '0' || d1 > '9') {
                  return false;
              }
              day = (d0 - '0') * 10 + d1 - '0';
          }
  
          if (month < 1) {
              return false;
          }
  
          if (day < 1) {
              return false;
          }
  
          switch (month) {
              case 1:
              case 3:
              case 5:
              case 7:
              case 8:
              case 10:
              case 12:
                  if (day > 31) {
                      return false;
                  }
                  break;
              case 2:
                  if (day > 29) {
                      return false;
                  }
                  break;
              case 4:
              case 6:
              case 9:
              case 11:
                  if (day > 30) {
                      return false;
                  }
                  break;
              default:
                  break;
          }
  
          int index = len - 1;
          if (str.charAt(index) == '0' && str.charAt(index - 1) == '.') {
              index -= 2;
          }
          final char x0 = str.charAt(index--);
          final char x1 = str.charAt(index--);
          final char x2 = str.charAt(index--);
          final char x3 = str.charAt(index--);
          final char x4 = str.charAt(index--);
          final char x5 = str.charAt(index--);
          final char x6 = str.charAt(index--);
          final char x7 = str.charAt(index--);
          final char x8 = str.charAt(index--);
          final char x9 = str.charAt(index--);
          final char x10 = str.charAt(index--);
          final char x11 = str.charAt(index--);
          final char x12 = str.charAt(index--);
  
          final char h0, h1, m0, m1, s0, s1;
  
          if (x5 == ' ') {
              if (x1 != ':' || x3 != ':') {
                  return false;
              }
              s0 = 0;
              s1 = x0;
              m0 = 0;
              m1 = x2;
              h0 = 0;
              h1 = x4;
          } else if (x6 == ' ') {
              if (x1 == ':') {
                  s0 = 0;
                  s1 = x0;
  
                  if (x3 == ':') {
                      m0 = 0;
                      m1 = x2;
                      h1 = x4;
                      h0 = x5;
                  } else if (x4 == ':') {
                      m0 = x2;
                      m1 = x3;
                      h0 = 0;
                      h1 = x5;
                  } else {
                      return false;
                  }
              } else if (x2 == ':') {
                  s0 = x0;
                  s1 = x1;
  
                  if (x4 != ':') {
                      return false;
                  }
  
                  m0 = 0;
                  m1 = x3;
                  h0 = 0;
                  h1 = x5;
              } else {
                  return false;
              }
          } else if (x7 == ' ') {
              if (x1 == ':') {
                  s0 = 0;
                  s1 = x0;
  
                  if (x4 != ':') {
                      return false;
                  }
                  m1 = x2;
                  m0 = x3;
                  h1 = x5;
                  h0 = x6;
              } else if (x2 == ':') {
                  s0 = x0;
                  s1 = x1;
  
                  if (x4 == ':') {
                      m0 = 0;
                      m1 = x3;
                      h1 = x5;
                      h0 = x6;
                  } else if (x5 == ':') {
                      m1 = x3;
                      m0 = x4;
                      h0 = 0;
                      h1 = x6;
                  } else {
                      return false;
                  }
              } else {
                  return false;
              }
          } else if (x8 == ' ') {
              if (x2 != ':' || x5 != ':') {
                  return false;
              }
              s1 = x0;
              s0 = x1;
              m1 = x3;
              m0 = x4;
              h1 = x6;
              h0 = x7;
          } else if (x12 == ' ') {
              if (x3 != '.' || x6 != ':' || x9 != ':') {
                  return false;
              }
              s1 = x4;
              s0 = x5;
              m1 = x7;
              m0 = x8;
              h1 = x10;
              h0 = x11;
  
              int S2 = x0;
              int S1 = x1;
              int S0 = x2;
  
              if (S0 < '0' || S0 > '9') {
                  return false;
              }
  
              if (S1 < '0' || S1 > '9') {
                  return false;
              }
  
              if (S2 < '0' || S2 > '9') {
                  return false;
              }
          } else {
              return false;
          }
  
          if (h0 == 0) {
              if (h1 < '0' || h1 > '9') {
                  return false;
              }
          } else {
              if (h0 < '0' || h0 > '2') {
                  return false;
              }
              if (h1 < '0' || h1 > '9') {
                  return false;
              }
  
              int hour = (h0 - '0') * 10 + (h1 - '0');
              if (hour > 24) {
                  return false;
              }
          }
  
          if (m0 == 0) {
              if (m1 < '0' || m1 > '9') {
                  return false;
              }
          } else {
              if (m0 < '0' || m0 > '6') {
                  return false;
              }
              if (m1 < '0' || m1 > '9') {
                  return false;
              }
  
              int minute = (m0 - '0') * 10 + (m1 - '0');
              if (minute > 60) {
                  return false;
              }
          }
  
          if (s0 == 0) {
              if (s1 < '0' || s1 > '9') {
                  return false;
              }
          } else {
              if (s0 < '0' || s0 > '6') {
                  return false;
              }
              if (s1 < '0' || s1 > '9') {
                  return false;
              }
  
              int second = (s0 - '0') * 10 + (s1 - '0');
              if (second > 60) {
                  return false;
              }
          }
  
          return true;
      }
  
      public static SQLTimestampExpr of(String str) {
          return new SQLTimestampExpr(str);
      }
  }
  