org.apache.phoenix.util.DateUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.phoenix.util.DateUtil.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.phoenix.util;

import static org.apache.phoenix.query.QueryConstants.MAX_ALLOWED_NANOS;
import static org.apache.phoenix.query.QueryConstants.MILLIS_TO_NANOS_CONVERTOR;

import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.TimeZone;

import org.apache.commons.lang.time.FastDateFormat;
import org.apache.phoenix.schema.IllegalDataException;
import org.apache.phoenix.schema.types.PDataType;
import org.joda.time.DateTimeZone;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.joda.time.format.ISODateTimeFormat;

import com.google.common.collect.Lists;

@SuppressWarnings({ "serial", "deprecation" })
public class DateUtil {
    public static final String DEFAULT_TIME_ZONE_ID = "GMT";
    public static final String LOCAL_TIME_ZONE_ID = "LOCAL";
    private static final TimeZone DEFAULT_TIME_ZONE = TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID);

    public static final String DEFAULT_MS_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
    public static final Format DEFAULT_MS_DATE_FORMATTER = FastDateFormat.getInstance(DEFAULT_MS_DATE_FORMAT,
            TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID));

    public static final String DEFAULT_DATE_FORMAT = DEFAULT_MS_DATE_FORMAT;
    public static final Format DEFAULT_DATE_FORMATTER = DEFAULT_MS_DATE_FORMATTER;

    public static final String DEFAULT_TIME_FORMAT = DEFAULT_MS_DATE_FORMAT;
    public static final Format DEFAULT_TIME_FORMATTER = DEFAULT_MS_DATE_FORMATTER;

    public static final String DEFAULT_TIMESTAMP_FORMAT = DEFAULT_MS_DATE_FORMAT;
    public static final Format DEFAULT_TIMESTAMP_FORMATTER = DEFAULT_MS_DATE_FORMATTER;

    private static final DateTimeFormatter ISO_DATE_TIME_FORMATTER = new DateTimeFormatterBuilder()
            .append(ISODateTimeFormat.dateParser())
            .appendOptional(new DateTimeFormatterBuilder().appendLiteral(' ').toParser())
            .appendOptional(new DateTimeFormatterBuilder().append(ISODateTimeFormat.timeParser()).toParser())
            .toFormatter().withChronology(ISOChronology.getInstanceUTC());

    private DateUtil() {
    }

    private static TimeZone getTimeZone(String timeZoneId) {
        TimeZone parserTimeZone;
        if (timeZoneId == null) {
            parserTimeZone = DateUtil.DEFAULT_TIME_ZONE;
        } else if (LOCAL_TIME_ZONE_ID.equalsIgnoreCase(timeZoneId)) {
            parserTimeZone = TimeZone.getDefault();
        } else {
            parserTimeZone = TimeZone.getTimeZone(timeZoneId);
        }
        return parserTimeZone;
    }

    private static String[] defaultPattern;
    static {
        int maxOrdinal = Integer.MIN_VALUE;
        List<PDataType> timeDataTypes = Lists.newArrayListWithExpectedSize(6);
        for (PDataType type : PDataType.values()) {
            if (java.util.Date.class.isAssignableFrom(type.getJavaClass())) {
                timeDataTypes.add(type);
                if (type.ordinal() > maxOrdinal) {
                    maxOrdinal = type.ordinal();
                }
            }
        }
        defaultPattern = new String[maxOrdinal + 1];
        for (PDataType type : timeDataTypes) {
            switch (type.getResultSetSqlType()) {
            case Types.TIMESTAMP:
                defaultPattern[type.ordinal()] = DateUtil.DEFAULT_TIMESTAMP_FORMAT;
                break;
            case Types.TIME:
                defaultPattern[type.ordinal()] = DateUtil.DEFAULT_TIME_FORMAT;
                break;
            case Types.DATE:
                defaultPattern[type.ordinal()] = DateUtil.DEFAULT_DATE_FORMAT;
                break;
            }
        }
    }

    private static String getDefaultFormat(PDataType type) {
        int ordinal = type.ordinal();
        if (ordinal >= 0 || ordinal < defaultPattern.length) {
            String format = defaultPattern[ordinal];
            if (format != null) {
                return format;
            }
        }
        throw new IllegalArgumentException("Expected a date/time type, but got " + type);
    }

    public static DateTimeParser getDateTimeParser(String pattern, PDataType pDataType, String timeZoneId) {
        TimeZone timeZone = getTimeZone(timeZoneId);
        String defaultPattern = getDefaultFormat(pDataType);
        if (pattern == null || pattern.length() == 0) {
            pattern = defaultPattern;
        }
        if (defaultPattern.equals(pattern)) {
            return ISODateFormatParserFactory.getParser(timeZone);
        } else {
            return new SimpleDateFormatParser(pattern, timeZone);
        }
    }

    public static DateTimeParser getDateTimeParser(String pattern, PDataType pDataType) {
        return getDateTimeParser(pattern, pDataType, null);
    }

    public static Format getDateFormatter(String pattern) {
        return DateUtil.DEFAULT_DATE_FORMAT.equals(pattern) ? DateUtil.DEFAULT_DATE_FORMATTER
                : FastDateFormat.getInstance(pattern, DateUtil.DEFAULT_TIME_ZONE);
    }

    public static Format getTimeFormatter(String pattern) {
        return DateUtil.DEFAULT_TIME_FORMAT.equals(pattern) ? DateUtil.DEFAULT_TIME_FORMATTER
                : FastDateFormat.getInstance(pattern, DateUtil.DEFAULT_TIME_ZONE);
    }

    public static Format getTimestampFormatter(String pattern) {
        return DateUtil.DEFAULT_TIMESTAMP_FORMAT.equals(pattern) ? DateUtil.DEFAULT_TIMESTAMP_FORMATTER
                : FastDateFormat.getInstance(pattern, DateUtil.DEFAULT_TIME_ZONE);
    }

    private static long parseDateTime(String dateTimeValue) {
        return ISODateFormatParser.getInstance().parseDateTime(dateTimeValue);
    }

    public static Date parseDate(String dateValue) {
        return new Date(parseDateTime(dateValue));
    }

    public static Time parseTime(String timeValue) {
        return new Time(parseDateTime(timeValue));
    }

    public static Timestamp parseTimestamp(String timestampValue) {
        return new Timestamp(parseDateTime(timestampValue));
    }

    /**
     * Utility function to work around the weirdness of the {@link Timestamp} constructor.
     * This method takes the milli-seconds that spills over to the nanos part as part of 
     * constructing the {@link Timestamp} object.
     * If we just set the nanos part of timestamp to the nanos passed in param, we 
     * end up losing the sub-second part of timestamp. 
     */
    public static Timestamp getTimestamp(long millis, int nanos) {
        if (nanos > MAX_ALLOWED_NANOS || nanos < 0) {
            throw new IllegalArgumentException("nanos > " + MAX_ALLOWED_NANOS + " or < 0");
        }
        Timestamp ts = new Timestamp(millis);
        if (ts.getNanos() + nanos > MAX_ALLOWED_NANOS) {
            int millisToNanosConvertor = BigDecimal.valueOf(MILLIS_TO_NANOS_CONVERTOR).intValue();
            int overFlowMs = (ts.getNanos() + nanos) / millisToNanosConvertor;
            int overFlowNanos = (ts.getNanos() + nanos) - (overFlowMs * millisToNanosConvertor);
            ts = new Timestamp(millis + overFlowMs);
            ts.setNanos(ts.getNanos() + overFlowNanos);
        } else {
            ts.setNanos(ts.getNanos() + nanos);
        }
        return ts;
    }

    /**
     * Utility function to convert a {@link BigDecimal} value to {@link Timestamp}.
     */
    public static Timestamp getTimestamp(BigDecimal bd) {
        return DateUtil.getTimestamp(bd.longValue(),
                ((bd.remainder(BigDecimal.ONE).multiply(BigDecimal.valueOf(MILLIS_TO_NANOS_CONVERTOR)))
                        .intValue()));
    }

    public static interface DateTimeParser {
        public long parseDateTime(String dateTimeString) throws IllegalDataException;

        public TimeZone getTimeZone();
    }

    /**
     * This class is used when a user explicitly provides phoenix.query.dateFormat in configuration
     */
    private static class SimpleDateFormatParser implements DateTimeParser {
        private String datePattern;
        private SimpleDateFormat parser;

        public SimpleDateFormatParser(String pattern, TimeZone timeZone) {
            datePattern = pattern;
            parser = new SimpleDateFormat(pattern) {
                @Override
                public java.util.Date parseObject(String source) throws ParseException {
                    java.util.Date date = super.parse(source);
                    return new java.sql.Date(date.getTime());
                }
            };
            parser.setTimeZone(timeZone);
        }

        @Override
        public long parseDateTime(String dateTimeString) throws IllegalDataException {
            try {
                java.util.Date date = parser.parse(dateTimeString);
                return date.getTime();
            } catch (ParseException e) {
                throw new IllegalDataException("Unable to parse date/time '" + dateTimeString
                        + "' using format string of '" + datePattern + "'.");
            }
        }

        @Override
        public TimeZone getTimeZone() {
            return parser.getTimeZone();
        }
    }

    private static class ISODateFormatParserFactory {
        private ISODateFormatParserFactory() {
        }

        public static DateTimeParser getParser(final TimeZone timeZone) {
            // If timeZone matches default, get singleton DateTimeParser
            if (timeZone.equals(DEFAULT_TIME_ZONE)) {
                return ISODateFormatParser.getInstance();
            }
            // Otherwise, create new DateTimeParser
            return new DateTimeParser() {
                private final DateTimeFormatter formatter = ISO_DATE_TIME_FORMATTER
                        .withZone(DateTimeZone.forTimeZone(timeZone));

                @Override
                public long parseDateTime(String dateTimeString) throws IllegalDataException {
                    try {
                        return formatter.parseDateTime(dateTimeString).getMillis();
                    } catch (IllegalArgumentException ex) {
                        throw new IllegalDataException(ex);
                    }
                }

                @Override
                public TimeZone getTimeZone() {
                    return timeZone;
                }
            };
        }
    }

    /**
     * This class is our default DateTime string parser
     */
    private static class ISODateFormatParser implements DateTimeParser {
        private static final ISODateFormatParser INSTANCE = new ISODateFormatParser();

        public static ISODateFormatParser getInstance() {
            return INSTANCE;
        }

        private final DateTimeFormatter formatter = ISO_DATE_TIME_FORMATTER.withZoneUTC();

        private ISODateFormatParser() {
        }

        @Override
        public long parseDateTime(String dateTimeString) throws IllegalDataException {
            try {
                return formatter.parseDateTime(dateTimeString).getMillis();
            } catch (IllegalArgumentException ex) {
                throw new IllegalDataException(ex);
            }
        }

        @Override
        public TimeZone getTimeZone() {
            return formatter.getZone().toTimeZone();
        }
    }
}