Added calendar-duration functions and timezone related function; fixed a small bug on printing timezone string.

git-svn-id: https://asterixdb.googlecode.com/svn/branches/asterix_stabilization_temporal_functionality@780 eaa15691-b419-025a-1212-ee371bd00084
diff --git a/asterix-app/src/test/resources/runtimets/queries/temp/adjust_timezone.aql b/asterix-app/src/test/resources/runtimets/queries/temp/adjust_timezone.aql
new file mode 100644
index 0000000..d7f6cca
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/temp/adjust_timezone.aql
@@ -0,0 +1,17 @@
+/*
+ * Description      :   Check the adjust-timezone functions
+ * Expected Result  :   Success
+ * Date             :   15th Oct, 2012
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+write output to nc1:"rttest/temp_adjust_timezone.adm";
+
+let $t1 := time("20:15:10.327")
+let $dt1 := datetime("2010-10-23T01:12:13.329Z")
+let $s1 := adjust_time_for_timezone($t1, "+0800")
+let $s2 := adjust_datetime_for_timezone($dt1, "-0615")
+return { "string1" : $s1, "string2" : $s2 }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/queries/temp/calendar_duration.aql b/asterix-app/src/test/resources/runtimets/queries/temp/calendar_duration.aql
new file mode 100644
index 0000000..8c9dee4
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/temp/calendar_duration.aql
@@ -0,0 +1,28 @@
+/*
+ * Description      :   Check the calendar-duration functions
+ * Expected Result  :   Success
+ * Date             :   15th Oct, 2012
+ */
+
+drop dataverse test if exists;
+create dataverse test;
+use dataverse test;
+
+write output to nc1:"rttest/temp_calendar_duration.adm";
+
+let $t1 := datetime("1987-11-19T23:49:23.938")
+let $t2 := date("-1328-10-23")
+let $dr1 := duration("P7382DT39283M3921.329S")
+let $dr2 := duration("-PT63H398212M3219.328S")
+let $dr3 := duration("P1Y90M")
+let $dr4 := duration("-P3Y89M4089DT47382.983S")
+let $cdr1 := calendar_duration_from_datetime($t1, $dr1)
+let $cdr2 := calendar_duration_from_datetime($t1, $dr2)
+let $cdr3 := calendar_duration_from_datetime($t1, $dr3)
+let $cdr4 := calendar_duration_from_datetime($t1, $dr4)
+let $cdr5 := calendar_duration_from_date($t2, $dr1)
+let $cdr6 := calendar_duration_from_date($t2, $dr2)
+let $cdr7 := calendar_duration_from_date($t2, $dr3)
+let $cdr8 := calendar_duration_from_date($t2, $dr4)
+
+return { "cduration1":$cdr1, "cduration2":$cdr2, "cduration3":$cdr3, "cduration4":$cdr4, "cduration5":$cdr5, "cduration6":$cdr6, "cduration7":$cdr7, "cduration8":$cdr8 }
diff --git a/asterix-app/src/test/resources/runtimets/results/temp/adjust_timezone.adm b/asterix-app/src/test/resources/runtimets/results/temp/adjust_timezone.adm
new file mode 100644
index 0000000..1f80fd9
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/temp/adjust_timezone.adm
@@ -0,0 +1 @@
+{ "string1": "04:15:10.327+08:00", "string2": "2010-10-22T18:57:13.329-06:15" }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/results/temp/calendar_duration.adm b/asterix-app/src/test/resources/runtimets/results/temp/calendar_duration.adm
new file mode 100644
index 0000000..eb7d565
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/temp/calendar_duration.adm
@@ -0,0 +1 @@
+{ "cduration1": duration("P20Y3M13DT7H48M21.329S"), "cduration2": duration("-P9M6DT4H45M39.328S"), "cduration3": duration("P8Y6M"), "cduration4": duration("-P21Y7M10DT13H9M42.983S"), "cduration5": duration("P20Y3M12DT7H48M21.329S"), "cduration6": duration("-P9M5DT4H45M39.328S"), "cduration7": duration("P8Y6M"), "cduration8": duration("-P21Y7M10DT13H9M42.983S") }
\ No newline at end of file
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/ADateAndTimeParser.java b/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/ADateAndTimeParser.java
index 636b831..d8c4213 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/ADateAndTimeParser.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/ADateAndTimeParser.java
@@ -213,53 +213,111 @@
         }
 
         if (length > offset) {
-            if (charAccessor.getCharAt(offset) != 'Z') {
-                if ((charAccessor.getCharAt(offset) != '+' && charAccessor.getCharAt(offset) != '-')
-                        || (isExtendedForm && charAccessor.getCharAt(offset + 3) != ':')) {
-                    throw new Exception("Wrong timezone format: missing sign or missing colon in an extended form");
-                }
-
-                short timezoneHour = 0;
-                short timezoneMinute = 0;
-
-                for (int i = 0; i < 2; i++) {
-                    if ((charAccessor.getCharAt(offset + 1 + i) >= '0' && charAccessor.getCharAt(offset + 1 + i) <= '9')) {
-                        timezoneHour = (short) (timezoneHour * 10 + charAccessor.getCharAt(offset + 1 + i) - '0');
-                    } else {
-                        throw new Exception("Non-numeric value in timezone hour field");
-                    }
-                }
-
-                if (timezoneHour < GregorianCalendarSystem.TIMEZONE_HOUR_MIN
-                        || timezoneHour > GregorianCalendarSystem.TIMEZONE_HOUR_MAX) {
-                    throw new Exception(timeErrorMessage + ": time zone hour " + timezoneHour);
-                }
-
-                int temp_offset = (isExtendedForm) ? 1 : 0;
-
-                for (int i = 0; i < 2; i++) {
-                    if ((charAccessor.getCharAt(offset + temp_offset + 3 + i) >= '0' && charAccessor.getCharAt(offset
-                            + temp_offset + 3 + i) <= '9')) {
-                        timezoneMinute = (short) (timezoneMinute * 10
-                                + charAccessor.getCharAt(offset + temp_offset + 3 + i) - '0');
-                    } else {
-                        throw new Exception("Non-numeric value in timezone minute field");
-                    }
-                }
-
-                if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
-                        || timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
-                    throw new Exception(timeErrorMessage + ": time zone minute " + timezoneMinute);
-                }
-
-                if (charAccessor.getCharAt(offset) == '-') {
-                    timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
-                } else {
-                    timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
-                }
-            }
+            //            if (charAccessor.getCharAt(offset) != 'Z') {
+            //                if ((charAccessor.getCharAt(offset) != '+' && charAccessor.getCharAt(offset) != '-')
+            //                        || (isExtendedForm && charAccessor.getCharAt(offset + 3) != ':')) {
+            //                    throw new Exception("Wrong timezone format: missing sign or missing colon in an extended form");
+            //                }
+            //
+            //                short timezoneHour = 0;
+            //                short timezoneMinute = 0;
+            //
+            //                for (int i = 0; i < 2; i++) {
+            //                    if ((charAccessor.getCharAt(offset + 1 + i) >= '0' && charAccessor.getCharAt(offset + 1 + i) <= '9')) {
+            //                        timezoneHour = (short) (timezoneHour * 10 + charAccessor.getCharAt(offset + 1 + i) - '0');
+            //                    } else {
+            //                        throw new Exception("Non-numeric value in timezone hour field");
+            //                    }
+            //                }
+            //
+            //                if (timezoneHour < GregorianCalendarSystem.TIMEZONE_HOUR_MIN
+            //                        || timezoneHour > GregorianCalendarSystem.TIMEZONE_HOUR_MAX) {
+            //                    throw new Exception(timeErrorMessage + ": time zone hour " + timezoneHour);
+            //                }
+            //
+            //                int temp_offset = (isExtendedForm) ? 1 : 0;
+            //
+            //                for (int i = 0; i < 2; i++) {
+            //                    if ((charAccessor.getCharAt(offset + temp_offset + 3 + i) >= '0' && charAccessor.getCharAt(offset
+            //                            + temp_offset + 3 + i) <= '9')) {
+            //                        timezoneMinute = (short) (timezoneMinute * 10
+            //                                + charAccessor.getCharAt(offset + temp_offset + 3 + i) - '0');
+            //                    } else {
+            //                        throw new Exception("Non-numeric value in timezone minute field");
+            //                    }
+            //                }
+            //
+            //                if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
+            //                        || timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
+            //                    throw new Exception(timeErrorMessage + ": time zone minute " + timezoneMinute);
+            //                }
+            //
+            //                if (charAccessor.getCharAt(offset) == '-') {
+            //                    timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
+            //                } else {
+            //                    timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
+            //                }
+            //            }
+            timezone = parseTimezonePart(charAccessor, offset);
         }
 
         return gCalInstance.getChronon(hour, min, sec, millis, timezone);
     }
+
+    /**
+     * Parse the given char sequence as a time string, and return the milliseconds represented by the time.
+     * 
+     * @param charAccessor
+     * @return
+     * @throws Exception
+     */
+    public static <T> int parseTimezonePart(ICharSequenceAccessor<T> charAccessor, int offset) throws Exception {
+        int timezone = 0;
+
+        if (charAccessor.getCharAt(offset) != 'Z') {
+            if ((charAccessor.getCharAt(offset) != '+' && charAccessor.getCharAt(offset) != '-')) {
+                throw new Exception("Wrong timezone format: missing sign or missing colon for a time zone");
+            }
+
+            short timezoneHour = 0;
+            short timezoneMinute = 0;
+
+            for (int i = 0; i < 2; i++) {
+                if ((charAccessor.getCharAt(offset + 1 + i) >= '0' && charAccessor.getCharAt(offset + 1 + i) <= '9')) {
+                    timezoneHour = (short) (timezoneHour * 10 + charAccessor.getCharAt(offset + 1 + i) - '0');
+                } else {
+                    throw new Exception("Non-numeric value in timezone hour field");
+                }
+            }
+
+            if (timezoneHour < GregorianCalendarSystem.TIMEZONE_HOUR_MIN
+                    || timezoneHour > GregorianCalendarSystem.TIMEZONE_HOUR_MAX) {
+                throw new Exception(timeErrorMessage + ": time zone hour " + timezoneHour);
+            }
+
+            int temp_offset = (charAccessor.getCharAt(offset + 3) == ':') ? 1 : 0;
+
+            for (int i = 0; i < 2; i++) {
+                if ((charAccessor.getCharAt(offset + temp_offset + 3 + i) >= '0' && charAccessor.getCharAt(offset
+                        + temp_offset + 3 + i) <= '9')) {
+                    timezoneMinute = (short) (timezoneMinute * 10
+                            + charAccessor.getCharAt(offset + temp_offset + 3 + i) - '0');
+                } else {
+                    throw new Exception("Non-numeric value in timezone minute field");
+                }
+            }
+
+            if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
+                    || timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
+                throw new Exception(timeErrorMessage + ": time zone minute " + timezoneMinute);
+            }
+
+            if (charAccessor.getCharAt(offset) == '-') {
+                timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
+            } else {
+                timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
+            }
+        }
+        return timezone;
+    }
 }
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/GregorianCalendarSystem.java b/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/GregorianCalendarSystem.java
index 7b8d709..1c060c3 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/GregorianCalendarSystem.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/GregorianCalendarSystem.java
@@ -57,6 +57,7 @@
     public static final int CHRONON_OF_MINUTE = 60 * CHRONON_OF_SECOND;
     public static final int CHRONON_OF_HOUR = 60 * CHRONON_OF_MINUTE;
     public static final long CHRONON_OF_DAY = 24 * CHRONON_OF_HOUR;
+    public static final int MONTHS_IN_A_YEAR = 12;
 
     /**
      * Minimum feasible value of each field
@@ -238,8 +239,13 @@
         return chrononTime;
     }
 
+    public long adjustChrononByTimezone(long chronon, int timezone) {
+        return chronon + timezone / 4 * CHRONON_OF_HOUR + (timezone % 4) * 15 * CHRONON_OF_MINUTE;
+    }
+
     /**
-     * Get the extended string representation of the given UTC chronon time under the given time zone. Only fields before
+     * Get the extended string representation of the given UTC chronon time under the given time zone. Only fields
+     * before
      * the given field index will be returned.
      * <p/>
      * The extended string representation is like:<br/>
@@ -305,10 +311,13 @@
                 break;
         }
 
-        if (untilField.compareTo(Fields.DAY) > 0) {
+        if (timezone == 0) {
             sbder.append("Z");
         } else {
             short tzMin = (short) ((timezone % 4) * 15);
+            if (tzMin < 0) {
+                tzMin = (short) (-1 * tzMin);
+            }
             short tzHr = (short) (timezone / 4);
             sbder.append((tzHr >= 0 ? "+" : "-")).append(String.format("%02d", (tzHr < 0 ? -tzHr : tzHr))).append(":")
                     .append(String.format("%02d", tzMin));
@@ -361,10 +370,13 @@
                 break;
         }
 
-        if (untilField.compareTo(Fields.DAY) > 0) {
+        if (timezone == 0) {
             sbder.append("Z");
         } else {
             short tzMin = (short) ((timezone % 4) * 15);
+            if (tzMin < 0) {
+                tzMin = (short) (-1 * tzMin);
+            }
             short tzHr = (short) (timezone / 4);
             sbder.append((tzHr >= 0 ? "+" : "-")).append(String.format("%02d", (tzHr < 0 ? -tzHr : tzHr)))
                     .append(String.format("%02d", tzMin));
@@ -424,7 +436,7 @@
      * @param year
      * @return
      */
-    protected boolean isLeapYear(int year) {
+    public boolean isLeapYear(int year) {
         return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
     }
 
@@ -456,7 +468,8 @@
      * Get the year for the given chronon time.
      * <p/>
      * This code is directly from the Joda library BadicChronology.java.<br/>
-     * The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May 7th, 2012.
+     * The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May
+     * 7th, 2012.
      * 
      * @param chrononTime
      * @return
@@ -503,7 +516,8 @@
      * Get the month of the year for the given chronon time and the year.
      * <p/>
      * This code is directly from the Joda library BasicGJChronology.java.<br/>
-     * The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May 7th, 2012.
+     * The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May
+     * 7th, 2012 and commented by Theodoros Ioannou on July 2012.
      * <p/>
      * 
      * @param millis
@@ -567,7 +581,8 @@
      * Get the day of the given month and year for the input chronon time.
      * <p/>
      * This function is directly from Joda Library BasicChronology.java.<br/>
-     * The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May 7th, 2012.
+     * The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May
+     * 7th, 2012.
      * <p/>
      * 
      * @param millis
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java b/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
index c399354..3673e5b 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/om/functions/AsterixBuiltinFunctions.java
@@ -427,6 +427,14 @@
             "subtract_datetime", 2);
     public final static FunctionIdentifier ADD_DATETIME_DURATION = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
             "add_datetime_duration", 2);
+    public final static FunctionIdentifier CALENDAR_DURATION_FROM_DATETIME = new FunctionIdentifier(
+            FunctionConstants.ASTERIX_NS, "calendar_duration_from_datetime", 2);
+    public final static FunctionIdentifier CALENDAR_DURATION_FROM_DATE = new FunctionIdentifier(
+            FunctionConstants.ASTERIX_NS, "calendar_duration_from_date", 2);
+    public final static FunctionIdentifier ADJUST_TIME_FOR_TIMEZONE = new FunctionIdentifier(
+            FunctionConstants.ASTERIX_NS, "adjust_time_for_timezone", 2);
+    public final static FunctionIdentifier ADJUST_DATETIME_FOR_TIMEZONE = new FunctionIdentifier(
+            FunctionConstants.ASTERIX_NS, "adjust_datetime_for_timezone", 2);
 
     public static final FunctionIdentifier EQ = AlgebricksBuiltinFunctions.EQ;
     public static final FunctionIdentifier LE = AlgebricksBuiltinFunctions.LE;
@@ -702,6 +710,10 @@
         add(DATETIME_FROM_UNIX_TIME_IN_MS, OptionalADateTimeTypeComputer.INSTANCE);
         add(SUBTRACT_DATETIME, OptionalADurationTypeComputer.INSTANCE);
         add(ADD_DATETIME_DURATION, OptionalADateTimeTypeComputer.INSTANCE);
+        add(CALENDAR_DURATION_FROM_DATETIME, OptionalADurationTypeComputer.INSTANCE);
+        add(CALENDAR_DURATION_FROM_DATE, OptionalADurationTypeComputer.INSTANCE);
+        add(ADJUST_DATETIME_FOR_TIMEZONE, OptionalAStringTypeComputer.INSTANCE);
+        add(ADJUST_TIME_FOR_TIMEZONE, OptionalAStringTypeComputer.INSTANCE);
 
         String metadataFunctionLoaderClassName = "edu.uci.ics.asterix.metadata.functions.MetadataBuiltinFunctions";
         try {
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/AdjustDateTimeForTimeZoneDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/AdjustDateTimeForTimeZoneDescriptor.java
new file mode 100644
index 0000000..72b3baa
--- /dev/null
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/AdjustDateTimeForTimeZoneDescriptor.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2009-2011 by The Regents of the University of California
+ * 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 from
+ * 
+ *     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 edu.uci.ics.asterix.runtime.evaluators.functions.temporal;
+
+import java.io.DataOutput;
+
+import edu.uci.ics.asterix.common.functions.FunctionConstants;
+import edu.uci.ics.asterix.dataflow.data.nontagged.serde.ADateTimeSerializerDeserializer;
+import edu.uci.ics.asterix.formats.nontagged.AqlSerializerDeserializerProvider;
+import edu.uci.ics.asterix.om.base.ANull;
+import edu.uci.ics.asterix.om.base.temporal.ADateAndTimeParser;
+import edu.uci.ics.asterix.om.base.temporal.ByteArrayCharSequenceAccessor;
+import edu.uci.ics.asterix.om.base.temporal.GregorianCalendarSystem;
+import edu.uci.ics.asterix.om.base.temporal.GregorianCalendarSystem.Fields;
+import edu.uci.ics.asterix.om.functions.IFunctionDescriptor;
+import edu.uci.ics.asterix.om.functions.IFunctionDescriptorFactory;
+import edu.uci.ics.asterix.om.types.ATypeTag;
+import edu.uci.ics.asterix.om.types.BuiltinType;
+import edu.uci.ics.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluator;
+import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory;
+import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
+import edu.uci.ics.hyracks.data.std.api.IDataOutputProvider;
+import edu.uci.ics.hyracks.data.std.util.ArrayBackedValueStorage;
+import edu.uci.ics.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+import edu.uci.ics.hyracks.dataflow.common.data.util.StringUtils;
+
+public class AdjustDateTimeForTimeZoneDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+    private final static long serialVersionUID = 1L;
+    public final static FunctionIdentifier FID = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+            "adjust_datetime_for_timezone", 2);
+
+    // allowed input types
+    private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
+    private final static byte SER_DATETIME_TYPE_TAG = ATypeTag.DATETIME.serialize();
+    private final static byte SER_STRING_TYPE_TAG = ATypeTag.STRING.serialize();
+
+    public final static IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
+
+        @Override
+        public IFunctionDescriptor createFunctionDescriptor() {
+            return new AdjustDateTimeForTimeZoneDescriptor();
+        }
+    };
+
+    /* (non-Javadoc)
+     * @see edu.uci.ics.asterix.runtime.base.IScalarFunctionDynamicDescriptor#createEvaluatorFactory(edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory[])
+     */
+    @Override
+    public ICopyEvaluatorFactory createEvaluatorFactory(final ICopyEvaluatorFactory[] args) throws AlgebricksException {
+        return new ICopyEvaluatorFactory() {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public ICopyEvaluator createEvaluator(final IDataOutputProvider output) throws AlgebricksException {
+                return new ICopyEvaluator() {
+
+                    private DataOutput out = output.getDataOutput();
+                    private ArrayBackedValueStorage argOut0 = new ArrayBackedValueStorage();
+                    private ArrayBackedValueStorage argOut1 = new ArrayBackedValueStorage();
+                    private ICopyEvaluator eval0 = args[0].createEvaluator(argOut0);
+                    private ICopyEvaluator eval1 = args[1].createEvaluator(argOut1);
+
+                    // possible output types
+                    @SuppressWarnings("unchecked")
+                    private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
+                            .getSerializerDeserializer(BuiltinType.ANULL);
+
+                    private ByteArrayCharSequenceAccessor charAccessor = new ByteArrayCharSequenceAccessor();
+
+                    private GregorianCalendarSystem calInstance = GregorianCalendarSystem.getInstance();
+
+                    @Override
+                    public void evaluate(IFrameTupleReference tuple) throws AlgebricksException {
+                        argOut0.reset();
+                        eval0.evaluate(tuple);
+                        argOut1.reset();
+                        eval1.evaluate(tuple);
+
+                        try {
+                            if (argOut0.getByteArray()[0] == SER_NULL_TYPE_TAG
+                                    || argOut1.getByteArray()[0] == SER_NULL_TYPE_TAG) {
+                                nullSerde.serialize(ANull.NULL, out);
+                                return;
+                            }
+
+                            if (argOut0.getByteArray()[0] != SER_DATETIME_TYPE_TAG) {
+                                throw new AlgebricksException(
+                                        "Inapplicable input type for parameter 0: expecting ATime, but got: "
+                                                + argOut0.getByteArray()[0]);
+                            }
+
+                            if (argOut1.getByteArray()[0] != SER_STRING_TYPE_TAG) {
+                                throw new AlgebricksException(
+                                        "Inapplicable input type for parameter 0: expecting AString, but got: "
+                                                + argOut1.getByteArray()[0]);
+                            }
+
+                            charAccessor.reset(argOut1.getByteArray(), 3, 0);
+
+                            int timezone = ADateAndTimeParser.parseTimezonePart(charAccessor, 0);
+
+                            if (!calInstance.validateTimeZone(timezone)) {
+                                throw new AlgebricksException("Wrong format for a time zone string!");
+                            }
+
+                            long chronon = ADateTimeSerializerDeserializer.getChronon(argOut0.getByteArray(), 1);
+
+                            chronon = calInstance.adjustChrononByTimezone(chronon, timezone);
+
+                            StringBuilder sbder = new StringBuilder();
+
+                            calInstance.getExtendStringRepWithTimezoneUntilField(chronon, timezone, sbder, Fields.YEAR,
+                                    Fields.MILLISECOND);
+
+                            out.writeByte(SER_STRING_TYPE_TAG);
+                            out.writeUTF(sbder.toString());
+
+                        } catch (Exception e1) {
+                            throw new AlgebricksException(e1);
+                        }
+                    }
+                };
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * @see edu.uci.ics.asterix.om.functions.IFunctionDescriptor#getIdentifier()
+     */
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+
+}
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/AdjustTimeForTimeZoneDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/AdjustTimeForTimeZoneDescriptor.java
new file mode 100644
index 0000000..29fbe65
--- /dev/null
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/AdjustTimeForTimeZoneDescriptor.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2009-2011 by The Regents of the University of California
+ * 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 from
+ * 
+ *     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 edu.uci.ics.asterix.runtime.evaluators.functions.temporal;
+
+import java.io.DataOutput;
+
+import edu.uci.ics.asterix.common.functions.FunctionConstants;
+import edu.uci.ics.asterix.dataflow.data.nontagged.serde.ATimeSerializerDeserializer;
+import edu.uci.ics.asterix.formats.nontagged.AqlSerializerDeserializerProvider;
+import edu.uci.ics.asterix.om.base.ANull;
+import edu.uci.ics.asterix.om.base.temporal.ADateAndTimeParser;
+import edu.uci.ics.asterix.om.base.temporal.ByteArrayCharSequenceAccessor;
+import edu.uci.ics.asterix.om.base.temporal.GregorianCalendarSystem;
+import edu.uci.ics.asterix.om.base.temporal.GregorianCalendarSystem.Fields;
+import edu.uci.ics.asterix.om.functions.IFunctionDescriptor;
+import edu.uci.ics.asterix.om.functions.IFunctionDescriptorFactory;
+import edu.uci.ics.asterix.om.types.ATypeTag;
+import edu.uci.ics.asterix.om.types.BuiltinType;
+import edu.uci.ics.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluator;
+import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory;
+import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
+import edu.uci.ics.hyracks.data.std.api.IDataOutputProvider;
+import edu.uci.ics.hyracks.data.std.util.ArrayBackedValueStorage;
+import edu.uci.ics.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+import edu.uci.ics.hyracks.dataflow.common.data.util.StringUtils;
+
+public class AdjustTimeForTimeZoneDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+    private final static long serialVersionUID = 1L;
+    public final static FunctionIdentifier FID = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+            "adjust_time_for_timezone", 2);
+
+    // allowed input types
+    private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
+    private final static byte SER_TIME_TYPE_TAG = ATypeTag.TIME.serialize();
+    private final static byte SER_STRING_TYPE_TAG = ATypeTag.STRING.serialize();
+
+    public final static IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
+
+        @Override
+        public IFunctionDescriptor createFunctionDescriptor() {
+            return new AdjustTimeForTimeZoneDescriptor();
+        }
+    };
+
+    /* (non-Javadoc)
+     * @see edu.uci.ics.asterix.runtime.base.IScalarFunctionDynamicDescriptor#createEvaluatorFactory(edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory[])
+     */
+    @Override
+    public ICopyEvaluatorFactory createEvaluatorFactory(final ICopyEvaluatorFactory[] args) throws AlgebricksException {
+        return new ICopyEvaluatorFactory() {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public ICopyEvaluator createEvaluator(final IDataOutputProvider output) throws AlgebricksException {
+                return new ICopyEvaluator() {
+
+                    private DataOutput out = output.getDataOutput();
+                    private ArrayBackedValueStorage argOut0 = new ArrayBackedValueStorage();
+                    private ArrayBackedValueStorage argOut1 = new ArrayBackedValueStorage();
+                    private ICopyEvaluator eval0 = args[0].createEvaluator(argOut0);
+                    private ICopyEvaluator eval1 = args[1].createEvaluator(argOut1);
+
+                    // possible output types
+                    @SuppressWarnings("unchecked")
+                    private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
+                            .getSerializerDeserializer(BuiltinType.ANULL);
+
+                    private ByteArrayCharSequenceAccessor charAccessor = new ByteArrayCharSequenceAccessor();
+
+                    private GregorianCalendarSystem calInstance = GregorianCalendarSystem.getInstance();
+
+                    @Override
+                    public void evaluate(IFrameTupleReference tuple) throws AlgebricksException {
+                        argOut0.reset();
+                        eval0.evaluate(tuple);
+                        argOut1.reset();
+                        eval1.evaluate(tuple);
+
+                        try {
+                            if (argOut0.getByteArray()[0] == SER_NULL_TYPE_TAG
+                                    || argOut1.getByteArray()[0] == SER_NULL_TYPE_TAG) {
+                                nullSerde.serialize(ANull.NULL, out);
+                                return;
+                            }
+
+                            if (argOut0.getByteArray()[0] != SER_TIME_TYPE_TAG) {
+                                throw new AlgebricksException(
+                                        "Inapplicable input type for parameter 0: expecting ATime, but got: "
+                                                + argOut0.getByteArray()[0]);
+                            }
+
+                            if (argOut1.getByteArray()[0] != SER_STRING_TYPE_TAG) {
+                                throw new AlgebricksException(
+                                        "Inapplicable input type for parameter 0: expecting AString, but got: "
+                                                + argOut1.getByteArray()[0]);
+                            }
+
+                            charAccessor.reset(argOut1.getByteArray(), 3, 0);
+
+                            int timezone = ADateAndTimeParser.parseTimezonePart(charAccessor, 0);
+
+                            if (!calInstance.validateTimeZone(timezone)) {
+                                throw new AlgebricksException("Wrong format for a time zone string!");
+                            }
+
+                            int chronon = ATimeSerializerDeserializer.getChronon(argOut0.getByteArray(), 1);
+
+                            chronon = (int) calInstance.adjustChrononByTimezone(chronon, timezone);
+
+                            StringBuilder sbder = new StringBuilder();
+
+                            calInstance.getExtendStringRepWithTimezoneUntilField(chronon, timezone, sbder, Fields.HOUR,
+                                    Fields.MILLISECOND);
+
+                            out.writeByte(SER_STRING_TYPE_TAG);
+                            out.writeUTF(sbder.toString());
+
+                        } catch (Exception e1) {
+                            throw new AlgebricksException(e1);
+                        }
+                    }
+                };
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * @see edu.uci.ics.asterix.om.functions.IFunctionDescriptor#getIdentifier()
+     */
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+
+}
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/CalendarDuartionFromDateDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/CalendarDuartionFromDateDescriptor.java
new file mode 100644
index 0000000..b72a11f
--- /dev/null
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/CalendarDuartionFromDateDescriptor.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2009-2011 by The Regents of the University of California
+ * 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 from
+ * 
+ *     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 edu.uci.ics.asterix.runtime.evaluators.functions.temporal;
+
+import java.io.DataOutput;
+
+import edu.uci.ics.asterix.common.functions.FunctionConstants;
+import edu.uci.ics.asterix.dataflow.data.nontagged.serde.ADateSerializerDeserializer;
+import edu.uci.ics.asterix.dataflow.data.nontagged.serde.ADurationSerializerDeserializer;
+import edu.uci.ics.asterix.formats.nontagged.AqlSerializerDeserializerProvider;
+import edu.uci.ics.asterix.om.base.ADuration;
+import edu.uci.ics.asterix.om.base.AMutableDuration;
+import edu.uci.ics.asterix.om.base.ANull;
+import edu.uci.ics.asterix.om.base.temporal.DurationArithmeticOperations;
+import edu.uci.ics.asterix.om.base.temporal.GregorianCalendarSystem;
+import edu.uci.ics.asterix.om.functions.IFunctionDescriptor;
+import edu.uci.ics.asterix.om.functions.IFunctionDescriptorFactory;
+import edu.uci.ics.asterix.om.types.ATypeTag;
+import edu.uci.ics.asterix.om.types.BuiltinType;
+import edu.uci.ics.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluator;
+import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory;
+import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
+import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
+import edu.uci.ics.hyracks.data.std.api.IDataOutputProvider;
+import edu.uci.ics.hyracks.data.std.util.ArrayBackedValueStorage;
+import edu.uci.ics.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+
+public class CalendarDuartionFromDateDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+    private final static long serialVersionUID = 1L;
+    public final static FunctionIdentifier FID = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+            "calendar_duration_from_date", 2);
+
+    // allowed input types
+    private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
+    private final static byte SER_DATE_TYPE_TAG = ATypeTag.DATE.serialize();
+    private final static byte SER_DURATION_TYPE_TAG = ATypeTag.DURATION.serialize();
+
+    public final static IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
+
+        @Override
+        public IFunctionDescriptor createFunctionDescriptor() {
+            return new CalendarDuartionFromDateDescriptor();
+        }
+    };
+
+    /* (non-Javadoc)
+     * @see edu.uci.ics.asterix.runtime.base.IScalarFunctionDynamicDescriptor#createEvaluatorFactory(edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory[])
+     */
+    @Override
+    public ICopyEvaluatorFactory createEvaluatorFactory(final ICopyEvaluatorFactory[] args) throws AlgebricksException {
+        return new ICopyEvaluatorFactory() {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public ICopyEvaluator createEvaluator(final IDataOutputProvider output) throws AlgebricksException {
+
+                return new ICopyEvaluator() {
+
+                    private DataOutput out = output.getDataOutput();
+                    private ArrayBackedValueStorage argOut0 = new ArrayBackedValueStorage();
+                    private ArrayBackedValueStorage argOut1 = new ArrayBackedValueStorage();
+                    private ICopyEvaluator eval0 = args[0].createEvaluator(argOut0);
+                    private ICopyEvaluator eval1 = args[1].createEvaluator(argOut1);
+
+                    // possible output types
+                    @SuppressWarnings("unchecked")
+                    private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
+                            .getSerializerDeserializer(BuiltinType.ANULL);
+                    @SuppressWarnings("unchecked")
+                    private ISerializerDeserializer<ADuration> durationSerde = AqlSerializerDeserializerProvider.INSTANCE
+                            .getSerializerDeserializer(BuiltinType.ADURATION);
+
+                    private AMutableDuration aDuration = new AMutableDuration(0, 0);
+
+                    private GregorianCalendarSystem calInstanct = GregorianCalendarSystem.getInstance();
+
+                    @Override
+                    public void evaluate(IFrameTupleReference tuple) throws AlgebricksException {
+
+                        argOut0.reset();
+                        eval0.evaluate(tuple);
+                        argOut1.reset();
+                        eval1.evaluate(tuple);
+
+                        try {
+                            if (argOut0.getByteArray()[0] == SER_NULL_TYPE_TAG
+                                    || argOut1.getByteArray()[0] == SER_NULL_TYPE_TAG) {
+                                nullSerde.serialize(ANull.NULL, out);
+                                return;
+                            }
+
+                            if (argOut0.getByteArray()[0] != SER_DATE_TYPE_TAG) {
+                                throw new AlgebricksException(
+                                        "Inapplicable input type for parameter 0: expecting ADateTime, but got: "
+                                                + argOut0.getByteArray()[0]);
+                            }
+
+                            if (argOut1.getByteArray()[0] != SER_DURATION_TYPE_TAG) {
+                                throw new AlgebricksException(
+                                        "Inapplicable input type for parameter 1: expecting ADateTime, but got: "
+                                                + argOut1.getByteArray()[0]);
+                            }
+
+                            int yearMonthDurationInMonths = ADurationSerializerDeserializer.getYearMonth(
+                                    argOut1.getByteArray(), 1);
+                            long dayTimeDurationInMs = ADurationSerializerDeserializer.getDayTime(
+                                    argOut1.getByteArray(), 1);
+
+                            long startingTimePoint = ADateSerializerDeserializer.getChronon(argOut0.getByteArray(), 1)
+                                    * GregorianCalendarSystem.CHRONON_OF_DAY;
+
+                            long endingTimePoint = DurationArithmeticOperations.addDuration(startingTimePoint,
+                                    yearMonthDurationInMonths, dayTimeDurationInMs);
+
+                            if (startingTimePoint == endingTimePoint) {
+                                aDuration.setValue(0, 0);
+                            } else {
+
+                                boolean negative = false;
+
+                                if (endingTimePoint < startingTimePoint) {
+                                    negative = true;
+                                    // swap the starting and ending time, so that ending time is always larger than the starting time.
+                                    long tmpTime = endingTimePoint;
+                                    endingTimePoint = startingTimePoint;
+                                    startingTimePoint = tmpTime;
+                                }
+
+                                int year0 = calInstanct.getYear(startingTimePoint);
+                                int month0 = calInstanct.getMonthOfYear(startingTimePoint, year0);
+
+                                int year1 = calInstanct.getYear(endingTimePoint);
+                                int month1 = calInstanct.getMonthOfYear(endingTimePoint, year1);
+
+                                int year = year1 - year0;
+                                int month = month1 - month0;
+                                int day = calInstanct.getDayOfMonthYear(endingTimePoint, year1, month1)
+                                        - calInstanct.getDayOfMonthYear(startingTimePoint, year0, month0);
+                                int hour = calInstanct.getHourOfDay(endingTimePoint)
+                                        - calInstanct.getHourOfDay(startingTimePoint);
+                                int min = calInstanct.getMinOfHour(endingTimePoint)
+                                        - calInstanct.getMinOfHour(startingTimePoint);
+                                int sec = calInstanct.getSecOfMin(endingTimePoint)
+                                        - calInstanct.getSecOfMin(startingTimePoint);
+                                int ms = calInstanct.getMillisOfSec(endingTimePoint)
+                                        - calInstanct.getMillisOfSec(startingTimePoint);
+
+                                if (ms < 0) {
+                                    ms += GregorianCalendarSystem.CHRONON_OF_SECOND;
+                                    sec -= 1;
+                                }
+
+                                if (sec < 0) {
+                                    sec += GregorianCalendarSystem.CHRONON_OF_MINUTE
+                                            / GregorianCalendarSystem.CHRONON_OF_SECOND;
+                                    min -= 1;
+                                }
+
+                                if (min < 0) {
+                                    min += GregorianCalendarSystem.CHRONON_OF_HOUR
+                                            / GregorianCalendarSystem.CHRONON_OF_MINUTE;
+                                    hour -= 1;
+                                }
+
+                                if (hour < 0) {
+                                    hour += GregorianCalendarSystem.CHRONON_OF_DAY
+                                            / GregorianCalendarSystem.CHRONON_OF_HOUR;
+                                    day -= 1;
+                                }
+
+                                if (day < 0) {
+                                    boolean isLeapYear = calInstanct.isLeapYear(year0);
+                                    day += (isLeapYear) ? (GregorianCalendarSystem.DAYS_OF_MONTH_LEAP[month0 - 1])
+                                            : (GregorianCalendarSystem.DAYS_OF_MONTH_ORDI[month0 - 1]);
+                                    month -= 1;
+                                }
+
+                                if (month < 0) {
+                                    month += GregorianCalendarSystem.MONTHS_IN_A_YEAR;
+                                    year -= 1;
+                                }
+
+                                if (negative) {
+                                    aDuration.setValue(-1 * (year * GregorianCalendarSystem.MONTHS_IN_A_YEAR + month),
+                                            -1
+                                                    * (day * GregorianCalendarSystem.CHRONON_OF_DAY + hour
+                                                            * GregorianCalendarSystem.CHRONON_OF_HOUR + min
+                                                            * GregorianCalendarSystem.CHRONON_OF_MINUTE + sec
+                                                            * GregorianCalendarSystem.CHRONON_OF_SECOND + ms));
+                                } else {
+                                    aDuration.setValue(year * GregorianCalendarSystem.MONTHS_IN_A_YEAR + month, day
+                                            * GregorianCalendarSystem.CHRONON_OF_DAY + hour
+                                            * GregorianCalendarSystem.CHRONON_OF_HOUR + min
+                                            * GregorianCalendarSystem.CHRONON_OF_MINUTE + sec
+                                            * GregorianCalendarSystem.CHRONON_OF_SECOND + ms);
+                                }
+                            }
+
+                            durationSerde.serialize(aDuration, out);
+
+                        } catch (HyracksDataException hex) {
+                            throw new AlgebricksException(hex);
+                        }
+                    }
+                };
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * @see edu.uci.ics.asterix.om.functions.IFunctionDescriptor#getIdentifier()
+     */
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+
+}
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/CalendarDurationFromDateTimeDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/CalendarDurationFromDateTimeDescriptor.java
new file mode 100644
index 0000000..c0b00a9
--- /dev/null
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/CalendarDurationFromDateTimeDescriptor.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2009-2011 by The Regents of the University of California
+ * 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 from
+ * 
+ *     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 edu.uci.ics.asterix.runtime.evaluators.functions.temporal;
+
+import java.io.DataOutput;
+
+import edu.uci.ics.asterix.common.functions.FunctionConstants;
+import edu.uci.ics.asterix.dataflow.data.nontagged.serde.ADateTimeSerializerDeserializer;
+import edu.uci.ics.asterix.dataflow.data.nontagged.serde.ADurationSerializerDeserializer;
+import edu.uci.ics.asterix.formats.nontagged.AqlSerializerDeserializerProvider;
+import edu.uci.ics.asterix.om.base.ADuration;
+import edu.uci.ics.asterix.om.base.AMutableDuration;
+import edu.uci.ics.asterix.om.base.ANull;
+import edu.uci.ics.asterix.om.base.temporal.DurationArithmeticOperations;
+import edu.uci.ics.asterix.om.base.temporal.GregorianCalendarSystem;
+import edu.uci.ics.asterix.om.functions.IFunctionDescriptor;
+import edu.uci.ics.asterix.om.functions.IFunctionDescriptorFactory;
+import edu.uci.ics.asterix.om.types.ATypeTag;
+import edu.uci.ics.asterix.om.types.BuiltinType;
+import edu.uci.ics.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import edu.uci.ics.hyracks.algebricks.common.exceptions.AlgebricksException;
+import edu.uci.ics.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluator;
+import edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory;
+import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
+import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
+import edu.uci.ics.hyracks.data.std.api.IDataOutputProvider;
+import edu.uci.ics.hyracks.data.std.util.ArrayBackedValueStorage;
+import edu.uci.ics.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+
+/**
+ * This function converts a given duration into a "human-readable" duration containing both year-month and day-time
+ * duration parts, by re-organizing values between the duration fields from the given reference time point.<p/>
+ * 
+ * The basic algorithm for this convert is simple: <br/>
+ * 1. Calculate the time point by adding the given duration to the given time point;<br/>
+ * 2. Calculate the differences by fields between two different time points;<br/>
+ * 3. Re-format the duration into a human-readable one.<p/>
+ * 
+ * In the implementation, we always do the subtraction from the later time point, resulting a positive duration always. Then
+ * the sign of the duration is decided by the input duration.<p/>
+ * 
+ * 
+ */
+public class CalendarDurationFromDateTimeDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+    private final static long serialVersionUID = 1L;
+    public final static FunctionIdentifier FID = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+            "calendar_duration_from_datetime", 2);
+
+    // allowed input types
+    private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
+    private final static byte SER_DATETIME_TYPE_TAG = ATypeTag.DATETIME.serialize();
+    private final static byte SER_DURATION_TYPE_TAG = ATypeTag.DURATION.serialize();
+
+    public final static IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
+
+        @Override
+        public IFunctionDescriptor createFunctionDescriptor() {
+            return new CalendarDurationFromDateTimeDescriptor();
+        }
+    };
+
+    /* (non-Javadoc)
+     * @see edu.uci.ics.asterix.runtime.base.IScalarFunctionDynamicDescriptor#createEvaluatorFactory(edu.uci.ics.hyracks.algebricks.runtime.base.ICopyEvaluatorFactory[])
+     */
+    @Override
+    public ICopyEvaluatorFactory createEvaluatorFactory(final ICopyEvaluatorFactory[] args) throws AlgebricksException {
+        return new ICopyEvaluatorFactory() {
+
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public ICopyEvaluator createEvaluator(final IDataOutputProvider output) throws AlgebricksException {
+                return new ICopyEvaluator() {
+
+                    private DataOutput out = output.getDataOutput();
+                    private ArrayBackedValueStorage argOut0 = new ArrayBackedValueStorage();
+                    private ArrayBackedValueStorage argOut1 = new ArrayBackedValueStorage();
+                    private ICopyEvaluator eval0 = args[0].createEvaluator(argOut0);
+                    private ICopyEvaluator eval1 = args[1].createEvaluator(argOut1);
+
+                    // possible output types
+                    @SuppressWarnings("unchecked")
+                    private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
+                            .getSerializerDeserializer(BuiltinType.ANULL);
+                    @SuppressWarnings("unchecked")
+                    private ISerializerDeserializer<ADuration> durationSerde = AqlSerializerDeserializerProvider.INSTANCE
+                            .getSerializerDeserializer(BuiltinType.ADURATION);
+
+                    private AMutableDuration aDuration = new AMutableDuration(0, 0);
+
+                    private GregorianCalendarSystem calInstanct = GregorianCalendarSystem.getInstance();
+
+                    @Override
+                    public void evaluate(IFrameTupleReference tuple) throws AlgebricksException {
+                        argOut0.reset();
+                        eval0.evaluate(tuple);
+                        argOut1.reset();
+                        eval1.evaluate(tuple);
+
+                        try {
+                            if (argOut0.getByteArray()[0] == SER_NULL_TYPE_TAG
+                                    || argOut1.getByteArray()[0] == SER_NULL_TYPE_TAG) {
+                                nullSerde.serialize(ANull.NULL, out);
+                                return;
+                            }
+
+                            if (argOut0.getByteArray()[0] != SER_DATETIME_TYPE_TAG) {
+                                throw new AlgebricksException(
+                                        "Inapplicable input type for parameter 0: expecting ADateTime, but got: "
+                                                + argOut0.getByteArray()[0]);
+                            }
+
+                            if (argOut1.getByteArray()[0] != SER_DURATION_TYPE_TAG) {
+                                throw new AlgebricksException(
+                                        "Inapplicable input type for parameter 1: expecting ADateTime, but got: "
+                                                + argOut1.getByteArray()[0]);
+                            }
+
+                            int yearMonthDurationInMonths = ADurationSerializerDeserializer.getYearMonth(
+                                    argOut1.getByteArray(), 1);
+                            long dayTimeDurationInMs = ADurationSerializerDeserializer.getDayTime(
+                                    argOut1.getByteArray(), 1);
+
+                            long startingTimePoint = ADateTimeSerializerDeserializer.getChronon(argOut0.getByteArray(),
+                                    1);
+
+                            long endingTimePoint = DurationArithmeticOperations.addDuration(startingTimePoint,
+                                    yearMonthDurationInMonths, dayTimeDurationInMs);
+
+                            if (startingTimePoint == endingTimePoint) {
+                                aDuration.setValue(0, 0);
+                            } else {
+
+                                boolean negative = false;
+
+                                if (endingTimePoint < startingTimePoint) {
+                                    negative = true;
+                                    // swap the starting and ending time, so that ending time is always larger than the starting time.
+                                    long tmpTime = endingTimePoint;
+                                    endingTimePoint = startingTimePoint;
+                                    startingTimePoint = tmpTime;
+                                }
+
+                                int year0 = calInstanct.getYear(startingTimePoint);
+                                int month0 = calInstanct.getMonthOfYear(startingTimePoint, year0);
+
+                                int year1 = calInstanct.getYear(endingTimePoint);
+                                int month1 = calInstanct.getMonthOfYear(endingTimePoint, year1);
+
+                                int year = year1 - year0;
+                                int month = month1 - month0;
+                                int day = calInstanct.getDayOfMonthYear(endingTimePoint, year1, month1)
+                                        - calInstanct.getDayOfMonthYear(startingTimePoint, year0, month0);
+                                int hour = calInstanct.getHourOfDay(endingTimePoint)
+                                        - calInstanct.getHourOfDay(startingTimePoint);
+                                int min = calInstanct.getMinOfHour(endingTimePoint)
+                                        - calInstanct.getMinOfHour(startingTimePoint);
+                                int sec = calInstanct.getSecOfMin(endingTimePoint)
+                                        - calInstanct.getSecOfMin(startingTimePoint);
+                                int ms = calInstanct.getMillisOfSec(endingTimePoint)
+                                        - calInstanct.getMillisOfSec(startingTimePoint);
+
+                                if (ms < 0) {
+                                    ms += GregorianCalendarSystem.CHRONON_OF_SECOND;
+                                    sec -= 1;
+                                }
+
+                                if (sec < 0) {
+                                    sec += GregorianCalendarSystem.CHRONON_OF_MINUTE
+                                            / GregorianCalendarSystem.CHRONON_OF_SECOND;
+                                    min -= 1;
+                                }
+
+                                if (min < 0) {
+                                    min += GregorianCalendarSystem.CHRONON_OF_HOUR
+                                            / GregorianCalendarSystem.CHRONON_OF_MINUTE;
+                                    hour -= 1;
+                                }
+
+                                if (hour < 0) {
+                                    hour += GregorianCalendarSystem.CHRONON_OF_DAY
+                                            / GregorianCalendarSystem.CHRONON_OF_HOUR;
+                                    day -= 1;
+                                }
+
+                                if (day < 0) {
+                                    boolean isLeapYear = calInstanct.isLeapYear(year0);
+                                    day += (isLeapYear) ? (GregorianCalendarSystem.DAYS_OF_MONTH_LEAP[month0 - 1])
+                                            : (GregorianCalendarSystem.DAYS_OF_MONTH_ORDI[month0 - 1]);
+                                    month -= 1;
+                                }
+
+                                if (month < 0) {
+                                    month += GregorianCalendarSystem.MONTHS_IN_A_YEAR;
+                                    year -= 1;
+                                }
+
+                                if (negative) {
+                                    aDuration.setValue(-1 * (year * GregorianCalendarSystem.MONTHS_IN_A_YEAR + month),
+                                            -1
+                                                    * (day * GregorianCalendarSystem.CHRONON_OF_DAY + hour
+                                                            * GregorianCalendarSystem.CHRONON_OF_HOUR + min
+                                                            * GregorianCalendarSystem.CHRONON_OF_MINUTE + sec
+                                                            * GregorianCalendarSystem.CHRONON_OF_SECOND + ms));
+                                } else {
+                                    aDuration.setValue(year * GregorianCalendarSystem.MONTHS_IN_A_YEAR + month, day
+                                            * GregorianCalendarSystem.CHRONON_OF_DAY + hour
+                                            * GregorianCalendarSystem.CHRONON_OF_HOUR + min
+                                            * GregorianCalendarSystem.CHRONON_OF_MINUTE + sec
+                                            * GregorianCalendarSystem.CHRONON_OF_SECOND + ms);
+                                }
+                            }
+
+                            durationSerde.serialize(aDuration, out);
+
+                        } catch (HyracksDataException hex) {
+                            throw new AlgebricksException(hex);
+                        }
+                    }
+                };
+            }
+        };
+    }
+
+    /* (non-Javadoc)
+     * @see edu.uci.ics.asterix.om.functions.IFunctionDescriptor#getIdentifier()
+     */
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return FID;
+    }
+
+}
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/formats/NonTaggedDataFormat.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/formats/NonTaggedDataFormat.java
index 85abde8..03ed3ae 100644
--- a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/formats/NonTaggedDataFormat.java
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/formats/NonTaggedDataFormat.java
@@ -165,6 +165,10 @@
 import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.AddDateDurationDescriptor;
 import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.AddDatetimeDurationDescriptor;
 import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.AddTimeDurationDescriptor;
+import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.AdjustDateTimeForTimeZoneDescriptor;
+import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.AdjustTimeForTimeZoneDescriptor;
+import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.CalendarDuartionFromDateDescriptor;
+import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.CalendarDurationFromDateTimeDescriptor;
 import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.DateFromDatetimeDescriptor;
 import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.DateFromUnixTimeInDaysDescriptor;
 import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.DatetimeFromDateAndTimeDescriptor;
@@ -412,6 +416,10 @@
         temp.add(DatetimeFromDateAndTimeDescriptor.FACTORY);
         temp.add(SubtractDatetimeDescriptor.FACTORY);
         temp.add(AddDatetimeDurationDescriptor.FACTORY);
+        temp.add(CalendarDurationFromDateTimeDescriptor.FACTORY);
+        temp.add(CalendarDuartionFromDateDescriptor.FACTORY);
+        temp.add(AdjustDateTimeForTimeZoneDescriptor.FACTORY);
+        temp.add(AdjustTimeForTimeZoneDescriptor.FACTORY);
 
         IFunctionManager mgr = new FunctionManagerImpl();
         for (IFunctionDescriptorFactory fdFactory : temp) {