blob: c0f0957616d9a8a4ec8de154369f32c6263de4a8 [file] [log] [blame]
/*
* 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.om.base.temporal;
import java.io.DataOutput;
import java.io.IOException;
import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
import edu.uci.ics.hyracks.dataflow.common.data.parsers.IValueParser;
import edu.uci.ics.hyracks.dataflow.common.data.parsers.IValueParserFactory;
public class ATimeParserFactory implements IValueParserFactory {
public static final IValueParserFactory INSTANCE = new ATimeParserFactory();
private static final long serialVersionUID = 1L;
private static final String timeErrorMessage = "Wrong Input Format for a Time Value";
private ATimeParserFactory() {
}
@Override
public IValueParser createValueParser() {
return new IValueParser() {
@Override
public void parse(char[] buffer, int start, int length, DataOutput out) throws HyracksDataException {
try {
out.writeInt(parseTimePart(buffer, start, length));
} catch (IOException ex) {
throw new HyracksDataException(ex);
}
}
};
}
/**
* Parse the given string as a time string, and return the milliseconds represented by the time.
*
* @param timeString
* @param start
* @param length
* @return
* @throws HyracksDataException
*/
public static int parseTimePart(String timeString, int start, int length) throws HyracksDataException {
int offset = 0;
int hour = 0, min = 0, sec = 0, millis = 0;
int timezone = 0;
boolean isExtendedForm = false;
if (timeString.charAt(start + offset + 2) == ':') {
isExtendedForm = true;
}
if (isExtendedForm
&& (timeString.charAt(start + offset + 2) != ':' || timeString.charAt(start + offset + 5) != ':')) {
throw new HyracksDataException(timeErrorMessage + ": Missing colon in an extended time format.");
}
// hour
for (int i = 0; i < 2; i++) {
if ((timeString.charAt(start + offset + i) >= '0' && timeString.charAt(start + offset + i) <= '9')) {
hour = hour * 10 + timeString.charAt(start + offset + i) - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in hour field");
}
}
if (hour < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.HOUR.ordinal()]
|| hour > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.HOUR.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": hour " + hour);
}
offset += (isExtendedForm) ? 3 : 2;
// minute
for (int i = 0; i < 2; i++) {
if ((timeString.charAt(start + offset + i) >= '0' && timeString.charAt(start + offset + i) <= '9')) {
min = min * 10 + timeString.charAt(start + offset + i) - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in minute field");
}
}
if (min < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.MINUTE.ordinal()]
|| min > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.MINUTE.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": min " + min);
}
offset += (isExtendedForm) ? 3 : 2;
// second
for (int i = 0; i < 2; i++) {
if ((timeString.charAt(start + offset + i) >= '0' && timeString.charAt(start + offset + i) <= '9')) {
sec = sec * 10 + timeString.charAt(start + offset + i) - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in second field");
}
}
if (sec < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.SECOND.ordinal()]
|| sec > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.SECOND.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": sec " + sec);
}
offset += 2;
if ((isExtendedForm && length > offset && timeString.charAt(start + offset) == '.')
|| (!isExtendedForm && length > offset)) {
offset += (isExtendedForm) ? 1 : 0;
int i = 0;
for (; i < 3 && offset + i < length; i++) {
if (timeString.charAt(start + offset + i) >= '0' && timeString.charAt(start + offset + i) <= '9') {
millis = millis * 10 + timeString.charAt(start + offset + i) - '0';
} else {
break;
}
}
offset += i;
for (; i < 3; i++) {
millis = millis * 10;
}
// error is thrown if more than three digits are seen for the millisecond part
if (length > offset && timeString.charAt(start + offset) >= '0' && timeString.charAt(start + offset) <= '9') {
throw new HyracksDataException(timeErrorMessage + ": too many fields for millisecond.");
}
}
if (length > offset) {
timezone = parseTimezonePart(timeString, start + offset);
}
return GregorianCalendarSystem.getInstance().getChronon(hour, min, sec, millis, timezone);
}
/**
* Parse the given string as a time string, and parse the timezone field.
*
* @param timeString
* @param start
* @return
* @throws HyracksDataException
*/
public static int parseTimezonePart(String timeString, int start) throws HyracksDataException {
int timezone = 0;
if (timeString.charAt(start) != 'Z') {
if ((timeString.charAt(start) != '+' && timeString.charAt(start) != '-')) {
throw new HyracksDataException("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 ((timeString.charAt(start + 1 + i) >= '0' && timeString.charAt(start + 1 + i) <= '9')) {
timezoneHour = (short) (timezoneHour * 10 + timeString.charAt(start + 1 + i) - '0');
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in timezone hour field");
}
}
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++) {
if ((timeString.charAt(start + temp_offset + 3 + i) >= '0' && timeString.charAt(start + temp_offset + 3
+ i) <= '9')) {
timezoneMinute = (short) (timezoneMinute * 10 + timeString.charAt(start + temp_offset + 3 + i) - '0');
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in timezone minute field");
}
}
if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
|| timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
throw new HyracksDataException(timeErrorMessage + ": time zone minute " + timezoneMinute);
}
if (timeString.charAt(start) == '-') {
timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
} else {
timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
}
}
return timezone;
}
/**
* Similar to {@link #parseTimePart(String, int, int)} but use a char array as input; although this is almost
* a copy-and-past code but it avoids object creation.
*
* @param timeString
* @param start
* @param length
* @return
* @throws HyracksDataException
*/
public static int parseTimePart(char[] timeString, int start, int length) throws HyracksDataException {
int offset = 0;
int hour = 0, min = 0, sec = 0, millis = 0;
int timezone = 0;
boolean isExtendedForm = false;
if (timeString[start + offset + 2] == ':') {
isExtendedForm = true;
}
if (isExtendedForm && (timeString[start + offset + 2] != ':' || timeString[start + offset + 5] != ':')) {
throw new HyracksDataException(timeErrorMessage + ": Missing colon in an extended time format.");
}
// hour
for (int i = 0; i < 2; i++) {
if ((timeString[start + offset + i] >= '0' && timeString[start + offset + i] <= '9')) {
hour = hour * 10 + timeString[start + offset + i] - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in hour field");
}
}
if (hour < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.HOUR.ordinal()]
|| hour > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.HOUR.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": hour " + hour);
}
offset += (isExtendedForm) ? 3 : 2;
// minute
for (int i = 0; i < 2; i++) {
if ((timeString[start + offset + i] >= '0' && timeString[start + offset + i] <= '9')) {
min = min * 10 + timeString[start + offset + i] - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in minute field");
}
}
if (min < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.MINUTE.ordinal()]
|| min > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.MINUTE.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": min " + min);
}
offset += (isExtendedForm) ? 3 : 2;
// second
for (int i = 0; i < 2; i++) {
if ((timeString[start + offset + i] >= '0' && timeString[start + offset + i] <= '9')) {
sec = sec * 10 + timeString[start + offset + i] - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in second field");
}
}
if (sec < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.SECOND.ordinal()]
|| sec > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.SECOND.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": sec " + sec);
}
offset += 2;
if ((isExtendedForm && length > offset && timeString[start + offset] == '.')
|| (!isExtendedForm && length > offset)) {
offset += (isExtendedForm) ? 1 : 0;
int i = 0;
for (; i < 3 && offset + i < length; i++) {
if (timeString[start + offset + i] >= '0' && timeString[start + offset + i] <= '9') {
millis = millis * 10 + timeString[start + offset + i] - '0';
} else {
break;
}
}
offset += i;
for (; i < 3; i++) {
millis = millis * 10;
}
// error is thrown if more than three digits are seen for the millisecond part
if (length > offset && timeString[start + offset] >= '0' && timeString[start + offset] <= '9') {
throw new HyracksDataException(timeErrorMessage + ": too many fields for millisecond.");
}
}
if (length > offset) {
timezone = parseTimezonePart(timeString, start + offset);
}
return GregorianCalendarSystem.getInstance().getChronon(hour, min, sec, millis, timezone);
}
/**
* Similar to {@link #parseTimezonePart(String, int)} but use a char array as input; although this is almost
* a copy-and-past code but it avoids object creation.
*
* @param timeString
* @param start
* @param length
* @return
* @throws HyracksDataException
*/
public static int parseTimezonePart(char[] timeString, int start) throws HyracksDataException {
int timezone = 0;
if (timeString[start] != 'Z') {
if ((timeString[start] != '+' && timeString[start] != '-')) {
throw new HyracksDataException("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 ((timeString[start + 1 + i] >= '0' && timeString[start + 1 + i] <= '9')) {
timezoneHour = (short) (timezoneHour * 10 + timeString[start + 1 + i] - '0');
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in timezone hour field");
}
}
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++) {
if ((timeString[start + temp_offset + 3 + i] >= '0' && timeString[start + temp_offset + 3 + i] <= '9')) {
timezoneMinute = (short) (timezoneMinute * 10 + timeString[start + temp_offset + 3 + i] - '0');
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in timezone minute field");
}
}
if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
|| timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
throw new HyracksDataException(timeErrorMessage + ": time zone minute " + timezoneMinute);
}
if (timeString[start] == '-') {
timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
} else {
timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
}
}
return timezone;
}
/**
* Similar to {@link #parseTimePart(String, int, int)} but use a byte array as input; although this is almost
* a copy-and-past code but it avoids object creation.
*
* @param timeString
* @param start
* @param length
* @return
* @throws HyracksDataException
*/
public static int parseTimePart(byte[] timeString, int start, int length) throws HyracksDataException {
int offset = 0;
int hour = 0, min = 0, sec = 0, millis = 0;
int timezone = 0;
boolean isExtendedForm = false;
if (timeString[start + offset + 2] == ':') {
isExtendedForm = true;
}
if (isExtendedForm && (timeString[start + offset + 2] != ':' || timeString[start + offset + 5] != ':')) {
throw new HyracksDataException(timeErrorMessage + ": Missing colon in an extended time format.");
}
// hour
for (int i = 0; i < 2; i++) {
if ((timeString[start + offset + i] >= '0' && timeString[start + offset + i] <= '9')) {
hour = hour * 10 + timeString[start + offset + i] - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in hour field");
}
}
if (hour < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.HOUR.ordinal()]
|| hour > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.HOUR.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": hour " + hour);
}
offset += (isExtendedForm) ? 3 : 2;
// minute
for (int i = 0; i < 2; i++) {
if ((timeString[start + offset + i] >= '0' && timeString[start + offset + i] <= '9')) {
min = min * 10 + timeString[start + offset + i] - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in minute field");
}
}
if (min < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.MINUTE.ordinal()]
|| min > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.MINUTE.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": min " + min);
}
offset += (isExtendedForm) ? 3 : 2;
// second
for (int i = 0; i < 2; i++) {
if ((timeString[start + offset + i] >= '0' && timeString[start + offset + i] <= '9')) {
sec = sec * 10 + timeString[start + offset + i] - '0';
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in second field");
}
}
if (sec < GregorianCalendarSystem.FIELD_MINS[GregorianCalendarSystem.Fields.SECOND.ordinal()]
|| sec > GregorianCalendarSystem.FIELD_MAXS[GregorianCalendarSystem.Fields.SECOND.ordinal()]) {
throw new HyracksDataException(timeErrorMessage + ": sec " + sec);
}
offset += 2;
if ((isExtendedForm && length > offset && timeString[start + offset] == '.')
|| (!isExtendedForm && length > offset)) {
offset += (isExtendedForm) ? 1 : 0;
int i = 0;
for (; i < 3 && offset + i < length; i++) {
if (timeString[start + offset + i] >= '0' && timeString[start + offset + i] <= '9') {
millis = millis * 10 + timeString[start + offset + i] - '0';
} else {
break;
}
}
offset += i;
for (; i < 3; i++) {
millis = millis * 10;
}
// error is thrown if more than three digits are seen for the millisecond part
if (length > offset && timeString[start + offset] >= '0' && timeString[start + offset] <= '9') {
throw new HyracksDataException(timeErrorMessage + ": too many fields for millisecond.");
}
}
if (length > offset) {
timezone = parseTimezonePart(timeString, start + offset);
}
return GregorianCalendarSystem.getInstance().getChronon(hour, min, sec, millis, timezone);
}
/**
* Similar to {@link #parseTimezonePart(String, int)} but use a byte array as input; although this is almost
* a copy-and-past code but it avoids object creation.
*
* @param timeString
* @param start
* @param length
* @return
* @throws HyracksDataException
*/
public static int parseTimezonePart(byte[] timeString, int start) throws HyracksDataException {
int timezone = 0;
if (timeString[start] != 'Z') {
if ((timeString[start] != '+' && timeString[start] != '-')) {
throw new HyracksDataException("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 ((timeString[start + 1 + i] >= '0' && timeString[start + 1 + i] <= '9')) {
timezoneHour = (short) (timezoneHour * 10 + timeString[start + 1 + i] - '0');
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in timezone hour field");
}
}
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++) {
if ((timeString[start + temp_offset + 3 + i] >= '0' && timeString[start + temp_offset + 3 + i] <= '9')) {
timezoneMinute = (short) (timezoneMinute * 10 + timeString[start + temp_offset + 3 + i] - '0');
} else {
throw new HyracksDataException(timeErrorMessage + ": Non-numeric value in timezone minute field");
}
}
if (timezoneMinute < GregorianCalendarSystem.TIMEZONE_MIN_MIN
|| timezoneMinute > GregorianCalendarSystem.TIMEZONE_MIN_MAX) {
throw new HyracksDataException(timeErrorMessage + ": time zone minute " + timezoneMinute);
}
if (timeString[start] == '-') {
timezone = (byte) -((timezoneHour * 4) + timezoneMinute / 15);
} else {
timezone = (byte) ((timezoneHour * 4) + timezoneMinute / 15);
}
}
return timezone;
}
}