checkpoint: added format-based parser; updated the internal
representation of timezone field.
diff --git a/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.1.ddl.aql b/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.1.ddl.aql
new file mode 100644
index 0000000..b899dc2
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.1.ddl.aql
@@ -0,0 +1,7 @@
+/**
+ * Test case for parsing temporal strings with format strings
+ * Expected Result: Success
+ **/
+
+drop dataverse test if exists;
+create dataverse test;
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.2.update.aql b/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.2.update.aql
new file mode 100644
index 0000000..ec16dd3
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.2.update.aql
@@ -0,0 +1,4 @@
+/**
+ * Test case for parsing temporal strings with format strings
+ * Expected Result: Success
+ **/
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.3.query.aql b/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.3.query.aql
new file mode 100644
index 0000000..16c55dc
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/queries/temporal/parse_01/parse_01.3.query.aql
@@ -0,0 +1,10 @@
+/**
+ * Test case for parsing temporal strings with format strings
+ * Expected Result: Success
+ **/
+
+use dataverse test;
+
+{ "date1": parse-date("2013-8-23", "YY-M-D"), "date2": parse-date("Aug 12 -12", "MMM D Y"), "date3": parse-date("-1234-01-01", "YYYY-MM-DD"), "date4": parse-date("09/11/-1980", "D/M/Y"),
+ "time1": parse-time("8:23:49", "h:m:s"), "time2": parse-time("8.19.23:32", "h.m.s:nn"), "time3": parse-time("08.19.23:32 pm", "h.m.s:nn a"), "time4": parse-time("6:30:40.948 pm PST", "h:mm:ss.nnn a z"),
+ "datetime1": parse-datetime("Dec 30 -1203 3:48:27 PM", "MMM DD YYYY h:m:s a"), "datetime2": parse-datetime("12/30/-1203 03:48:27.392 PM Asia/Shanghai", "MM/DD/YYY hh:mm:ss.nnn a z"), "datetime3": parse-datetime("1723-12-03T23:59:23.392Z", "YYYY-MM-DDThh:mm:ss.nnnz"), "datetime4": parse-datetime("1723-12-03T23:59:23.392-04:00", "YYYY-MM-DDThh:mm:ss.nnnz") }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/results/temporal/parse_01/parse_01.1.adm b/asterix-app/src/test/resources/runtimets/results/temporal/parse_01/parse_01.1.adm
new file mode 100644
index 0000000..67c3941
--- /dev/null
+++ b/asterix-app/src/test/resources/runtimets/results/temporal/parse_01/parse_01.1.adm
@@ -0,0 +1 @@
+{ "date1": date("2013-08-23"), "date2": date("-0012-08-12"), "date3": date("-1234-01-01"), "date4": date("-1980-11-09"), "time1": time("08:23:49.000Z"), "time2": time("08:19:23.320Z"), "time3": time("20:19:23.320Z"), "time4": time("10:30:40.948Z"), "datetime1": datetime("-1203-12-30T15:48:27.000Z"), "datetime2": datetime("-1203-12-30T23:48:27.392Z"), "datetime3": datetime("1723-12-03T23:59:23.392Z"), "datetime4": datetime("1723-12-04T03:59:23.392Z") }
\ No newline at end of file
diff --git a/asterix-app/src/test/resources/runtimets/testsuite.xml b/asterix-app/src/test/resources/runtimets/testsuite.xml
index 67ca419..fda9c09 100644
--- a/asterix-app/src/test/resources/runtimets/testsuite.xml
+++ b/asterix-app/src/test/resources/runtimets/testsuite.xml
@@ -4349,6 +4349,11 @@
</test-group>
<test-group name="temporal">
<test-case FilePath="temporal">
+ <compilation-unit name="parse_01">
+ <output-dir compare="Text">parse_01</output-dir>
+ </compilation-unit>
+ </test-case>
+ <test-case FilePath="temporal">
<compilation-unit name="day_of_week_01">
<output-dir compare="Text">day_of_week_01</output-dir>
</compilation-unit>
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/ATimeParserFactory.java b/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/ATimeParserFactory.java
index 2200ea3..3a0a80a 100644
--- a/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/ATimeParserFactory.java
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/ATimeParserFactory.java
@@ -181,11 +181,6 @@
}
}
- if (timezoneHour < GregorianCalendarSystem.TIMEZONE_HOUR_MIN
- || timezoneHour > GregorianCalendarSystem.TIMEZONE_HOUR_MAX) {
- throw new HyracksDataException(timeErrorMessage + ": time zone hour " + timezoneHour);
- }
-
int temp_offset = (timeString.charAt(start + 3) == ':') ? 1 : 0;
for (int i = 0; i < 2; i++) {
@@ -197,15 +192,11 @@
}
}
- if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
- || timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
- throw new HyracksDataException(timeErrorMessage + ": time zone minute " + timezoneMinute);
- }
+ timezone = (int) (timezoneHour * GregorianCalendarSystem.CHRONON_OF_HOUR + timezoneMinute
+ * GregorianCalendarSystem.CHRONON_OF_MINUTE);
- if (timeString.charAt(start) == '-') {
- timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
- } else {
- timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
+ if (timeString.charAt(start) == '+') {
+ timezone *= -1;
}
}
return timezone;
@@ -345,11 +336,6 @@
}
}
- if (timezoneHour < GregorianCalendarSystem.TIMEZONE_HOUR_MIN
- || timezoneHour > GregorianCalendarSystem.TIMEZONE_HOUR_MAX) {
- throw new HyracksDataException(timeErrorMessage + ": time zone hour " + timezoneHour);
- }
-
int temp_offset = (timeString[start + 3] == ':') ? 1 : 0;
for (int i = 0; i < 2; i++) {
@@ -360,15 +346,11 @@
}
}
- if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
- || timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
- throw new HyracksDataException(timeErrorMessage + ": time zone minute " + timezoneMinute);
- }
+ timezone = (int) (timezoneHour * GregorianCalendarSystem.CHRONON_OF_HOUR + timezoneMinute
+ * GregorianCalendarSystem.CHRONON_OF_MINUTE);
- if (timeString[start] == '-') {
- timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
- } else {
- timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
+ if (timeString[start] == '+') {
+ timezone *= -1;
}
}
return timezone;
@@ -508,11 +490,6 @@
}
}
- if (timezoneHour < GregorianCalendarSystem.TIMEZONE_HOUR_MIN
- || timezoneHour > GregorianCalendarSystem.TIMEZONE_HOUR_MAX) {
- throw new HyracksDataException(timeErrorMessage + ": time zone hour " + timezoneHour);
- }
-
int temp_offset = (timeString[start + 3] == ':') ? 1 : 0;
for (int i = 0; i < 2; i++) {
@@ -523,15 +500,11 @@
}
}
- if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
- || timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
- throw new HyracksDataException(timeErrorMessage + ": time zone minute " + timezoneMinute);
- }
+ timezone = (int) (timezoneHour * GregorianCalendarSystem.CHRONON_OF_HOUR + timezoneMinute
+ * GregorianCalendarSystem.CHRONON_OF_MINUTE);
- if (timeString[start] == '-') {
- timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
- } else {
- timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
+ if (timeString[start] == '+') {
+ timezone *= -1;
}
}
return timezone;
diff --git a/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/DateTimeFormatUtils.java b/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/DateTimeFormatUtils.java
new file mode 100644
index 0000000..569db14
--- /dev/null
+++ b/asterix-om/src/main/java/edu/uci/ics/asterix/om/base/temporal/DateTimeFormatUtils.java
@@ -0,0 +1,856 @@
+/*
+ * Copyright 2009-2013 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.om.base.temporal;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.TimeZone;
+
+import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
+
+/**
+ * {@link DateTimeFormatUtils} provides the utility methods to parse and print a date/time/datetime
+ * value based on the given format string.
+ */
+public class DateTimeFormatUtils {
+
+ private final GregorianCalendarSystem CAL = GregorianCalendarSystem.getInstance();
+
+ // For time
+ private final char HOUR_CHAR = 'h';
+ private final char MINUTE_CHAR = 'm';
+ private final char SECOND_CHAR = 's';
+ private final char MILLISECOND_CHAR = 'n';
+ private final char AMPM_CHAR = 'a';
+ private final char TIMEZONE_CHAR = 'z';
+
+ private final int MAX_HOUR_CHARS = 2;
+ private final int MAX_MINUTE_CHARS = 2;
+ private final int MAX_SECOND_CHARS = 2;
+ private final int MAX_MILLISECOND_CHARS = 3;
+ private final int MAX_AMPM_CHARS = 1;
+ private final int MAX_TIMEZONE_CHARS = 1;
+
+ private enum DateTimeProcessState {
+ INIT,
+ YEAR,
+ MONTH,
+ DAY,
+ HOUR,
+ MINUTE,
+ SECOND,
+ MILLISECOND,
+ AMPM,
+ TIMEZONE,
+ SEPARATOR
+ }
+
+ // For date
+ private final char YEAR_CHAR = 'Y';
+ private final char MONTH_CHAR = 'M';
+ private final char DAY_CHAR = 'D';
+
+ private final int MAX_YEAR_CHARS = 4;
+ private final int MAX_MONTH_CHARS = 3;
+ private final int MAX_DAY_CHARS = 2;
+
+ private final byte[][] MONTH_NAMES = new byte[][] { "jan".getBytes(), "feb".getBytes(), "mar".getBytes(),
+ "apr".getBytes(), "may".getBytes(), "jun".getBytes(), "jul".getBytes(), "aug".getBytes(), "sep".getBytes(),
+ "oct".getBytes(), "nov".getBytes(), "dec".getBytes() };
+
+ private final byte[] UTC_BYTEARRAY = "utc".getBytes();
+ private final byte[] GMT_BYTEARRAY = "gmt".getBytes();
+
+ private final byte[] AM_BYTEARRAY = "am".getBytes();
+ private final byte[] PM_BYTEARRAY = "pm".getBytes();
+
+ // Separators, for both time and date
+ private final char HYPHEN_CHAR = '-';
+ private final char COLON_CHAR = ':';
+ private final char SOLIDUS_CHAR = '/';
+ private final char PERIOD_CHAR = '.';
+ private final char COMMA_CHAR = ',';
+ private final char T_CHAR = 'T';
+
+ private final int MS_PER_MINUTE = 60 * 1000;
+ private final int MS_PER_HOUR = 60 * MS_PER_MINUTE;
+
+ private final byte TO_LOWER_OFFSET = 'A' - 'a';
+
+ private final String[] TZ_IDS = TimeZone.getAvailableIDs();
+ private final byte[][] TIMEZONE_IDS = new byte[TZ_IDS.length][];
+ {
+ Arrays.sort(TZ_IDS);
+ for (int i = 0; i < TIMEZONE_IDS.length; i++) {
+ TIMEZONE_IDS[i] = TZ_IDS[i].getBytes();
+ }
+ }
+
+ private final int[] TIMEZONE_OFFSETS = new int[TIMEZONE_IDS.length];
+ {
+ for (int i = 0; i < TIMEZONE_IDS.length; i++) {
+ TIMEZONE_OFFSETS[i] = TimeZone.getTimeZone(TZ_IDS[i]).getRawOffset();
+ }
+ }
+
+ private static class DateTimeFormatUtilsHolder {
+ private static final DateTimeFormatUtils INSTANCE = new DateTimeFormatUtils();
+ }
+
+ public static DateTimeFormatUtils getInstance() {
+ return DateTimeFormatUtilsHolder.INSTANCE;
+ }
+
+ private DateTimeFormatUtils() {
+ }
+
+ private int parseFormatField(byte[] format, int formatStart, int formatLength, int formatPointer, char formatChar,
+ int maxAllowedFormatCharCopied) {
+
+ int formatCharCopies = 0;
+
+ formatPointer++;
+ formatCharCopies++;
+ while (formatPointer < formatLength && format[formatStart + formatPointer] == formatChar) {
+ formatPointer++;
+ formatCharCopies++;
+ }
+ if (formatCharCopies > maxAllowedFormatCharCopied) {
+ throw new IllegalStateException("The format string for " + formatChar
+ + " is too long: expected no more than " + maxAllowedFormatCharCopied + " but got "
+ + formatCharCopies);
+ }
+
+ return formatCharCopies;
+ }
+
+ public enum DateTimeParseMode {
+ DATE_ONLY,
+ TIME_ONLY,
+ DATETIME
+ }
+
+ private boolean byteArrayEqualToString(byte[] barray, int start, int length, byte[] str) {
+ boolean equal = true;
+ if (length == str.length) {
+ for (int i = 0; i < length; i++) {
+ if (toLower(barray[start + i]) != str[i]) {
+ equal = false;
+ }
+ }
+ } else {
+ equal = false;
+ }
+ return equal;
+ }
+
+ private Comparator<byte[]> byteArrayComparator = new Comparator<byte[]>() {
+ @Override
+ public int compare(byte[] o1, byte[] o2) {
+ int i = 0;
+ for (; i < o1.length && i < o2.length; i++) {
+ if (o1[i] != o2[i]) {
+ return o1[i] - o2[i];
+ }
+ }
+ if (i < o1.length) {
+ return -1;
+ } else if (i < o2.length) {
+ return 1;
+ }
+ return 0;
+ }
+ };
+
+ private int monthIDSearch(byte[] barray, int start, int length) {
+ for (int i = 0; i < MONTH_NAMES.length; i++) {
+ if (byteArrayEqualToString(barray, start, length, MONTH_NAMES[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private int binaryTimezoneIDSearch(byte[] barray, int start, int length) {
+ return Arrays.binarySearch(TIMEZONE_IDS, 0, TIMEZONE_IDS.length,
+ Arrays.copyOfRange(barray, start, start + length), byteArrayComparator);
+ }
+
+ private int indexOf(byte[] barray, int start, int length, char c) {
+ int i = 0;
+ for (; i < length; i++) {
+ if (barray[start + i] == c) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private void append(Appendable appender, byte[] bs) throws IOException {
+ for (int i = 0; i < bs.length; i++) {
+ appender.append((char) bs[i]);
+ }
+ }
+
+ private byte toLower(byte b) {
+ if (b >= 'A' && b <= 'Z') {
+ return (byte) (b - TO_LOWER_OFFSET);
+ }
+ return b;
+ }
+
+ public long parseDateTime(byte[] data, int dataStart, int dataLength, byte[] format, int formatStart,
+ int formatLength, DateTimeParseMode parseMode) throws HyracksDataException {
+ int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, ms = 0, timezone = 0;
+
+ boolean negativeYear = false;
+ int formatCharCopies = 0;
+
+ int dataStringPointer = 0, formatPointer = 0;
+
+ byte separatorChar = '\0';
+
+ DateTimeProcessState processState = DateTimeProcessState.INIT;
+
+ int pointerMove = 0;
+
+ while (dataStringPointer < dataLength && formatPointer < formatLength) {
+ formatCharCopies = 0;
+ switch (format[formatStart + formatPointer]) {
+ case YEAR_CHAR:
+ processState = DateTimeProcessState.YEAR;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, YEAR_CHAR,
+ MAX_YEAR_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case MONTH_CHAR:
+ processState = DateTimeProcessState.MONTH;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, MONTH_CHAR,
+ MAX_MONTH_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case DAY_CHAR:
+ processState = DateTimeProcessState.DAY;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, DAY_CHAR,
+ MAX_DAY_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case HOUR_CHAR:
+ processState = DateTimeProcessState.HOUR;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, HOUR_CHAR,
+ MAX_HOUR_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case MINUTE_CHAR:
+ processState = DateTimeProcessState.MINUTE;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, MINUTE_CHAR,
+ MAX_MINUTE_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case SECOND_CHAR:
+ processState = DateTimeProcessState.SECOND;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, SECOND_CHAR,
+ MAX_SECOND_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case MILLISECOND_CHAR:
+ processState = DateTimeProcessState.MILLISECOND;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, MILLISECOND_CHAR,
+ MAX_MILLISECOND_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+
+ case AMPM_CHAR:
+ processState = DateTimeProcessState.AMPM;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, AMPM_CHAR,
+ MAX_AMPM_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+
+ case TIMEZONE_CHAR:
+ processState = DateTimeProcessState.TIMEZONE;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, TIMEZONE_CHAR,
+ MAX_TIMEZONE_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case ' ':
+ case HYPHEN_CHAR:
+ case COLON_CHAR:
+ case SOLIDUS_CHAR:
+ case PERIOD_CHAR:
+ case COMMA_CHAR:
+ case T_CHAR:
+ // separator
+ separatorChar = format[formatStart + formatPointer];
+ processState = DateTimeProcessState.SEPARATOR;
+ formatPointer++;
+ formatCharCopies++;
+ while (formatPointer < formatLength && format[formatStart + formatPointer] == separatorChar) {
+ formatPointer++;
+ formatCharCopies++;
+ }
+ break;
+
+ default:
+ throw new HyracksDataException("Unexpected date format string at " + (formatStart + formatPointer)
+ + ": " + format[formatStart + formatPointer]);
+ }
+
+ // check whether the process state is valid for the parse mode
+
+ switch (processState) {
+ case YEAR:
+ case MONTH:
+ case DAY:
+ if (parseMode == DateTimeParseMode.TIME_ONLY) {
+ throw new HyracksDataException("Unexpected date format string when parsing a time value");
+ }
+ break;
+ case HOUR:
+ case MINUTE:
+ case SECOND:
+ case MILLISECOND:
+ case AMPM:
+ case TIMEZONE:
+ if (parseMode == DateTimeParseMode.DATE_ONLY) {
+ throw new HyracksDataException("Unexpected time format string when parsing a date value");
+ }
+ break;
+ default:
+ // do nothing
+ }
+
+ switch (processState) {
+ case INIT:
+ break;
+ case YEAR:
+ if (dataStringPointer < dataLength && data[dataStart + dataStringPointer] == HYPHEN_CHAR) {
+ negativeYear = true;
+ dataStringPointer++;
+ }
+ case DAY:
+ int maxAllowedFormatCharCopies = (processState == DateTimeProcessState.YEAR) ? 4 : 2;
+ int parsedValue = 0;
+
+ int processedFieldsCount = 0;
+ for (int i = 0; i < formatCharCopies; i++) {
+ if (data[dataStart + dataStringPointer] < '0' || data[dataStart + dataStringPointer] > '9') {
+ throw new HyracksDataException("Unexpected char for year field at "
+ + (dataStart + dataStringPointer) + ": " + data[dataStart + dataStringPointer]);
+ }
+ parsedValue = parsedValue * 10 + (data[dataStart + dataStringPointer] - '0');
+ dataStringPointer++;
+ processedFieldsCount++;
+ }
+ // for more digits
+ while (processedFieldsCount < maxAllowedFormatCharCopies && dataStringPointer < dataLength
+ && data[dataStart + dataStringPointer] >= '0' && data[dataStart + dataStringPointer] <= '9') {
+ parsedValue = parsedValue * 10 + (data[dataStart + dataStringPointer] - '0');
+ dataStringPointer++;
+ processedFieldsCount++;
+ }
+ if (processState == DateTimeProcessState.YEAR) {
+ year = parsedValue;
+ if (negativeYear) {
+ year *= -1;
+ }
+ } else {
+ day = parsedValue;
+ }
+ break;
+ case MONTH:
+ if (formatCharCopies == 3) {
+ // the month is in the text format
+ int monthNameMatch = monthIDSearch(data, dataStart + dataStringPointer, 3);
+ if (monthNameMatch >= 0) {
+ month = monthNameMatch + 1;
+ dataStringPointer += 3;
+ } else {
+ throw new HyracksDataException("Unrecognizable month string "
+ + data[dataStart + dataStringPointer] + " "
+ + data[dataStart + dataStringPointer + 1] + " "
+ + data[dataStart + dataStringPointer + 2]);
+ }
+ } else {
+ int processedMonthFieldsCount = 0;
+ for (int i = 0; i < formatCharCopies; i++) {
+ if (data[dataStart + dataStringPointer] < '0' || data[dataStart + dataStringPointer] > '9') {
+ throw new HyracksDataException("Unexpected char for month field at "
+ + (dataStart + dataStringPointer) + ": " + data[dataStart + dataStringPointer]);
+ }
+ month = month * 10 + (data[dataStart + dataStringPointer] - '0');
+ dataStringPointer++;
+ if (processedMonthFieldsCount++ > 2) {
+ throw new HyracksDataException("Unexpected char for month field at "
+ + (dataStart + dataStringPointer) + ": " + data[dataStart + dataStringPointer]);
+ }
+ }
+ // if there are more than 2 digits for the day string
+ while (processedMonthFieldsCount < 2 && dataStringPointer < dataLength
+ && data[dataStart + dataStringPointer] >= '0'
+ && data[dataStart + dataStringPointer] <= '9') {
+ month = month * 10 + (data[dataStart + dataStringPointer] - '0');
+ dataStringPointer++;
+ processedMonthFieldsCount++;
+ }
+ }
+ break;
+ case HOUR:
+ case MINUTE:
+ case SECOND:
+ case MILLISECOND:
+ int processFieldsCount = 0;
+ int expectedMaxCount = (processState == DateTimeProcessState.MILLISECOND) ? 3 : 2;
+ parsedValue = 0;
+ for (int i = 0; i < formatCharCopies; i++) {
+ if (data[dataStart + dataStringPointer] < '0' || data[dataStart + dataStringPointer] > '9') {
+ throw new HyracksDataException("Unexpected char for " + processState.name() + " field at "
+ + (dataStart + dataStringPointer) + ": " + data[dataStart + dataStringPointer]);
+ }
+ parsedValue = parsedValue * 10 + (data[dataStart + dataStringPointer] - '0');
+ dataStringPointer++;
+ if (processFieldsCount++ > expectedMaxCount) {
+ throw new HyracksDataException("Unexpected char for " + processState.name() + " field at "
+ + dataStringPointer + ": " + data[dataStart + dataStringPointer]);
+ }
+ }
+ // if there are more than formatCharCopies digits for the hour string
+ while (processFieldsCount < expectedMaxCount && dataStringPointer < dataLength
+ && data[dataStart + dataStringPointer] >= '0' && data[dataStart + dataStringPointer] <= '9') {
+ parsedValue = parsedValue * 10 + (data[dataStart + dataStringPointer] - '0');
+ dataStringPointer++;
+ processFieldsCount++;
+ }
+ if (processState == DateTimeProcessState.HOUR) {
+ hour = parsedValue;
+ } else if (processState == DateTimeProcessState.MINUTE) {
+ min = parsedValue;
+ } else if (processState == DateTimeProcessState.SECOND) {
+ sec = parsedValue;
+ } else if (processState == DateTimeProcessState.MILLISECOND) {
+ ms = parsedValue;
+ for (int i = processFieldsCount; i < 3; i++) {
+ ms *= 10;
+ }
+ }
+ break;
+ case TIMEZONE:
+ if (data[dataStart + dataStringPointer] == 'Z'
+ && ((dataStringPointer + 1 >= dataLength) || (data[dataStart + dataStringPointer + 1] < 'A'
+ && data[dataStart + dataStringPointer + 1] > 'Z'
+ && data[dataStart + dataStringPointer + 1] < 'a' && data[dataStart
+ + dataStringPointer + 1] > 'z'))) {
+ // UTC as Z
+ timezone = 0;
+ } else if ((data[dataStart + dataStringPointer] == '+' || data[dataStart + dataStringPointer] == '-')
+ || (dataStringPointer + 3 < dataLength && (data[dataStart + dataStringPointer + 3] == '+' || data[dataStart
+ + dataStringPointer + 3] == '-'))) {
+ // UTC+ or GMT+ format
+ if (dataStringPointer + 3 < dataLength
+ && (byteArrayEqualToString(data, dataStart + dataStringPointer, 3, UTC_BYTEARRAY) || byteArrayEqualToString(
+ data, dataStart + dataStringPointer, 3, GMT_BYTEARRAY))) {
+ dataStringPointer += 3;
+ }
+ // parse timezone as +zz:zz or +zzzz
+ boolean negativeTimeZone = false;
+ if (data[dataStart + dataStringPointer] == '-') {
+ negativeTimeZone = true;
+ dataStringPointer++;
+ } else if (data[dataStart + dataStringPointer] == '+') {
+ dataStringPointer++;
+ } else {
+ throw new HyracksDataException(
+ "Incorrect timezone hour field: expecting sign + or - but got: "
+ + data[dataStart + dataStringPointer]);
+ }
+ // timezone hours
+ for (int i = 0; i < 2; i++) {
+ if (data[dataStart + dataStringPointer + i] >= '0'
+ && data[dataStart + dataStringPointer + i] <= '9') {
+ timezone += (data[dataStart + dataStringPointer + i] - '0') * MS_PER_HOUR;
+ } else {
+ throw new HyracksDataException("Unexpected character for timezone hour field at "
+ + (dataStart + dataStringPointer) + ": " + data[dataStart + dataStringPointer]);
+ }
+ }
+ dataStringPointer += 2;
+ // skip the ":" separator
+ if (data[dataStart + dataStringPointer] == ':') {
+ dataStringPointer++;
+ }
+ // timezone minutes
+ for (int i = 0; i < 2; i++) {
+ if (data[dataStart + dataStringPointer + i] >= '0'
+ && data[dataStart + dataStringPointer + i] <= '9') {
+ timezone += (data[dataStart + dataStringPointer + i] - '0') * MS_PER_MINUTE;
+ } else {
+ throw new HyracksDataException("Unexpected character for timezone minute field at "
+ + (dataStart + dataStringPointer) + ": " + data[dataStart + dataStringPointer]);
+ }
+ }
+ dataStringPointer += 2;
+ if (!negativeTimeZone) {
+ timezone *= -1;
+ }
+ } else {
+ // do lookup from the zoneinfor database
+ int timezoneEndField = dataStringPointer;
+ while (timezoneEndField < dataLength
+ && ((data[dataStart + timezoneEndField] >= '0' && data[dataStart + timezoneEndField] <= '9')
+ || (data[dataStart + timezoneEndField] >= 'a' && data[dataStart
+ + timezoneEndField] <= 'z')
+ || (data[dataStart + timezoneEndField] >= 'A' && data[dataStart
+ + timezoneEndField] <= 'Z')
+ || data[dataStart + timezoneEndField] == '/' || data[dataStart
+ + timezoneEndField] == '_')) {
+ timezoneEndField++;
+ }
+ int searchIdx = binaryTimezoneIDSearch(data, dataStart + dataStringPointer, timezoneEndField
+ - dataStringPointer);
+ if (searchIdx >= 0) {
+ timezone = TIMEZONE_OFFSETS[searchIdx];
+ } else {
+ throw new HyracksDataException("Unexpected timezone string: "
+ + new String(Arrays.copyOfRange(data, dataStart + dataStringPointer, dataStart
+ + dataStringPointer)));
+ }
+ dataStringPointer = timezoneEndField;
+ }
+ break;
+ case AMPM:
+ if (dataStringPointer + 1 < dataLength) {
+ if (hour > 12 || hour <= 0) {
+ throw new IllegalStateException("Hour " + hour + " cannot be a time for AM.");
+ }
+ if (byteArrayEqualToString(data, dataStart + dataStringPointer, 2, AM_BYTEARRAY)) {
+ // do nothing
+ } else if (byteArrayEqualToString(data, dataStart + dataStringPointer, 2, PM_BYTEARRAY)) {
+ hour += 12;
+ if (hour == 24) {
+ hour = 0;
+ }
+ } else {
+ throw new HyracksDataException("Unexpected string for AM/PM marker "
+ + new String(Arrays.copyOfRange(data, dataStart + dataStringPointer, dataStart
+ + dataStringPointer + 2)));
+ }
+ dataStringPointer += 2;
+ } else {
+ throw new HyracksDataException("Cannot find valid AM/PM marker.");
+ }
+ break;
+ case SEPARATOR:
+ if (separatorChar == '\0') {
+ throw new HyracksDataException("Incorrect separator char in date string as "
+ + data[dataStart + dataStringPointer]);
+ }
+ for (int i = 0; i < formatCharCopies; i++) {
+ if (data[dataStart + dataStringPointer] != separatorChar) {
+ throw new HyracksDataException("Expecting separator " + separatorChar + " but got "
+ + data[dataStart + dataStringPointer]);
+ }
+ dataStringPointer++;
+ }
+ break;
+ default:
+ throw new HyracksDataException("Unexpected time format information when parsing a date value");
+ }
+ }
+
+ if (parseMode == DateTimeParseMode.TIME_ONLY) {
+ return CAL.getChronon(hour, min, sec, ms, timezone);
+ }
+ return CAL.getChronon(year, month, day, hour, min, sec, ms, timezone);
+ }
+
+ public void printDateTime(long chronon, int timezone, byte[] format, int formatStart, int formatLength,
+ Appendable appender, DateTimeParseMode parseMode) throws HyracksDataException {
+ int year = CAL.getYear(chronon);
+ int month = CAL.getMonthOfYear(chronon, year);
+ int day = CAL.getDayOfMonthYear(chronon, year, month);
+ int hour = CAL.getHourOfDay(chronon);
+ int min = CAL.getMinOfHour(chronon);
+ int sec = CAL.getSecOfMin(chronon);
+ int ms = CAL.getMillisOfSec(chronon);
+
+ int formatCharCopies = 0;
+
+ int formatPointer = 0;
+
+ byte separatorChar = '\0';
+
+ DateTimeProcessState processState = DateTimeProcessState.INIT;
+
+ int pointerMove = 0;
+
+ boolean usePM = false;
+ if (indexOf(format, formatStart, formatLength, 'a') >= 0) {
+ if (hour >= 12) {
+ usePM = true;
+ hour -= 12;
+ }
+ if (hour == 0) {
+ hour = 12;
+ }
+ }
+
+ while (formatPointer < formatLength) {
+
+ formatCharCopies = 0;
+
+ switch (format[formatStart + formatPointer]) {
+ case YEAR_CHAR:
+ processState = DateTimeProcessState.YEAR;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, YEAR_CHAR,
+ MAX_YEAR_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case MONTH_CHAR:
+ processState = DateTimeProcessState.MONTH;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, MONTH_CHAR,
+ MAX_MONTH_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case DAY_CHAR:
+ processState = DateTimeProcessState.DAY;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, DAY_CHAR,
+ MAX_DAY_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case HOUR_CHAR:
+ processState = DateTimeProcessState.HOUR;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, HOUR_CHAR,
+ MAX_HOUR_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case MINUTE_CHAR:
+ processState = DateTimeProcessState.MINUTE;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, MINUTE_CHAR,
+ MAX_MINUTE_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case SECOND_CHAR:
+ processState = DateTimeProcessState.SECOND;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, SECOND_CHAR,
+ MAX_SECOND_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case MILLISECOND_CHAR:
+ processState = DateTimeProcessState.MILLISECOND;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, MILLISECOND_CHAR,
+ MAX_MILLISECOND_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+
+ case AMPM_CHAR:
+ processState = DateTimeProcessState.AMPM;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, AMPM_CHAR,
+ MAX_AMPM_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+
+ case TIMEZONE_CHAR:
+ processState = DateTimeProcessState.TIMEZONE;
+ pointerMove = parseFormatField(format, formatStart, formatLength, formatPointer, TIMEZONE_CHAR,
+ MAX_TIMEZONE_CHARS);
+ formatPointer += pointerMove;
+ formatCharCopies += pointerMove;
+ break;
+ case ' ':
+ case HYPHEN_CHAR:
+ case COLON_CHAR:
+ case SOLIDUS_CHAR:
+ case PERIOD_CHAR:
+ case COMMA_CHAR:
+ case T_CHAR:
+ // separator
+ separatorChar = format[formatPointer];
+ processState = DateTimeProcessState.SEPARATOR;
+ formatPointer++;
+ formatCharCopies++;
+ while (formatPointer < formatLength && format[formatPointer] == separatorChar) {
+ formatPointer++;
+ formatCharCopies++;
+ }
+ break;
+
+ default:
+ throw new HyracksDataException("Unexpected format string at " + (formatStart + formatPointer)
+ + ": " + format[formatStart + formatPointer]);
+ }
+
+ // check whether the process state is valid for the parse mode
+
+ switch (processState) {
+ case YEAR:
+ case MONTH:
+ case DAY:
+ if (parseMode == DateTimeParseMode.TIME_ONLY) {
+ throw new HyracksDataException("Unexpected date format string when parsing a time value");
+ }
+ break;
+ case HOUR:
+ case MINUTE:
+ case SECOND:
+ case MILLISECOND:
+ case AMPM:
+ case TIMEZONE:
+ if (parseMode == DateTimeParseMode.DATE_ONLY) {
+ throw new HyracksDataException("Unexpected time format string when parsing a date value");
+ }
+ break;
+ default:
+ // do nothing
+ }
+
+ try {
+ switch (processState) {
+ case INIT:
+ break;
+ case YEAR:
+ if (year < 0) {
+ appender.append('-');
+ year *= -1;
+ }
+ case MONTH:
+ if (processState == DateTimeProcessState.MONTH && formatCharCopies == 3) {
+ append(appender, MONTH_NAMES[month - 1]);
+ break;
+ }
+ case DAY:
+ int val = 0;
+ if (processState == DateTimeProcessState.YEAR) {
+ val = year;
+ } else if (processState == DateTimeProcessState.MONTH) {
+ val = month;
+ } else {
+ val = day;
+ }
+ int valFieldCount = (int) Math.ceil(Math.log10(val));
+ if (val == 1 || val == 0) {
+ valFieldCount = 1;
+ }
+ for (int i = 0; i < formatCharCopies - valFieldCount; i++) {
+ appender.append('0');
+ }
+ appender.append(String.valueOf(val));
+ break;
+ case HOUR:
+ case MINUTE:
+ case SECOND:
+ val = 0;
+ if (processState == DateTimeProcessState.HOUR) {
+ val = hour;
+ } else if (processState == DateTimeProcessState.MINUTE) {
+ val = min;
+ } else if (processState == DateTimeProcessState.SECOND) {
+ val = sec;
+ }
+
+ if (val < 10) {
+ for (int i = 0; i < formatCharCopies - 1; i++) {
+ appender.append('0');
+ }
+ }
+ appender.append(String.valueOf(val));
+ break;
+ case MILLISECOND:
+ int msFieldCount = (int) Math.ceil(Math.log10(ms));
+ for (int i = 0; i < 3 - msFieldCount; i++) {
+ appender.append('0');
+ }
+ if (formatCharCopies < 3) {
+
+ if (formatCharCopies == 1) {
+ if (ms % 100 == 0) {
+ // the tailing two zeros can be removed
+ ms = ms / 100;
+ } else if (ms % 10 == 0) {
+ // the tailing one zero can be removed
+ ms = ms / 10;
+ }
+ } else {
+ if (ms % 10 == 0) {
+ // the tailing one zero can be removed
+ ms = ms / 10;
+ }
+ }
+
+ }
+ appender.append(String.valueOf(ms));
+ break;
+ case TIMEZONE:
+ if (timezone == 0) {
+ appender.append('Z');
+ break;
+ }
+ if (timezone < 0) {
+ appender.append('-');
+ timezone *= -1;
+ }
+ int timezoneField = timezone / MS_PER_HOUR;
+ if (timezoneField < 10) {
+ appender.append('0');
+ }
+ appender.append(String.valueOf(timezoneField));
+ timezoneField = timezone % MS_PER_HOUR / MS_PER_MINUTE;
+ if (timezoneField < 10) {
+ appender.append('0');
+ }
+ appender.append(String.valueOf(timezoneField));
+ break;
+ case AMPM:
+ if (usePM) {
+ appender.append("PM");
+ } else {
+ appender.append("AM");
+ }
+ break;
+ case SEPARATOR:
+ if (separatorChar == '\0') {
+ throw new HyracksDataException(
+ "Incorrect separator: separator char is not initialized properly!");
+ }
+ for (int i = 0; i < formatCharCopies; i++) {
+ appender.append(String.valueOf(separatorChar));
+ }
+ break;
+ default:
+ throw new HyracksDataException("Unexpected time state when printing a date value");
+ }
+ } catch (IOException ex) {
+ throw new HyracksDataException(ex);
+ }
+ }
+ }
+}
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 5d27fb0..3be0858 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
@@ -164,15 +164,8 @@
* @return
*/
public boolean validateTimeZone(int timezone) {
- short tzMin = (short) ((timezone % 4) * 15);
- if (tzMin < -60 || tzMin >= 60) {
- return false;
- }
-
- short tzHr = (short) (timezone / 4);
-
- if (tzHr < -12 && tzHr > 14) {
+ if (timezone < -12 * CHRONON_OF_DAY || timezone > 14 * CHRONON_OF_DAY) {
return false;
}
@@ -211,8 +204,8 @@
*/
public long getChronon(int year, int month, int day, int hour, int min, int sec, int millis, int timezone) {
// Added milliseconds for all fields but month and day
- long chrononTime = chrononizeBeginningOfYear(year) + (hour - timezone / 4) * CHRONON_OF_HOUR
- + (min - (timezone % 4) * 15) * CHRONON_OF_MINUTE + sec * CHRONON_OF_SECOND + millis;
+ long chrononTime = chrononizeBeginningOfYear(year) + hour * CHRONON_OF_HOUR + min * CHRONON_OF_MINUTE + sec
+ * CHRONON_OF_SECOND + millis + timezone;
// Added milliseconds for days of the month.
chrononTime += (day - 1 + DAYS_SINCE_MONTH_BEGIN_ORDI[month - 1]) * CHRONON_OF_DAY;
@@ -237,13 +230,13 @@
*/
public int getChronon(int hour, int min, int sec, int millis, int timezone) {
// Added milliseconds for all fields but month and day
- long chrononTime = (hour - timezone / 4) * CHRONON_OF_HOUR + (min - (timezone % 4) * 15) * CHRONON_OF_MINUTE
- + sec * CHRONON_OF_SECOND + millis;
- return (int)chrononTime;
+ long chrononTime = hour * CHRONON_OF_HOUR + min * CHRONON_OF_MINUTE + sec * CHRONON_OF_SECOND + millis
+ + timezone;
+ return (int) chrononTime;
}
public long adjustChrononByTimezone(long chronon, int timezone) {
- return chronon + timezone / 4 * CHRONON_OF_HOUR + (timezone % 4) * 15 * CHRONON_OF_MINUTE;
+ return chronon - timezone;
}
public static int getChrononInDays(long chronon) {
@@ -330,12 +323,12 @@
if (timezone == 0) {
sbder.append("Z");
} else {
- short tzMin = (short) ((timezone % 4) * 15);
+ int tzMin = (int) (timezone % CHRONON_OF_HOUR / CHRONON_OF_MINUTE);
if (tzMin < 0) {
tzMin = (short) (-1 * tzMin);
}
- short tzHr = (short) (timezone / 4);
- sbder.append((tzHr >= 0 ? "+" : "-")).append(String.format("%02d", (tzHr < 0 ? -tzHr : tzHr)))
+ int tzHr = (int) (timezone / CHRONON_OF_HOUR);
+ sbder.append((tzHr >= 0 ? "-" : "+")).append(String.format("%02d", (tzHr < 0 ? -tzHr : tzHr)))
.append(":").append(String.format("%02d", tzMin));
}
}
@@ -391,12 +384,12 @@
if (timezone == 0) {
sbder.append("Z");
} else {
- short tzMin = (short) ((timezone % 4) * 15);
+ int tzMin = (int) (timezone % CHRONON_OF_HOUR / CHRONON_OF_MINUTE);
if (tzMin < 0) {
tzMin = (short) (-1 * tzMin);
}
- short tzHr = (short) (timezone / 4);
- sbder.append((tzHr >= 0 ? "+" : "-")).append(String.format("%02d", (tzHr < 0 ? -tzHr : tzHr)))
+ int tzHr = (int) (timezone / CHRONON_OF_HOUR);
+ sbder.append((tzHr >= 0 ? "-" : "+")).append(String.format("%02d", (tzHr < 0 ? -tzHr : tzHr)))
.append(String.format("%02d", tzMin));
}
}
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 41cf3c1..3c8a0c4 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
@@ -542,6 +542,18 @@
FunctionConstants.ASTERIX_NS, "adjust-datetime-for-timezone", 2);
public final static FunctionIdentifier DAY_OF_WEEK = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
"day-of-week");
+ public final static FunctionIdentifier PARSE_DATE = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+ "parse-date", 2);
+ public final static FunctionIdentifier PARSE_TIME = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+ "parse-time", 2);
+ public final static FunctionIdentifier PARSE_DATETIME = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+ "parse-datetime", 2);
+ public final static FunctionIdentifier PRINT_DATE = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+ "print-date", 2);
+ public final static FunctionIdentifier PRINT_TIME = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+ "print-time", 2);
+ public final static FunctionIdentifier PRINT_DATETIME = new FunctionIdentifier(FunctionConstants.ASTERIX_NS,
+ "print-datetime", 2);
public final static FunctionIdentifier GET_POINT_X_COORDINATE_ACCESSOR = new FunctionIdentifier(
FunctionConstants.ASTERIX_NS, "get-x", 1);
@@ -858,6 +870,12 @@
addFunction(GET_YEAR_MONTH_DURATION, OptionalAYearMonthDurationTypeComputer.INSTANCE);
addFunction(INTERVAL_BIN, OptionalAIntervalTypeComputer.INSTANCE);
addFunction(DAY_OF_WEEK, OptionalAInt32TypeComputer.INSTANCE);
+ addFunction(PARSE_DATE, OptionalADateTypeComputer.INSTANCE);
+ addFunction(PARSE_TIME, OptionalATimeTypeComputer.INSTANCE);
+ addFunction(PARSE_DATETIME, OptionalADateTimeTypeComputer.INSTANCE);
+ addFunction(PRINT_DATE, OptionalAStringTypeComputer.INSTANCE);
+ addFunction(PRINT_TIME, OptionalAStringTypeComputer.INSTANCE);
+ addFunction(PRINT_DATETIME, OptionalAStringTypeComputer.INSTANCE);
// interval constructors
addFunction(INTERVAL_CONSTRUCTOR_DATE, OptionalAIntervalTypeComputer.INSTANCE);
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseDateDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseDateDescriptor.java
new file mode 100644
index 0000000..4a4dff4
--- /dev/null
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseDateDescriptor.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2009-2013 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.formats.nontagged.AqlSerializerDeserializerProvider;
+import edu.uci.ics.asterix.om.base.ADate;
+import edu.uci.ics.asterix.om.base.AMutableDate;
+import edu.uci.ics.asterix.om.base.ANull;
+import edu.uci.ics.asterix.om.base.temporal.DateTimeFormatUtils;
+import edu.uci.ics.asterix.om.base.temporal.GregorianCalendarSystem;
+import edu.uci.ics.asterix.om.base.temporal.DateTimeFormatUtils.DateTimeParseMode;
+import edu.uci.ics.asterix.om.functions.AsterixBuiltinFunctions;
+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.om.types.EnumDeserializer;
+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 ParseDateDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+ private static final long serialVersionUID = 1L;
+ public final static FunctionIdentifier FID = AsterixBuiltinFunctions.PARSE_DATE;
+
+ private final static byte SER_STRING_TYPE_TAG = ATypeTag.STRING.serialize();
+ private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
+ private final static DateTimeFormatUtils DT_UTILS = DateTimeFormatUtils.getInstance();
+
+ public final static IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
+
+ @Override
+ public IFunctionDescriptor createFunctionDescriptor() {
+ return new ParseDateDescriptor();
+ }
+ };
+
+ @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);
+
+ @SuppressWarnings("unchecked")
+ private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
+ .getSerializerDeserializer(BuiltinType.ANULL);
+ @SuppressWarnings("unchecked")
+ private ISerializerDeserializer<ADate> dateSerde = AqlSerializerDeserializerProvider.INSTANCE
+ .getSerializerDeserializer(BuiltinType.ADATE);
+
+ private AMutableDate aDate = new AMutableDate(0);
+
+ @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_STRING_TYPE_TAG
+ || argOut1.getByteArray()[0] != SER_STRING_TYPE_TAG) {
+ throw new AlgebricksException(getIdentifier().getName()
+ + ": expects two strings but got ("
+ + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(argOut0.getByteArray()[0])
+ + ", "
+ + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(argOut1.getByteArray()[0])
+ + ")");
+ }
+ int length0 = (argOut0.getByteArray()[1] & 0xff << 8)
+ + (argOut0.getByteArray()[2] & 0xff << 0);
+ int length1 = (argOut1.getByteArray()[1] & 0xff << 8)
+ + (argOut1.getByteArray()[2] & 0xff << 0);
+ long chronon = DT_UTILS.parseDateTime(argOut0.getByteArray(), 3, length0,
+ argOut1.getByteArray(), 3, length1, DateTimeParseMode.DATE_ONLY);
+
+ aDate.setValue((int) (chronon / GregorianCalendarSystem.CHRONON_OF_DAY));
+ dateSerde.serialize(aDate, out);
+
+ } catch (HyracksDataException ex) {
+ throw new AlgebricksException(ex);
+ }
+ }
+ };
+ }
+
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see edu.uci.ics.asterix.om.functions.AbstractFunctionDescriptor#getIdentifier()
+ */
+ @Override
+ public FunctionIdentifier getIdentifier() {
+ return FID;
+ }
+
+}
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseDateTimeDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseDateTimeDescriptor.java
new file mode 100644
index 0000000..f679839
--- /dev/null
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseDateTimeDescriptor.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2009-2013 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.formats.nontagged.AqlSerializerDeserializerProvider;
+import edu.uci.ics.asterix.om.base.ADateTime;
+import edu.uci.ics.asterix.om.base.AMutableDateTime;
+import edu.uci.ics.asterix.om.base.ANull;
+import edu.uci.ics.asterix.om.base.temporal.DateTimeFormatUtils;
+import edu.uci.ics.asterix.om.base.temporal.DateTimeFormatUtils.DateTimeParseMode;
+import edu.uci.ics.asterix.om.functions.AsterixBuiltinFunctions;
+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.om.types.EnumDeserializer;
+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 ParseDateTimeDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+ private static final long serialVersionUID = 1L;
+ public final static FunctionIdentifier FID = AsterixBuiltinFunctions.PARSE_DATETIME;
+
+ private final static byte SER_STRING_TYPE_TAG = ATypeTag.STRING.serialize();
+ private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
+ private final static DateTimeFormatUtils DT_UTILS = DateTimeFormatUtils.getInstance();
+
+ public final static IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
+
+ @Override
+ public IFunctionDescriptor createFunctionDescriptor() {
+ return new ParseDateTimeDescriptor();
+ }
+ };
+
+ @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);
+
+ @SuppressWarnings("unchecked")
+ private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
+ .getSerializerDeserializer(BuiltinType.ANULL);
+ @SuppressWarnings("unchecked")
+ private ISerializerDeserializer<ADateTime> datetimeSerde = AqlSerializerDeserializerProvider.INSTANCE
+ .getSerializerDeserializer(BuiltinType.ADATETIME);
+
+ private AMutableDateTime aDateTime = new AMutableDateTime(0);
+
+ @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_STRING_TYPE_TAG
+ || argOut1.getByteArray()[0] != SER_STRING_TYPE_TAG) {
+ throw new AlgebricksException(getIdentifier().getName()
+ + ": expects two strings but got ("
+ + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(argOut0.getByteArray()[0])
+ + ", "
+ + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(argOut1.getByteArray()[0])
+ + ")");
+ }
+ int length0 = (argOut0.getByteArray()[1] & 0xff << 8)
+ + (argOut0.getByteArray()[2] & 0xff << 0);
+ int length1 = (argOut1.getByteArray()[1] & 0xff << 8)
+ + (argOut1.getByteArray()[2] & 0xff << 0);
+ long chronon = DT_UTILS.parseDateTime(argOut0.getByteArray(), 3, length0,
+ argOut1.getByteArray(), 3, length1, DateTimeParseMode.DATETIME);
+
+ aDateTime.setValue(chronon);
+ datetimeSerde.serialize(aDateTime, out);
+
+ } catch (HyracksDataException ex) {
+ throw new AlgebricksException(ex);
+ }
+ }
+ };
+ }
+
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see edu.uci.ics.asterix.om.functions.AbstractFunctionDescriptor#getIdentifier()
+ */
+ @Override
+ public FunctionIdentifier getIdentifier() {
+ return FID;
+ }
+
+}
diff --git a/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseTimeDescriptor.java b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseTimeDescriptor.java
new file mode 100644
index 0000000..b92db54
--- /dev/null
+++ b/asterix-runtime/src/main/java/edu/uci/ics/asterix/runtime/evaluators/functions/temporal/ParseTimeDescriptor.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2009-2013 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.formats.nontagged.AqlSerializerDeserializerProvider;
+import edu.uci.ics.asterix.om.base.AMutableTime;
+import edu.uci.ics.asterix.om.base.ANull;
+import edu.uci.ics.asterix.om.base.ATime;
+import edu.uci.ics.asterix.om.base.temporal.DateTimeFormatUtils;
+import edu.uci.ics.asterix.om.base.temporal.DateTimeFormatUtils.DateTimeParseMode;
+import edu.uci.ics.asterix.om.functions.AsterixBuiltinFunctions;
+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.om.types.EnumDeserializer;
+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 ParseTimeDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+
+ private static final long serialVersionUID = 1L;
+ public final static FunctionIdentifier FID = AsterixBuiltinFunctions.PARSE_TIME;
+
+ private final static byte SER_STRING_TYPE_TAG = ATypeTag.STRING.serialize();
+ private final static byte SER_NULL_TYPE_TAG = ATypeTag.NULL.serialize();
+ private final static DateTimeFormatUtils DT_UTILS = DateTimeFormatUtils.getInstance();
+
+ public final static IFunctionDescriptorFactory FACTORY = new IFunctionDescriptorFactory() {
+
+ @Override
+ public IFunctionDescriptor createFunctionDescriptor() {
+ return new ParseTimeDescriptor();
+ }
+ };
+
+ @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);
+
+ @SuppressWarnings("unchecked")
+ private ISerializerDeserializer<ANull> nullSerde = AqlSerializerDeserializerProvider.INSTANCE
+ .getSerializerDeserializer(BuiltinType.ANULL);
+ @SuppressWarnings("unchecked")
+ private ISerializerDeserializer<ATime> timeSerde = AqlSerializerDeserializerProvider.INSTANCE
+ .getSerializerDeserializer(BuiltinType.ATIME);
+
+ private AMutableTime aTime = new AMutableTime(0);
+
+ @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_STRING_TYPE_TAG
+ || argOut1.getByteArray()[0] != SER_STRING_TYPE_TAG) {
+ throw new AlgebricksException(getIdentifier().getName()
+ + ": expects two strings but got ("
+ + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(argOut0.getByteArray()[0])
+ + ", "
+ + EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(argOut1.getByteArray()[0])
+ + ")");
+ }
+ int length0 = (argOut0.getByteArray()[1] & 0xff << 8)
+ + (argOut0.getByteArray()[2] & 0xff << 0);
+ int length1 = (argOut1.getByteArray()[1] & 0xff << 8)
+ + (argOut1.getByteArray()[2] & 0xff << 0);
+ long chronon = DT_UTILS.parseDateTime(argOut0.getByteArray(), 3, length0,
+ argOut1.getByteArray(), 3, length1, DateTimeParseMode.TIME_ONLY);
+
+ aTime.setValue((int) chronon);
+ timeSerde.serialize(aTime, out);
+
+ } catch (HyracksDataException ex) {
+ throw new AlgebricksException(ex);
+ }
+ }
+ };
+ }
+
+ };
+ }
+
+ /* (non-Javadoc)
+ * @see edu.uci.ics.asterix.om.functions.AbstractFunctionDescriptor#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 a13eed3..71ce352 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
@@ -245,6 +245,9 @@
import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.IntervalOverlapsDescriptor;
import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.IntervalStartedByDescriptor;
import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.IntervalStartsDescriptor;
+import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.ParseDateDescriptor;
+import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.ParseDateTimeDescriptor;
+import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.ParseTimeDescriptor;
import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.TimeFromDatetimeDescriptor;
import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.TimeFromUnixTimeInMsDescriptor;
import edu.uci.ics.asterix.runtime.evaluators.functions.temporal.YearMonthDurationComparatorDecriptor;
@@ -541,6 +544,9 @@
temp.add(GetDayTimeDurationDescriptor.FACTORY);
temp.add(IntervalBinDescriptor.FACTORY);
temp.add(DayOfWeekDescriptor.FACTORY);
+ temp.add(ParseDateDescriptor.FACTORY);
+ temp.add(ParseTimeDescriptor.FACTORY);
+ temp.add(ParseDateTimeDescriptor.FACTORY);
// Interval constructor
temp.add(AIntervalFromDateConstructorDescriptor.FACTORY);