You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2014/05/20 20:46:07 UTC

[09/48] git commit: TAJO-761: Implements INTERVAL type. (Hyoungjun Kim via hyunsik)

TAJO-761: Implements INTERVAL type. (Hyoungjun Kim via hyunsik)


Project: http://git-wip-us.apache.org/repos/asf/tajo/repo
Commit: http://git-wip-us.apache.org/repos/asf/tajo/commit/5bbb7e82
Tree: http://git-wip-us.apache.org/repos/asf/tajo/tree/5bbb7e82
Diff: http://git-wip-us.apache.org/repos/asf/tajo/diff/5bbb7e82

Branch: refs/heads/window_function
Commit: 5bbb7e822ecde5b783ed1d6210b0e9fe9262d054
Parents: 8c92a9d
Author: Hyunsik Choi <hy...@apache.org>
Authored: Thu Apr 24 21:03:21 2014 +0900
Committer: Hyunsik Choi <hy...@apache.org>
Committed: Thu Apr 24 21:03:21 2014 +0900

----------------------------------------------------------------------
 CHANGES                                         |   4 +
 .../apache/tajo/algebra/IntervalLiteral.java    |  46 ++
 .../java/org/apache/tajo/algebra/OpType.java    |   3 +-
 .../java/org/apache/tajo/datum/DateDatum.java   |  75 +++-
 .../org/apache/tajo/datum/DatumFactory.java     |   8 +
 .../java/org/apache/tajo/datum/Float4Datum.java |   7 +
 .../java/org/apache/tajo/datum/Float8Datum.java |   7 +
 .../java/org/apache/tajo/datum/Int2Datum.java   |   7 +
 .../java/org/apache/tajo/datum/Int4Datum.java   |   7 +
 .../java/org/apache/tajo/datum/Int8Datum.java   |   8 +
 .../org/apache/tajo/datum/IntervalDatum.java    | 434 +++++++++++++++++++
 .../java/org/apache/tajo/datum/TimeDatum.java   |  31 ++
 .../org/apache/tajo/datum/TimestampDatum.java   |  58 ++-
 .../java/org/apache/tajo/json/DatumAdapter.java |   8 +
 .../org/apache/tajo/util/TimeStampUtil.java     |   1 -
 .../apache/tajo/datum/TestIntervalDatum.java    | 199 +++++++++
 .../apache/tajo/datum/TestTimestampDatum.java   |   3 +
 .../org/apache/tajo/engine/parser/SQLLexer.g4   |   1 +
 .../org/apache/tajo/engine/parser/SQLParser.g4  |   7 +
 .../org/apache/tajo/engine/eval/BinaryEval.java |  40 ++
 .../tajo/engine/function/datetime/ToDate.java   |  71 +++
 .../apache/tajo/engine/parser/SQLAnalyzer.java  |  13 +-
 .../tajo/engine/planner/AlgebraVisitor.java     |   1 +
 .../tajo/engine/planner/BaseAlgebraVisitor.java |   9 +-
 .../tajo/engine/planner/ExprAnnotator.java      |   5 +
 .../tajo/engine/planner/ExprNormalizer.java     |  20 +-
 .../tajo/engine/planner/ExprsVerifier.java      |  37 +-
 .../tajo/engine/eval/TestIntervalType.java      |  96 ++++
 .../engine/function/TestDateTimeFunctions.java  |   6 +
 .../org/apache/tajo/storage/RowStoreUtil.java   |  12 +
 .../storage/TextSerializerDeserializer.java     |   5 +
 31 files changed, 1212 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index d7a17d3..00f00ad 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,10 @@ Tajo Change Log
 
 Release 0.9.0 - unreleased
 
+  NEW FEATURES
+
+    TAJO-761: Implements INTERVAL type. (Hyoungjun Kim via hyunsik)
+
   IMPROVEMENT
 
     TAJO-769: A minor improvements for HCatalogStore (Fengdong Yu via hyunsik)

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-algebra/src/main/java/org/apache/tajo/algebra/IntervalLiteral.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/IntervalLiteral.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/IntervalLiteral.java
new file mode 100644
index 0000000..6e0f2d0
--- /dev/null
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/IntervalLiteral.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.algebra;
+
+public class IntervalLiteral extends Expr {
+  private String exprStr;
+
+  public IntervalLiteral(String str) {
+    super(OpType.IntervalLiteral);
+    this.exprStr = str;
+  }
+
+
+  public String getExprStr() {
+    return exprStr;
+  }
+
+  public int hashCode() {
+    return exprStr.hashCode();
+  }
+
+  @Override
+  boolean equalsTo(Expr expr) {
+    if (expr instanceof IntervalLiteral) {
+      IntervalLiteral another = (IntervalLiteral) expr;
+      return exprStr.equals(another.exprStr);
+    }
+    return false;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
index a62ccfd..aeb28f0 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
@@ -105,7 +105,8 @@ public enum OpType {
   NullLiteral(NullLiteral.class),
   TimeLiteral(TimeLiteral.class),
   DateLiteral(DateLiteral.class),
-  TimestampLiteral(TimestampLiteral.class);
+  TimestampLiteral(TimestampLiteral.class),
+  IntervalLiteral(IntervalLiteral.class);
 
   private Class baseClass;
 

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java
index c327931..6b155ac 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/DateDatum.java
@@ -22,7 +22,7 @@ import org.apache.tajo.common.TajoDataTypes;
 import org.apache.tajo.datum.exception.InvalidCastException;
 import org.apache.tajo.datum.exception.InvalidOperationException;
 import org.apache.tajo.util.Bytes;
-import org.joda.time.LocalDate;
+import org.joda.time.*;
 import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
 
@@ -53,6 +53,11 @@ public class DateDatum extends Datum {
     this.date = date;
   }
 
+  public LocalDate getDate() {
+    //LocalDate is immutable
+    return date;
+  }
+
   public DateDatum(byte [] bytes) {
     this(Bytes.toInt(bytes));
   }
@@ -93,6 +98,74 @@ public class DateDatum extends Datum {
     return asChars();
   }
 
+  public Datum plus(Datum datum) {
+    switch(datum.type()) {
+      case INT2:
+      case INT4:
+      case INT8:
+      case FLOAT4:
+      case FLOAT8:
+        return new DateDatum(date.plusDays(datum.asInt2()));
+      case INTERVAL:
+        IntervalDatum interval = (IntervalDatum)datum;
+        LocalDate localDate;
+        if (interval.getMonths() > 0) {
+          localDate = date.plusMonths(interval.getMonths());
+        } else {
+          localDate = date;
+        }
+        return new TimestampDatum(localDate.toDateTimeAtStartOfDay().getMillis() + interval.getMilliSeconds());
+      case TIME:
+        return new TimestampDatum(createDateTime(date, ((TimeDatum)datum).getTime(), true));
+      default:
+        throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  public Datum minus(Datum datum) {
+    switch(datum.type()) {
+      case INT2:
+      case INT4:
+      case INT8:
+      case FLOAT4:
+      case FLOAT8:
+        return new DateDatum(date.minusDays(datum.asInt2()));
+      case INTERVAL:
+        IntervalDatum interval = (IntervalDatum)datum;
+        LocalDate localDate;
+        if (interval.getMonths() > 0) {
+          localDate = date.minusMonths(interval.getMonths());
+        } else {
+          localDate = date;
+        }
+        return new TimestampDatum(localDate.toDateTimeAtStartOfDay().getMillis() - interval.getMilliSeconds());
+      case TIME:
+        return new TimestampDatum(createDateTime(date, ((TimeDatum)datum).getTime(), false));
+      case DATE:
+        return new Int4Datum(Days.daysBetween(((DateDatum)datum).date, date).getDays());
+      default:
+        throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  public static DateTime createDateTime(LocalDate date, LocalTime time, boolean plus) {
+    //TODO create too many temporary instance. This must be improved.
+    DateTime dateTime = new DateTime(date.toDate().getTime());
+    if (plus) {
+      return dateTime
+                .plusHours(time.getHourOfDay())
+                .plusMinutes(time.getMinuteOfHour())
+                .plusSeconds(time.getSecondOfMinute())
+                .plusMillis(time.getMillisOfSecond());
+    } else {
+      return dateTime
+                .minusHours(time.getHourOfDay())
+                .minusMinutes(time.getMinuteOfHour())
+                .minusSeconds(time.getSecondOfMinute())
+                .minusMillis(time.getMillisOfSecond());
+    }
+  }
+
   @Override
   public int asInt4() {
     return encode();

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java b/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java
index 2ddea9e..4eacfc3 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/DatumFactory.java
@@ -51,6 +51,8 @@ public class DatumFactory {
         return TextDatum.class;
       case TIMESTAMP:
         return TimestampDatum.class;
+      case INTERVAL:
+        return IntervalDatum.class;
       case DATE:
         return DateDatum.class;
       case TIME:
@@ -95,6 +97,8 @@ public class DatumFactory {
       return createTime(value);
     case TIMESTAMP:
       return createTimeStamp(value);
+    case INTERVAL:
+      return createInterval(value);
     case BLOB:
       return createBlob(value);
     case INET4:
@@ -282,6 +286,10 @@ public class DatumFactory {
     return new TimestampDatum(timeStamp);
   }
 
+  public static IntervalDatum createInterval(String intervalStr) {
+    return new IntervalDatum(intervalStr);
+  }
+
   public static DateDatum createDate(Datum datum) {
     switch (datum.type()) {
     case INT4:

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
index 0c65dfc..b042f2d 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Float4Datum.java
@@ -210,6 +210,8 @@ public class Float4Datum extends NumericDatum {
       return DatumFactory.createFloat8(val + datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val + datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt4()));
     case NULL_TYPE:
       return datum;
     default:
@@ -230,6 +232,8 @@ public class Float4Datum extends NumericDatum {
       return DatumFactory.createFloat8(val - datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val - datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt4()));
     case NULL_TYPE:
       return datum;
     default:
@@ -250,6 +254,9 @@ public class Float4Datum extends NumericDatum {
       return DatumFactory.createFloat8(val * datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val * datum.asFloat8());
+    case INTERVAL:
+      IntervalDatum interval = (IntervalDatum)datum;
+      return new IntervalDatum((int)(interval.getMonths() * val), (long)(interval.getMilliSeconds() * val));
     case NULL_TYPE:
       return datum;
     default:

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
index 010cf29..55b079f 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Float8Datum.java
@@ -199,6 +199,8 @@ public class Float8Datum extends NumericDatum {
       return DatumFactory.createFloat8(val + datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val + datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt4()));
     case NULL_TYPE:
       return datum;
     default:
@@ -219,6 +221,8 @@ public class Float8Datum extends NumericDatum {
       return DatumFactory.createFloat8(val - datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val - datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt4()));
     case NULL_TYPE:
       return datum;
     default:
@@ -239,6 +243,9 @@ public class Float8Datum extends NumericDatum {
       return DatumFactory.createFloat8(val * datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val * datum.asFloat8());
+    case INTERVAL:
+      IntervalDatum interval = (IntervalDatum)datum;
+      return new IntervalDatum((int)(interval.getMonths() * val), (long)(interval.getMilliSeconds() * val));
     case NULL_TYPE:
       return datum;
     default:

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
index e24d7be..46616e7 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int2Datum.java
@@ -201,6 +201,8 @@ public class Int2Datum extends NumericDatum {
       return DatumFactory.createFloat4(val + datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val + datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt2()));
     case NULL_TYPE:
       return datum;
     default:
@@ -221,6 +223,8 @@ public class Int2Datum extends NumericDatum {
       return DatumFactory.createFloat4(val - datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val - datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt2()));
     case NULL_TYPE:
       return datum;
     default:
@@ -241,6 +245,9 @@ public class Int2Datum extends NumericDatum {
       return DatumFactory.createFloat4(val * datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val * datum.asFloat8());
+    case INTERVAL:
+      IntervalDatum interval = (IntervalDatum)datum;
+      return new IntervalDatum(interval.getMonths() * val, interval.getMilliSeconds() * val);
     case NULL_TYPE:
       return datum;
     default:

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
index 9cd0c06..8296a0f 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int4Datum.java
@@ -205,6 +205,8 @@ public class Int4Datum extends NumericDatum {
       return DatumFactory.createFloat4(val + datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val + datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt4()));
     case NULL_TYPE:
       return datum;
     default:
@@ -225,6 +227,8 @@ public class Int4Datum extends NumericDatum {
       return DatumFactory.createFloat4(val - datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val - datum.asFloat8());
+    case DATE:
+        return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt4()));
     case NULL_TYPE:
       return datum;
     default:
@@ -245,6 +249,9 @@ public class Int4Datum extends NumericDatum {
       return DatumFactory.createFloat4(val * datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val * datum.asFloat8());
+    case INTERVAL:
+      IntervalDatum interval = (IntervalDatum)datum;
+      return new IntervalDatum(interval.getMonths() * val, interval.getMilliSeconds() * val);
     case NULL_TYPE:
       return datum;
     default:

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java b/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
index 1104a3c..6d3e5b2 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/Int8Datum.java
@@ -213,6 +213,8 @@ public class Int8Datum extends NumericDatum {
       return DatumFactory.createFloat8(val + datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val + datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().plusDays(asInt4()));
     case NULL_TYPE:
       return datum;
     default:
@@ -233,6 +235,8 @@ public class Int8Datum extends NumericDatum {
       return DatumFactory.createFloat8(val - datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val - datum.asFloat8());
+    case DATE:
+      return new DateDatum(((DateDatum)datum).getDate().minusDays(asInt4()));
     case NULL_TYPE:
       return datum;
     default:
@@ -253,6 +257,10 @@ public class Int8Datum extends NumericDatum {
       return DatumFactory.createFloat8(val * datum.asFloat4());
     case FLOAT8:
       return DatumFactory.createFloat8(val * datum.asFloat8());
+    case INTERVAL:
+      IntervalDatum interval = (IntervalDatum)datum;
+      int intVal = asInt4();
+      return new IntervalDatum(interval.getMonths() * intVal, interval.getMilliSeconds() * asInt8());
     case NULL_TYPE:
       return datum;
     default:

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/IntervalDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/IntervalDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/IntervalDatum.java
new file mode 100644
index 0000000..4e04f91
--- /dev/null
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/IntervalDatum.java
@@ -0,0 +1,434 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.datum;
+
+import com.google.common.base.Objects;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.datum.exception.InvalidOperationException;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalTime;
+
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+public class IntervalDatum extends Datum {
+  static final long MINUTE_MILLIS = 60 * 1000;
+  static final long HOUR_MILLIS = 60 * MINUTE_MILLIS;
+  static final long DAY_MILLIS = 24 * HOUR_MILLIS;
+  static final long MONTH_MILLIS = 30 * DAY_MILLIS;
+
+  static enum DATE_UNIT {
+    CENTURY, DECADE, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MICROSEC, MILLISEC, TIMEZONE,
+  }
+  static Map<String, DATE_UNIT> DATE_FORMAT_LITERAL = new HashMap<String, DATE_UNIT>();
+  static {
+    Object[][] dateFormatLiterals = new Object[][]{
+        {DATE_UNIT.CENTURY, "c,cent,centuries,century"},
+        {DATE_UNIT.DAY, "d,day,days"},
+        {DATE_UNIT.DECADE, "dec,decade,decades,decs"},
+        {DATE_UNIT.HOUR, "h,hour,hours,hr,hrs"},
+        {DATE_UNIT.MILLISEC, "millisecon,ms,msec,msecond,mseconds,msecs"},
+        {DATE_UNIT.MINUTE, "m,min,mins,minute,minutes"},
+        {DATE_UNIT.MONTH, "mon,mons,month,months"},
+        {DATE_UNIT.SECOND, "s,sec,second,seconds,secs"},
+        {DATE_UNIT.TIMEZONE, "timezone"},
+        {DATE_UNIT.MICROSEC, "microsecon,us,usec,microsecond,useconds,usecs"},
+        {DATE_UNIT.YEAR, "y,year,years,yr,yrs"}
+    };
+
+    for (Object[] eachLiteral: dateFormatLiterals) {
+      String[] tokens = ((String)eachLiteral[1]).split(",");
+      DATE_UNIT unit = (DATE_UNIT)eachLiteral[0];
+      for (String eachToken: tokens) {
+        DATE_FORMAT_LITERAL.put(eachToken, unit);
+      }
+    }
+  }
+
+  private int months;
+  private long millieconds;
+
+  public IntervalDatum(long milliseconds) {
+    this(0, milliseconds);
+  }
+
+  public IntervalDatum(int months, long milliseconds) {
+    super(TajoDataTypes.Type.INTERVAL);
+    this.months = months;
+    this.millieconds = milliseconds;
+  }
+
+  public IntervalDatum(String intervalStr) {
+    super(TajoDataTypes.Type.INTERVAL);
+
+    intervalStr = intervalStr.trim();
+    if (intervalStr.isEmpty()) {
+      throw new InvalidOperationException("interval expression is empty");
+    }
+
+    try {
+      int year = 0;
+      int month = 0;
+      int day = 0;
+      int hour = 0;
+      int minute = 0;
+      int second = 0;
+      int microsecond = 0;
+      int millisecond = 0;
+      long time = 0;
+
+      int length = intervalStr.getBytes().length;
+
+      int start = 0;
+      StringBuilder digitChars = new StringBuilder();
+      StringBuilder unitChars = new StringBuilder();
+      for (int i = 0; i < length; i++) {
+        char c = intervalStr.charAt(i);
+        if (Character.isDigit(c) || c == ':' || c == '.' || c == '-') {
+          digitChars.append(c);
+        } else if (c == ' ') {
+          if (digitChars.length() > 0) {
+            if (unitChars.length() > 0) {
+              String unitName = unitChars.toString();
+              DATE_UNIT foundUnit = DATE_FORMAT_LITERAL.get(unitName);
+              if (foundUnit == null) {
+                throw new InvalidOperationException("invalid input syntax for type interval: " + intervalStr);
+              }
+              int digit = Integer.parseInt(digitChars.toString());
+              switch(foundUnit) {
+                case YEAR:    year = digit;   break;
+                case MONTH:   month = digit;  break;
+                case DAY:     day = digit;    break;
+                case HOUR:    hour = digit;   break;
+                case MINUTE:  minute = digit; break;
+                case SECOND:  second = digit;   break;
+                case MICROSEC:  microsecond = digit;  break;
+                case MILLISEC:  millisecond = digit;  break;
+              default: throw new InvalidOperationException("Unknown datetime unit: " + foundUnit);
+              }
+              digitChars.setLength(0);
+              unitChars.setLength(0);
+            } else if (digitChars.indexOf(":") >= 0) {
+              time = parseTime(digitChars.toString());
+              digitChars.setLength(0);
+            }
+          }
+        } else {
+          unitChars.append(c);
+        }
+      }
+      if (digitChars.length() > 0) {
+        if (unitChars.length() > 0) {
+          String unitName = unitChars.toString();
+          DATE_UNIT foundUnit = DATE_FORMAT_LITERAL.get(unitName);
+          if (foundUnit == null) {
+            throw new InvalidOperationException("invalid input syntax for type interval: " + intervalStr);
+          }
+          int digit = Integer.parseInt(digitChars.toString());
+          switch(foundUnit) {
+            case YEAR:    year = digit;   break;
+            case MONTH:   month = digit;  break;
+            case DAY:     day = digit;    break;
+            case HOUR:    hour = digit;   break;
+            case MINUTE:  minute = digit; break;
+            case SECOND:  second = digit;   break;
+            case MICROSEC:  microsecond = digit;  break;
+            case MILLISEC:  millisecond = digit;  break;
+            default: throw new InvalidOperationException("Unknown datetime unit: " + foundUnit);
+          }
+        } else if (digitChars.indexOf(":") >= 0) {
+          time = parseTime(digitChars.toString());
+          digitChars.setLength(0);
+        }
+      }
+
+      if (time > 0 && (hour != 0 || minute != 0 || second != 0 || microsecond != 0 || millisecond != 0)) {
+          throw new InvalidOperationException("invalid input syntax for type interval: " + intervalStr);
+      }
+
+      this.millieconds = time + day * DAY_MILLIS + hour * HOUR_MILLIS + minute * 60 * 1000L + second * 1000L +
+          microsecond * 100L + millisecond;
+      this.months = year * 12 + month;
+    } catch (InvalidOperationException e) {
+      throw e;
+    } catch (Throwable t) {
+      throw new InvalidOperationException(t.getMessage() + ": " + intervalStr);
+    }
+  }
+
+  public static long parseTime(String timeStr) {
+    //parse HH:mm:ss.SSS
+    int hour = 0;
+    int minute = 0;
+
+    int second = 0;
+    int millisecond = 0;
+    String[] timeTokens = timeStr.split(":");
+    if (timeTokens.length == 1) {
+      //sec
+      String[] secondTokens = timeTokens[0].split("\\.");
+      if (secondTokens.length == 1) {
+        second = Integer.parseInt(secondTokens[0]);
+      } else if (secondTokens.length == 2) {
+        millisecond = Integer.parseInt(secondTokens[1]);
+      } else {
+        throw new InvalidOperationException("invalid input syntax for type interval: " + timeStr);
+      }
+    } else {
+      if (timeTokens.length > 3) {
+        throw new InvalidOperationException("invalid input syntax for type interval: " + timeStr);
+      }
+      for(int i = 0; i < timeTokens.length - 1; i++) {
+        if (i == 0)   hour = Integer.parseInt(timeTokens[i]);
+        if (i == 1)   minute = Integer.parseInt(timeTokens[i]);
+      }
+      if (timeTokens.length == 3) {
+        //sec
+        String[] secondTokens = timeTokens[2].split("\\.");
+        if (secondTokens.length == 1) {
+          second = Integer.parseInt(secondTokens[0]);
+        } else if (secondTokens.length == 2) {
+          second = Integer.parseInt(secondTokens[0]);
+          millisecond = Integer.parseInt(secondTokens[1]);
+        } else {
+          throw new InvalidOperationException("invalid input syntax for type interval: " + timeStr);
+        }
+      }
+    }
+
+    return hour * HOUR_MILLIS+ minute * MINUTE_MILLIS + second * 1000 + millisecond;
+  }
+
+  public int getMonths() {
+    return this.months;
+  }
+
+  public long getMilliSeconds() {
+    return millieconds;
+  }
+
+  @Override
+  public Datum plus(Datum datum) {
+    switch(datum.type()) {
+      case INTERVAL:
+        IntervalDatum other = (IntervalDatum) datum;
+        return new IntervalDatum(months + other.months, millieconds + other.millieconds);
+      case DATE:
+        LocalDate date = ((DateDatum)datum).getDate();
+        LocalDate localDate;
+        if (months > 0) {
+          localDate = date.plusMonths(months);
+        } else {
+          localDate = date;
+        }
+        return new TimestampDatum(localDate.toDateTimeAtStartOfDay().getMillis() + millieconds);
+      case TIME:
+        LocalTime localTime = ((TimeDatum)datum).getTime();
+        localTime = localTime.plusMillis((int) millieconds);
+        return new TimeDatum(localTime);
+      case TIMESTAMP:
+        DateTime dateTime = ((TimestampDatum) datum).getDateTime();
+        if (months > 0) {
+          dateTime = dateTime.plusMonths(months);
+        }
+        if (millieconds > 0) {
+          dateTime = dateTime.plusMillis((int) millieconds);
+        }
+        return new TimestampDatum(dateTime);
+      default:
+        throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  @Override
+  public Datum minus(Datum datum) {
+    if (datum.type() == TajoDataTypes.Type.INTERVAL) {
+      IntervalDatum other = (IntervalDatum) datum;
+      return new IntervalDatum(months - other.months, millieconds - other.millieconds);
+    } else {
+      throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  @Override
+  public Datum multiply(Datum datum) {
+    switch(datum.type()) {
+      case INT2:
+      case INT4:
+      case INT8:
+        long int8Val = datum.asInt8();
+        return createIntervalDatum((double)months * int8Val, (double) millieconds * int8Val);
+      case FLOAT4:
+      case FLOAT8:
+        double float8Val = datum.asFloat8();
+        return createIntervalDatum((double)months * float8Val, (double) millieconds * float8Val);
+      default:
+        throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  @Override
+  public Datum divide(Datum datum) {
+    switch(datum.type()) {
+      case INT2:
+      case INT4:
+      case INT8:
+        long int8Val = datum.asInt8();
+        return createIntervalDatum((double)months / int8Val, (double) millieconds / int8Val);
+      case FLOAT4:
+      case FLOAT8:
+        double float8Val = datum.asFloat8();
+        return createIntervalDatum((double)months / float8Val, (double) millieconds / float8Val);
+      default:
+        throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  private IntervalDatum createIntervalDatum(double monthValue, double millisValue) {
+    int month = (int)(monthValue);
+    return new IntervalDatum(month, Math.round((monthValue - (double)month) * (double)MONTH_MILLIS + (double)millisValue));
+  }
+
+  @Override
+  public long asInt8() {
+    return (months * 30) * DAY_MILLIS + millieconds;
+  }
+
+  public String toString() {
+    return asChars();
+  }
+
+  static DecimalFormat df = new DecimalFormat("00");
+  static DecimalFormat df2 = new DecimalFormat("000");
+  @Override
+  public String asChars() {
+    try {
+      StringBuilder sb = new StringBuilder();
+
+      String prefix = "";
+
+      if (months != 0) {
+        int positiveNum = Math.abs(months);
+        int year = positiveNum / 12;
+        int remainMonth = positiveNum - year * 12;
+
+        if (year > 0) {
+          sb.append(months < 0 ? "-" : "");
+          sb.append(year).append(year == 1 ? " year" : " years");
+          prefix = " ";
+        }
+        sb.append(prefix).append(months < 0 ? "-" : "").append(remainMonth).append(months == 1 ? " month" : " months");
+        prefix = " ";
+      }
+
+      formatMillis(sb, prefix, millieconds);
+      return sb.toString();
+    } catch (Exception e) {
+      return "";
+    }
+  }
+
+  public static String formatMillis(long millis) {
+    StringBuilder sb = new StringBuilder();
+    formatMillis(sb, "", millis);
+    return sb.toString();
+  }
+
+  public static void formatMillis(StringBuilder sb, String prefix, long millis) {
+    if (millis != 0) {
+      long positiveNum = Math.abs(millis);
+      int days = (int)(positiveNum / DAY_MILLIS);
+      long remainInterval = positiveNum - days * DAY_MILLIS;
+
+      if(days != 0) {
+        sb.append(prefix).append(millis < 0 ? "-" : "").append(days).append(days == 1 ? " day" : " days");
+        prefix = " ";
+      }
+      if (remainInterval != 0) {
+        int hour = (int) (remainInterval / HOUR_MILLIS);
+        int minutes = (int) (remainInterval - hour * HOUR_MILLIS) / (60 * 1000);
+        long sec = (int) (remainInterval - hour * HOUR_MILLIS - minutes * (60 * 1000L)) / 1000L;
+        long millisecond = (int) (remainInterval - hour * HOUR_MILLIS - minutes * (60 * 1000L) - sec * 1000L);
+
+        sb.append(prefix)
+            .append(millis < 0 ? "-" : "")
+            .append(df.format(hour))
+            .append(":")
+            .append(df.format(minutes))
+            .append(":")
+            .append(df.format(sec));
+
+        if (millisecond > 0) {
+          sb.append(".").append(df2.format(millisecond));
+        }
+      }
+    }
+  }
+
+  @Override
+  public int size() {
+    return 0;
+  }
+
+  @Override
+  public int compareTo(Datum datum) {
+    if (datum.type() == TajoDataTypes.Type.INTERVAL) {
+      long val = asInt8();
+      long another = datum.asInt8();
+      if (val < another) {
+        return -1;
+      } else if (val > another) {
+        return 1;
+      } else {
+        return 0;
+      }
+    } else if (datum instanceof NullDatum || datum.isNull()) {
+      return -1;
+    } else {
+      throw new InvalidOperationException();
+    }
+  }
+
+  @Override
+  public Datum equalsTo(Datum datum) {
+    if (datum.type() == TajoDataTypes.Type.INTERVAL) {
+      return DatumFactory.createBool(asInt8() == datum.asInt8());
+    } else if (datum.isNull()) {
+      return datum;
+    } else {
+      throw new InvalidOperationException();
+    }
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof IntervalDatum) {
+      return asInt8() == ((IntervalDatum)obj).asInt8();
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public int hashCode(){
+    return Objects.hashCode(asInt8());
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java
index 21ae881..e7f1684 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/TimeDatum.java
@@ -22,6 +22,7 @@ import org.apache.tajo.common.TajoDataTypes;
 import org.apache.tajo.datum.exception.InvalidCastException;
 import org.apache.tajo.datum.exception.InvalidOperationException;
 import org.apache.tajo.util.Bytes;
+import org.joda.time.DateTime;
 import org.joda.time.LocalTime;
 import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
@@ -125,6 +126,32 @@ public class TimeDatum extends Datum {
     return Bytes.toBytes(asInt8());
   }
 
+  public Datum plus(Datum datum) {
+    switch(datum.type()) {
+      case INTERVAL:
+        IntervalDatum interval = ((IntervalDatum)datum);
+        return new TimeDatum(time.plusMillis((int)interval.getMilliSeconds()));
+      case DATE:
+        DateTime dateTime = DateDatum.createDateTime(((DateDatum)datum).getDate(), time, true);
+        return new TimestampDatum(dateTime);
+      default:
+        throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  public Datum minus(Datum datum) {
+    switch(datum.type()) {
+      case INTERVAL:
+        IntervalDatum interval = ((IntervalDatum)datum);
+        return new TimeDatum(time.minusMillis((int)interval.getMilliSeconds()));
+      case TIME:
+        return new IntervalDatum(
+            time.toDateTimeToday().getMillis() - ((TimeDatum)datum).getTime().toDateTimeToday().getMillis() );
+      default:
+        throw new InvalidOperationException(datum.type());
+    }
+  }
+
   @Override
   public Datum equalsTo(Datum datum) {
     if (datum.type() == TajoDataTypes.Type.TIME) {
@@ -160,4 +187,8 @@ public class TimeDatum extends Datum {
   public int hashCode() {
     return time.hashCode();
   }
+
+  public LocalTime getTime() {
+    return time;
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java b/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java
index 6411bec..219d5ca 100644
--- a/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java
+++ b/tajo-common/src/main/java/org/apache/tajo/datum/TimestampDatum.java
@@ -41,6 +41,11 @@ public class TimestampDatum extends Datum {
     dateTime = new DateTime((long)timestamp * 1000);
   }
 
+  public TimestampDatum(long timestamp) {
+    super(TajoDataTypes.Type.TIMESTAMP);
+    dateTime = new DateTime(timestamp);
+  }
+
   public TimestampDatum(DateTime dateTime) {
     super(TajoDataTypes.Type.TIMESTAMP);
     this.dateTime = dateTime;
@@ -53,7 +58,14 @@ public class TimestampDatum extends Datum {
 
   public TimestampDatum(String datetime) {
     super(TajoDataTypes.Type.TIMESTAMP);
-    this.dateTime = DateTime.parse(datetime, DEFAULT_FORMATTER);
+
+    DateTime tmpDateTime = null;
+    try {
+      tmpDateTime = DateTime.parse(datetime, DEFAULT_FORMATTER);
+    } catch (IllegalArgumentException e) {
+      tmpDateTime = DateTime.parse(datetime, FRACTION_FORMATTER);
+    }
+    this.dateTime = tmpDateTime;
   }
 
   public int getUnixTime() {
@@ -125,6 +137,50 @@ public class TimestampDatum extends Datum {
   }
 
   @Override
+  public Datum plus(Datum datum) {
+    if (datum.type() == TajoDataTypes.Type.INTERVAL) {
+      IntervalDatum interval = (IntervalDatum)datum;
+
+      DateTime plusDateTime = null;
+      if (interval.getMonths() > 0) {
+        plusDateTime = dateTime.plusMonths(interval.getMonths());
+      } else {
+        plusDateTime = dateTime;
+      }
+      if (interval.getMilliSeconds() > 0) {
+        plusDateTime = plusDateTime.plusMillis((int) interval.getMilliSeconds());
+      }
+      return new TimestampDatum(plusDateTime);
+
+    } else {
+      throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  @Override
+  public Datum minus(Datum datum) {
+    switch(datum.type()) {
+      case INTERVAL:
+        IntervalDatum interval = (IntervalDatum)datum;
+
+        DateTime minusDateTime = null;
+        if (interval.getMonths() > 0) {
+          minusDateTime = dateTime.minusMonths(interval.getMonths());
+        } else {
+          minusDateTime = dateTime;
+        }
+        if (interval.getMilliSeconds() > 0) {
+          minusDateTime = minusDateTime.minusMillis((int)interval.getMilliSeconds());
+        }
+        return new TimestampDatum(minusDateTime);
+      case TIMESTAMP:
+        return new IntervalDatum(dateTime.getMillis() - ((TimestampDatum)datum).dateTime.getMillis());
+      default:
+        throw new InvalidOperationException(datum.type());
+    }
+  }
+
+  @Override
   public long asInt8() {
     return dateTime.getMillis();
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java b/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java
index f24d213..2a162a9 100644
--- a/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java
+++ b/tajo-common/src/main/java/org/apache/tajo/json/DatumAdapter.java
@@ -40,6 +40,10 @@ public class DatumAdapter implements GsonSerDerAdapter<Datum> {
       return new TimeDatum(jsonObject.get("value").getAsLong());
     case TIMESTAMP:
       return new TimestampDatum(new DateTime(jsonObject.get("value").getAsLong()));
+    case INTERVAL:
+      String[] values = jsonObject.get("value").getAsString().split(",");
+
+      return new IntervalDatum(Integer.parseInt(values[0]), Long.parseLong(values[1]));
     default:
       return context.deserialize(jsonObject.get("body"),
           DatumFactory.getDatumClass(TajoDataTypes.Type.valueOf(typeName)));
@@ -60,6 +64,10 @@ public class DatumAdapter implements GsonSerDerAdapter<Datum> {
     case TIMESTAMP:
       jsonObj.addProperty("value", ((TimestampDatum)src).getMillis());
       break;
+    case INTERVAL:
+      IntervalDatum interval = (IntervalDatum)src;
+      jsonObj.addProperty("value", interval.getMonths() + "," + interval.getMilliSeconds());
+      break;
     default:
       jsonObj.add("body", context.serialize(src));
     }

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java b/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java
index 830c4aa..aa1be8b 100644
--- a/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java
+++ b/tajo-common/src/main/java/org/apache/tajo/util/TimeStampUtil.java
@@ -63,5 +63,4 @@ public class TimeStampUtil {
     public static long convertToMicroSeconds(DateTime dateTime) {
         return  dateTime.getMillis() * 1000;
     }
-
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/test/java/org/apache/tajo/datum/TestIntervalDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/test/java/org/apache/tajo/datum/TestIntervalDatum.java b/tajo-common/src/test/java/org/apache/tajo/datum/TestIntervalDatum.java
new file mode 100644
index 0000000..5936c31
--- /dev/null
+++ b/tajo-common/src/test/java/org/apache/tajo/datum/TestIntervalDatum.java
@@ -0,0 +1,199 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.datum;
+
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.datum.exception.InvalidOperationException;
+import org.joda.time.DateTime;
+import org.joda.time.LocalTime;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class TestIntervalDatum {
+  @Test
+  public final void parseIntervalDatum() {
+    IntervalDatum datum = new IntervalDatum("3y 5month 10day 23:50:40.200");
+    assertEquals("3 years 5 months 10 days 23:50:40.200", datum.asChars());
+
+    datum = new IntervalDatum("23:50:40.200 5month 10day 3y");
+    assertEquals("3 years 5 months 10 days 23:50:40.200", datum.asChars());
+
+    datum = new IntervalDatum("3y   5month 10  day   23:50:40.200");
+    assertEquals("3 years 5 months 10 days 23:50:40.200", datum.asChars());
+
+    datum = new IntervalDatum("3years   5months 10  day   23:50:40.200");
+    assertEquals("3 years 5 months 10 days 23:50:40.200", datum.asChars());
+
+    datum = new IntervalDatum("5 hour");
+    assertEquals("05:00:00", datum.asChars());
+    try {
+      datum = new IntervalDatum("3years   5months 10  day  1h 23:50:40.200");
+      fail("hour and time format can not be used at the same time  in interval.");
+    } catch (InvalidOperationException e) {
+      //success
+    }
+  }
+
+  @Test
+  public final void testAsText() {
+    IntervalDatum datum = new IntervalDatum(14,
+        IntervalDatum.DAY_MILLIS + 10 * IntervalDatum.HOUR_MILLIS + 20 * IntervalDatum.MINUTE_MILLIS + 30 * 1000 + 400);
+    assertEquals("1 year 2 months 1 day 10:20:30.400", datum.asChars());
+  }
+
+  @Test
+  public final void testOperation() throws Exception {
+    // http://www.postgresql.org/docs/8.2/static/functions-datetime.html
+
+    // date '2001-09-28' + integer '7'	==> date '2001-10-05'
+    Datum datum = new DateDatum(2001, 9, 28);
+    Datum[] datums = new Datum[]{new Int2Datum((short) 7), new Int4Datum(7), new Int8Datum(7),
+          new Float4Datum(7.0f), new Float8Datum(7.0f)};
+
+    for (int i = 0; i < datums.length; i++) {
+      Datum result = datum.plus(datums[i]);
+      assertEquals(TajoDataTypes.Type.DATE, result.type());
+      assertEquals(new DateDatum(2001, 10, 5), result);
+    }
+
+    // date '2001-09-28' + interval '1 hour'	==> timestamp '2001-09-28 01:00:00'
+    datum = new DateDatum(2001, 9, 28);
+    Datum result = datum.plus(new IntervalDatum(60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+    assertEquals(new TimestampDatum(new DateTime(2001, 9, 28, 1, 0, 0, 0)), result);
+
+    // date '2001-09-28' + time '03:00' ==> timestamp '2001-09-28 03:00:00'
+    datum = new DateDatum(2001, 9, 28);
+    result = datum.plus(new TimeDatum(new LocalTime(3, 0)));
+    assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+    assertEquals(new TimestampDatum(new DateTime(2001, 9, 28, 3, 0, 0, 0)), result);
+
+    // interval '1 day' + interval '1 hour'	interval '1 day 01:00:00'
+    datum = new IntervalDatum(IntervalDatum.DAY_MILLIS);
+    result = datum.plus(new IntervalDatum(0, 1 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+    assertEquals("1 day 01:00:00", result.asChars());
+
+    // timestamp '2001-09-28 01:00' + interval '23 hours'	==> timestamp '2001-09-29 00:00:00'
+    datum = new TimestampDatum(new DateTime(2001, 9, 28, 1, 0, 0, 0));
+    result = datum.plus(new IntervalDatum(23 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+    assertEquals(new TimestampDatum(new DateTime(2001, 9, 29, 0, 0, 0, 0)), result);
+
+    // time '01:00' + interval '3 hours' ==> time '04:00:00'
+    datum = new TimeDatum(new LocalTime(1, 0, 0, 0));
+    result = datum.plus(new IntervalDatum(3 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.TIME, result.type());
+    assertEquals(new TimeDatum(new LocalTime(4, 0, 0, 0)), result);
+
+//    // - interval '23 hours' ==> interval '-23:00:00'
+//    // TODO Currently Interval's inverseSign() not supported
+
+    // date '2001-10-01' - date '2001-09-28' ==>	integer '3'
+    datum = new DateDatum(2001, 10, 01);
+    result = datum.minus(new DateDatum(2001, 9, 28));
+    assertEquals(TajoDataTypes.Type.INT4, result.type());
+    assertEquals(new Int4Datum(3), result);
+
+    // date '2001-10-01' - integer '7' ==>	date '2001-09-24'
+    datum = new DateDatum(2001, 10, 01);
+    for (int i = 0; i < datums.length; i++) {
+      Datum result2 = datum.minus(datums[i]);
+      assertEquals(TajoDataTypes.Type.DATE, result2.type());
+      assertEquals(new DateDatum(2001, 9, 24), result2);
+    }
+
+    // date '2001-09-28' - interval '1 hour' ==> timestamp '2001-09-27 23:00:00'
+    datum = new DateDatum(2001, 9, 28);
+    result = datum.minus(new IntervalDatum(1 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+    assertEquals(new TimestampDatum(new DateTime(2001, 9, 27, 23, 0, 0, 0)), result);
+
+    // date '2001-09-28' - interval '1 day 1 hour' ==> timestamp '2001-09-26 23:00:00'
+    datum = new DateDatum(2001, 9, 28);
+    result = datum.minus(new IntervalDatum(IntervalDatum.DAY_MILLIS + 1 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+    assertEquals(new TimestampDatum(new DateTime(2001, 9, 26, 23, 0, 0, 0)), result);
+
+    // time '05:00' - time '03:00' ==>	interval '02:00:00'
+    datum = new TimeDatum(new LocalTime(5, 0, 0, 0));
+    result = datum.minus(new TimeDatum(new LocalTime(3, 0, 0, 0)));
+    assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+    assertEquals(new IntervalDatum(2 * 60 * 60 * 1000), result);
+
+    // time '05:00' - interval '2 hours' ==>	time '03:00:00'
+    datum = new TimeDatum(new LocalTime(5, 0, 0, 0));
+    result = datum.minus(new IntervalDatum(2 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.TIME, result.type());
+    assertEquals(new TimeDatum(3, 0, 0, 0), result);
+
+    // timestamp '2001-09-28 23:00' - interval '23 hours' ==>	timestamp '2001-09-28 00:00:00'
+    datum = new TimestampDatum(new DateTime(2001, 9, 28, 23, 0, 0, 0));
+    result = datum.minus(new IntervalDatum(23 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+    assertEquals(new TimestampDatum(new DateTime(2001, 9, 28, 0, 0, 0, 0)), result);
+
+    // interval '1 day' - interval '1 hour'	==> interval '1 day -01:00:00'
+    datum = new IntervalDatum(IntervalDatum.DAY_MILLIS);
+    result = datum.minus(new IntervalDatum(1 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+    assertEquals(new IntervalDatum(23 * 60 * 60 * 1000), result);
+
+    // timestamp '2001-09-29 03:00' - timestamp '2001-09-27 12:00' ==>	interval '1 day 15:00:00'
+    datum = new TimestampDatum(new DateTime(2001, 9, 29, 3, 0, 0, 0));
+    result = datum.minus(new TimestampDatum(new DateTime(2001, 9, 27, 12, 0, 0, 0)));
+    assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+    assertEquals(new IntervalDatum(IntervalDatum.DAY_MILLIS + 15 * 60 * 60 * 1000), result);
+
+    // 900 * interval '1 second' ==> interval '00:15:00'
+    Datum[] datum900 = new Datum[]{new Int2Datum((short)900), new Int4Datum(900), new Int8Datum(900),
+        new Float4Datum(900.0f), new Float8Datum(900.0f)};
+
+    datum = new IntervalDatum(1000);
+    for (int i = 0; i < datum900.length; i++) {
+      Datum result2 = datum.multiply(datum900[i]);
+      assertEquals(TajoDataTypes.Type.INTERVAL, result2.type());
+      assertEquals(new IntervalDatum(15 * 60 * 1000), result2);
+
+      result2 = datum900[i].multiply(datum);
+      assertEquals(TajoDataTypes.Type.INTERVAL, result2.type());
+      assertEquals(new IntervalDatum(15 * 60 * 1000), result2);
+    }
+
+    // double precision '3.5' * interval '1 hour'	==> interval '03:30:00'
+    datum = new Float8Datum(3.5f);
+    result = datum.multiply(new IntervalDatum(1 * 60 * 60 * 1000));
+    assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+    assertEquals(new IntervalDatum(3 * 60 * 60 * 1000 + 30 * 60 * 1000), result);
+
+    // interval '1 hour' / double precision '1.5' ==>	interval '00:40:00'
+    datum = new IntervalDatum(1 * 60 * 60 * 1000);
+    result = datum.divide(new Float8Datum(1.5f));
+    assertEquals(TajoDataTypes.Type.INTERVAL, result.type());
+    assertEquals(new IntervalDatum(40 * 60 * 1000), result);
+
+    // timestamp '2001-08-31 01:00:00' + interval '1 mons' ==> timestamp 2001-09-30 01:00:00
+    datum = new TimestampDatum(new DateTime(2001, 8, 31, 1, 0, 0, 0));
+    result = datum.plus(new IntervalDatum(1, 0));
+    assertEquals(TajoDataTypes.Type.TIMESTAMP, result.type());
+    assertEquals(new TimestampDatum(new DateTime(2001, 9, 30, 1, 0, 0, 0)), result);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java b/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java
index 246791b..7aa01ac 100644
--- a/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java
+++ b/tajo-common/src/test/java/org/apache/tajo/datum/TestTimestampDatum.java
@@ -92,6 +92,9 @@ public class TestTimestampDatum {
   public final void testAsTextBytes() {
     Datum d = DatumFactory.createTimeStamp("1980-04-01 01:50:01");
     assertArrayEquals(d.toString().getBytes(), d.asTextBytes());
+
+    d = DatumFactory.createTimeStamp("1980-04-01 01:50:01.578");
+    assertArrayEquals(d.toString().getBytes(), d.asTextBytes());
   }
 
   @Test

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4 b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
index 7fa7973..05312ce 100644
--- a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
+++ b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLLexer.g4
@@ -335,6 +335,7 @@ NCHAR : N C H A R;
 NVARCHAR : N V A R C H A R;
 
 DATE : D A T E;
+INTERVAL: I N T E R V A L;
 TIME : T I M E;
 TIMETZ : T I M E T Z;
 TIMESTAMP : T I M E S T A M P;

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4 b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
index f6385eb..0076794 100644
--- a/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
+++ b/tajo-core/src/main/antlr4/org/apache/tajo/engine/parser/SQLParser.g4
@@ -313,6 +313,7 @@ nonreserved_keywords
   | INT4
   | INT8
   | INTEGER
+  | INTERVAL
   | NCHAR
   | NUMERIC
   | NVARCHAR
@@ -350,6 +351,7 @@ datetime_literal
   : timestamp_literal
   | time_literal
   | date_literal
+  | interval_literal
   ;
 
 time_literal
@@ -364,6 +366,10 @@ date_literal
   : DATE date_string=Character_String_Literal
   ;
 
+interval_literal
+  : INTERVAL interval_string=Character_String_Literal
+  ;
+
 boolean_literal
   : TRUE | FALSE | UNKNOWN
   ;
@@ -462,6 +468,7 @@ boolean_type
 
 datetime_type
   : DATE
+  | INTERVAL
   | TIME
   | TIME WITH TIME ZONE
   | TIMETZ

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
index d362927..f3e145a 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
@@ -82,6 +82,8 @@ public class BinaryEval extends EvalNode implements Cloneable {
       case INT8: return CatalogUtil.newSimpleDataType(Type.INT8);
       case FLOAT4: return CatalogUtil.newSimpleDataType(Type.FLOAT4);
       case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
+      case DATE: return CatalogUtil.newSimpleDataType(Type.DATE);
+      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
       }
     }
 
@@ -92,6 +94,8 @@ public class BinaryEval extends EvalNode implements Cloneable {
       case INT8: return CatalogUtil.newSimpleDataType(Type.INT8);
       case FLOAT4:
       case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
+      case DATE: return CatalogUtil.newSimpleDataType(Type.DATE);
+      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
       }
     }
 
@@ -102,6 +106,7 @@ public class BinaryEval extends EvalNode implements Cloneable {
       case INT8:
       case FLOAT4:
       case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
+      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
       }
     }
 
@@ -112,6 +117,41 @@ public class BinaryEval extends EvalNode implements Cloneable {
       case INT8:
       case FLOAT4:
       case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
+      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
+      }
+    }
+
+    case DATE: {
+      switch(right.getType()) {
+      case INT2:
+      case INT4:
+      case INT8: return CatalogUtil.newSimpleDataType(Type.DATE);
+      case INTERVAL:
+      case TIME: return CatalogUtil.newSimpleDataType(Type.TIMESTAMP);
+      case DATE: return CatalogUtil.newSimpleDataType(Type.INT4);
+      }
+    }
+
+    case TIME: {
+      switch(right.getType()) {
+      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.TIME);
+      case TIME: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
+      case DATE: return CatalogUtil.newSimpleDataType(Type.INT4);
+      }
+    }
+
+    case TIMESTAMP: {
+      switch (right.getType()) {
+      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.TIMESTAMP);
+      case TIMESTAMP: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
+      }
+    }
+
+    case INTERVAL: {
+      switch (right.getType()) {
+      case INTERVAL:
+      case FLOAT4:
+      case FLOAT8: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
       }
     }
 

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/function/datetime/ToDate.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/function/datetime/ToDate.java b/tajo-core/src/main/java/org/apache/tajo/engine/function/datetime/ToDate.java
new file mode 100644
index 0000000..ba6a020
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/function/datetime/ToDate.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.engine.function.datetime;
+
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.datum.DateDatum;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.datum.NullDatum;
+import org.apache.tajo.engine.function.GeneralFunction;
+import org.apache.tajo.engine.function.annotation.Description;
+import org.apache.tajo.engine.function.annotation.ParamTypes;
+import org.apache.tajo.storage.Tuple;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Description(
+    functionName = "to_date",
+    description = "Convert string to date. Format should be a java format string.",
+    example = "> SELECT to_date('2014-01-01', 'yyyy-MM-dd');\n"
+        + "2014-01-01",
+    returnType = TajoDataTypes.Type.DATE,
+    paramTypes = {@ParamTypes(paramTypes = {TajoDataTypes.Type.TEXT, TajoDataTypes.Type.TEXT})}
+)
+public class ToDate extends GeneralFunction {
+  private static Map<String, DateTimeFormatter> formattercCache =
+      new ConcurrentHashMap<String, DateTimeFormatter>();
+
+  public ToDate() {
+    super(new Column[]{
+        new Column("string", TajoDataTypes.Type.TEXT),
+        new Column("format", TajoDataTypes.Type.TEXT)});
+  }
+
+  @Override
+  public Datum eval(Tuple params) {
+    if(params.isNull(0) || params.isNull(1)) {
+      return NullDatum.get();
+    }
+
+    String value = params.get(0).asChars();
+    String pattern = params.get(1).asChars();
+
+    DateTimeFormatter formatter = formattercCache.get(pattern);
+    if (formatter == null) {
+      formatter = DateTimeFormat.forPattern(pattern);
+      formattercCache.put(pattern, formatter);
+    }
+
+    return new DateDatum(formatter.parseDateTime(value).toLocalDate());
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
index cb356f8..e7de8f6 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
@@ -1384,6 +1384,8 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
       return visitTime_literal(ctx.time_literal());
     } else if (checkIfExist(ctx.date_literal())) {
       return visitDate_literal(ctx.date_literal());
+    } else if (checkIfExist(ctx.interval_literal())) {
+      return visitInterval_literal(ctx.interval_literal());
     } else {
       return visitTimestamp_literal(ctx.timestamp_literal());
     }
@@ -1410,6 +1412,11 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
     return new TimestampLiteral(parseDate(datePart), parseTime(timePart));
   }
 
+  @Override public Expr visitInterval_literal(@NotNull SQLParser.Interval_literalContext ctx) {
+    String intervalStr = stripQuote(ctx.interval_string.getText());
+    return new IntervalLiteral(intervalStr);
+  }
+
   private DateValue parseDate(String datePart) {
     // e.g., 1980-04-01
     String[] parts = datePart.split("-");
@@ -1421,7 +1428,7 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
     String[] parts = timePart.split(":");
 
     TimeValue time;
-    boolean hasFractionOfSeconds = parts[2].indexOf('.') > 0;
+    boolean hasFractionOfSeconds = (parts.length > 2 && parts[2].indexOf('.') > 0);
     if (hasFractionOfSeconds) {
       String[] secondsParts = parts[2].split("\\.");
       time = new TimeValue(parts[0], parts[1], secondsParts[0]);
@@ -1429,7 +1436,9 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
         time.setSecondsFraction(secondsParts[1]);
       }
     } else {
-      time = new TimeValue(parts[0], parts[1], parts[2]);
+      time = new TimeValue(parts[0],
+          (parts.length > 1 ? parts[1] : "0"),
+          (parts.length > 2 ? parts[2] : "0"));
     }
     return time;
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
index aa94801..0ef8a26 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
@@ -105,6 +105,7 @@ public interface AlgebraVisitor<CONTEXT, RESULT> {
   RESULT visitLiteral(CONTEXT ctx, Stack<Expr> stack, LiteralValue expr) throws PlanningException;
   RESULT visitNullLiteral(CONTEXT ctx, Stack<Expr> stack, NullLiteral expr) throws PlanningException;
   RESULT visitTimestampLiteral(CONTEXT ctx, Stack<Expr> stack, TimestampLiteral expr) throws PlanningException;
+  RESULT visitIntervalLiteral(CONTEXT ctx, Stack<Expr> stack, IntervalLiteral expr) throws PlanningException;
   RESULT visitTimeLiteral(CONTEXT ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException;
   RESULT visitDateLiteral(CONTEXT ctx, Stack<Expr> stack, DateLiteral expr) throws PlanningException;
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
index b8f3311..841ea22 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
@@ -249,7 +249,9 @@ public class BaseAlgebraVisitor<CONTEXT, RESULT> implements AlgebraVisitor<CONTE
     case TimestampLiteral:
       current = visitTimestampLiteral(ctx, stack, (TimestampLiteral) expr);
       break;
-
+    case IntervalLiteral:
+      current = visitIntervalLiteral(ctx, stack, (IntervalLiteral) expr);
+      break;
 
 
     default:
@@ -728,6 +730,11 @@ public class BaseAlgebraVisitor<CONTEXT, RESULT> implements AlgebraVisitor<CONTE
   }
 
   @Override
+  public RESULT visitIntervalLiteral(CONTEXT ctx, Stack<Expr> stack, IntervalLiteral expr) throws PlanningException {
+    return null;
+  }
+
+  @Override
   public RESULT visitTimeLiteral(CONTEXT ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException {
     return null;
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
index 1b57b98..be0259b 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
@@ -550,6 +550,11 @@ public class ExprAnnotator extends BaseAlgebraVisitor<ExprAnnotator.Context, Eva
   }
 
   @Override
+  public EvalNode visitIntervalLiteral(Context ctx, Stack<Expr> stack, IntervalLiteral expr) throws PlanningException {
+    return new ConstEval(new IntervalDatum(expr.getExprStr()));
+  }
+
+  @Override
   public EvalNode visitTimeLiteral(Context ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException {
     TimeValue timeValue = expr.getTime();
     int [] times = timeToIntArray(timeValue.getHours(),

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
index 9030629..b87665a 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
@@ -188,17 +188,19 @@ class ExprNormalizer extends SimpleAlgebraVisitor<ExprNormalizer.ExprNormalizedR
     stack.push(expr);
 
     Expr param;
-    for (int i = 0; i < expr.getParams().length; i++) {
-      param = expr.getParams()[i];
-      visit(ctx, stack, param);
-
-      if (OpType.isAggregationFunction(param.getType())) {
-        String referenceName = ctx.plan.generateUniqueColumnName(param);
-        ctx.aggExprs.add(new NamedExpr(param, referenceName));
-        expr.getParams()[i] = new ColumnReferenceExpr(referenceName);
+    Expr[] paramExprs = expr.getParams();
+    if (paramExprs != null) {
+      for (int i = 0; i < paramExprs.length; i++) {
+        param = paramExprs[i];
+        visit(ctx, stack, param);
+
+        if (OpType.isAggregationFunction(param.getType())) {
+          String referenceName = ctx.plan.generateUniqueColumnName(param);
+          ctx.aggExprs.add(new NamedExpr(param, referenceName));
+          expr.getParams()[i] = new ColumnReferenceExpr(referenceName);
+        }
       }
     }
-
     stack.pop();
 
     return expr;

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java
index 551393c..4cb4776 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprsVerifier.java
@@ -141,7 +141,37 @@ public class ExprsVerifier extends BasicEvalNodeVisitor<VerificationState, EvalN
   private static void checkArithmeticOperand(VerificationState state, BinaryEval evalNode) {
     EvalNode leftExpr = evalNode.getLeftExpr();
     EvalNode rightExpr = evalNode.getRightExpr();
-    if (!(checkNumericType(leftExpr.getValueType()) && checkNumericType(rightExpr.getValueType()))) {
+
+    DataType leftDataType = leftExpr.getValueType();
+    DataType rightDataType = rightExpr.getValueType();
+
+    Type leftType = leftDataType.getType();
+    Type rightType = rightDataType.getType();
+
+    if (leftType == Type.DATE &&
+          (checkIntType(rightDataType) ||
+              rightType == Type.DATE || rightType == Type.INTERVAL || rightType == Type.TIME)) {
+      return;
+    }
+
+    if (leftType == Type.INTERVAL &&
+        (checkNumericType(rightDataType) ||
+            rightType == Type.DATE || rightType == Type.INTERVAL || rightType == Type.TIME ||
+            rightType == Type.TIMESTAMP)) {
+      return;
+    }
+
+    if (leftType == Type.TIME &&
+        (rightType == Type.DATE || rightType == Type.INTERVAL || rightType == Type.TIME)) {
+      return;
+    }
+
+    if (leftType == Type.TIMESTAMP &&
+        (rightType == Type.TIMESTAMP || rightType == Type.INTERVAL || rightType == Type.TIME)) {
+      return;
+    }
+
+    if (!(checkNumericType(leftDataType) && checkNumericType(rightDataType))) {
       state.addVerification("No operator matches the given name and argument type(s): " + evalNode.toString());
     }
   }
@@ -150,6 +180,11 @@ public class ExprsVerifier extends BasicEvalNodeVisitor<VerificationState, EvalN
     return dataType.getType() == Type.INET4 || dataType.getType() == Type.INET6;
   }
 
+  private static boolean checkIntType(DataType dataType) {
+    int typeNumber = dataType.getType().getNumber();
+    return Type.INT1.getNumber() < typeNumber && typeNumber <= Type.INT8.getNumber();
+  }
+
   private static boolean checkNumericType(DataType dataType) {
     int typeNumber = dataType.getType().getNumber();
     return Type.INT1.getNumber() < typeNumber && typeNumber <= Type.NUMERIC.getNumber();

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestIntervalType.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestIntervalType.java b/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestIntervalType.java
new file mode 100644
index 0000000..7648d1f
--- /dev/null
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/eval/TestIntervalType.java
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.tajo.engine.eval;
+
+import org.apache.tajo.datum.exception.InvalidOperationException;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.junit.Assert.fail;
+
+public class TestIntervalType extends ExprTestBase {
+  @Test
+  public void testIntervalPostgresqlCase() throws IOException {
+    // http://www.postgresql.org/docs/8.2/static/functions-datetime.html
+    testSimpleEval("select date '2001-09-28' + 7", new String[]{"2001-10-05"});
+    testSimpleEval("select date '2001-09-28' + interval '1 hour'", new String[]{"2001-09-28 01:00:00"});
+
+    testSimpleEval("select date '2001-09-28' + time '03:00'", new String[]{"2001-09-28 03:00:00"});
+    testSimpleEval("select interval '1 day' + interval '1 hour'", new String[]{"1 day 01:00:00"});
+
+    testSimpleEval("select timestamp '2001-09-28 01:00' + interval '23 hours'", new String[]{"2001-09-29 00:00:00"});
+
+    testSimpleEval("select time '01:00' + interval '3 hours'", new String[]{"04:00:00"});
+
+    testSimpleEval("select date '2001-10-01' - date '2001-09-28'", new String[]{"3"});
+    testSimpleEval("select date '2001-10-01' - 7", new String[]{"2001-09-24"});
+    testSimpleEval("select date '2001-09-28' - interval '1 hour'", new String[]{"2001-09-27 23:00:00"});
+
+    testSimpleEval("select time '05:00' - time '03:00'", new String[]{"02:00:00"});
+    testSimpleEval("select time '05:00' - interval '2 hours'", new String[]{"03:00:00"});
+    testSimpleEval("select timestamp '2001-09-28 23:00' - interval '23 hours'", new String[]{"2001-09-28 00:00:00"});
+
+    testSimpleEval("select interval '1 day' - interval '1 hour'", new String[]{"23:00:00"});
+
+    testSimpleEval("select timestamp '2001-09-29 03:00' - timestamp '2001-09-27 12:00'", new String[]{"1 day 15:00:00"});
+    testSimpleEval("select 900 * interval '1 second'", new String[]{"00:15:00"});
+    testSimpleEval("select 21 * interval '1 day'", new String[]{"21 days"});
+    testSimpleEval("select 3.5 * interval '1 hour'", new String[]{"03:30:00"});
+    testSimpleEval("select interval '1 hour' / 1.5", new String[]{"00:40:00"});
+  }
+
+  @Test
+  public void testCaseByCase() throws Exception {
+    testSimpleEval("select date '2001-08-28' + interval '10 day 1 hour'", new String[]{"2001-09-07 01:00:00"});
+    testSimpleEval("select interval '10 day 01:00:00' + date '2001-08-28'", new String[]{"2001-09-07 01:00:00"});
+    testSimpleEval("select time '10:20:30' + interval '1 day 01:00:00'", new String[]{"11:20:30"});
+    testSimpleEval("select interval '1 day 01:00:00' + time '10:20:30'", new String[]{"11:20:30"});
+    testSimpleEval("select time '10:20:30' - interval '1 day 01:00:00'", new String[]{"09:20:30"});
+
+    testSimpleEval("select (interval '1 month 20 day' + interval '50 day')", new String[]{"1 month 70 days"});
+    testSimpleEval("select date '2013-01-01' + interval '1 month 70 day'", new String[]{"2013-04-12 00:00:00"});
+    testSimpleEval("select date '2013-01-01' + (interval '1 month 20 day' + interval '50 day')", new String[]{"2013-04-12 00:00:00"});
+    testSimpleEval("select interval '1 month 70 day' + date '2013-01-01'", new String[]{"2013-04-12 00:00:00"});
+    testSimpleEval("select date '2013-01-01' - interval '1 month 70 day'", new String[]{"2012-09-22 00:00:00"});
+
+    testSimpleEval("select timestamp '2001-09-28 23:00' - interval '1 month 2 day 10:20:30'", new String[]{"2001-08-26 12:39:30"});
+    testSimpleEval("select timestamp '2001-09-28 23:00' + interval '1 month 2 day 10:20:30'", new String[]{"2001-10-31 09:20:30"});
+    testSimpleEval("select interval '1 month 2 day 10:20:30' + timestamp '2001-09-28 23:00'", new String[]{"2001-10-31 09:20:30"});
+
+
+    testSimpleEval("select interval '5 month' / 3", new String[]{"1 month 20 days"});
+
+    // Notice: Different from postgresql result(13 days 01:02:36.4992) because of double type precision.
+    testSimpleEval("select interval '1 month' / 2.3", new String[]{"13 days 01:02:36.522"});
+
+    testSimpleEval("select interval '1 month' * 2.3", new String[]{"2 months 9 days"});
+    testSimpleEval("select interval '3 year 5 month 1 hour' / 1.5", new String[]{"2 years 3 months 10 days 00:40:00"});
+  }
+
+  @Test
+  public void testWrongFormatLiteral() throws Exception {
+    try {
+      testSimpleEval("select interval '1 month 2 day 23 hours 10:20:30'", new String[]{"DUMMY"});
+      fail("hour and time format can not be used at the same time  in interval.");
+    } catch (InvalidOperationException e) {
+      //success
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java b/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java
index ac7a2b8..8641548 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/function/TestDateTimeFunctions.java
@@ -250,4 +250,10 @@ public class TestDateTimeFunctions extends ExprTestBase {
      testSimpleEval("select utc_usec_to('year' ,1274259481071200);", new String[]{1262304000000000L+""});
      testSimpleEval("select utc_usec_to('week' ,1207929480000000, 2);", new String[]{1207612800000000L+""});
   }
+
+  @Test
+  public void testToDate() throws IOException {
+    testSimpleEval("select to_date('2014-01-04', 'yyyy-MM-dd')", new String[]{"2014-01-04"});
+    testSimpleEval("select to_date('2014-01-04', 'yyyy-MM-dd') + interval '1 day'", new String[]{"2014-01-05 00:00:00"});
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java
----------------------------------------------------------------------
diff --git a/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java b/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java
index b64bfe1..33b2ff3 100644
--- a/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java
+++ b/tajo-storage/src/main/java/org/apache/tajo/storage/RowStoreUtil.java
@@ -22,6 +22,7 @@ import org.apache.tajo.catalog.Column;
 import org.apache.tajo.catalog.Schema;
 import org.apache.tajo.common.TajoDataTypes;
 import org.apache.tajo.datum.DatumFactory;
+import org.apache.tajo.datum.IntervalDatum;
 import org.apache.tajo.util.BitArray;
 
 import java.nio.ByteBuffer;
@@ -116,6 +117,12 @@ public class RowStoreUtil {
             tuple.put(i, DatumFactory.createFromInt8(type, l));
             break;
 
+        case INTERVAL:
+            int month  = bb.getInt();
+            long milliseconds  = bb.getLong();
+            tuple.put(i, new IntervalDatum(month, milliseconds));
+            break;
+
           case FLOAT4:
             float f = bb.getFloat();
             tuple.put(i, DatumFactory.createFloat4(f));
@@ -197,6 +204,11 @@ public class RowStoreUtil {
           case TIMESTAMP:
             bb.putLong(tuple.get(i).asInt8());
             break;
+          case INTERVAL:
+            IntervalDatum interval = (IntervalDatum) tuple.get(i);
+            bb.putInt(interval.getMonths());
+            bb.putLong(interval.getMilliSeconds());
+            break;
           case BLOB:
             byte [] bytes = tuple.get(i).asByteArray();
             bb.putInt(bytes.length);

http://git-wip-us.apache.org/repos/asf/tajo/blob/5bbb7e82/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java
----------------------------------------------------------------------
diff --git a/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java b/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java
index de73a3a..6113357 100644
--- a/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java
+++ b/tajo-storage/src/main/java/org/apache/tajo/storage/TextSerializerDeserializer.java
@@ -79,6 +79,7 @@ public class TextSerializerDeserializer implements SerializerDeserializer {
       case DATE:
       case TIME:
       case TIMESTAMP:
+      case INTERVAL:
         bytes = datum.asTextBytes();
         length = bytes.length;
         out.write(bytes);
@@ -158,6 +159,10 @@ public class TextSerializerDeserializer implements SerializerDeserializer {
         datum = isNull(bytes, offset, length, nullCharacters) ? NullDatum.get()
             : DatumFactory.createTimeStamp(new String(bytes, offset, length));
         break;
+      case INTERVAL:
+        datum = isNull(bytes, offset, length, nullCharacters) ? NullDatum.get()
+            : DatumFactory.createInterval(new String(bytes, offset, length));
+        break;
       case PROTOBUF: {
         if (isNull(bytes, offset, length, nullCharacters)) {
           datum = NullDatum.get();