com.linkedin.pinot.common.data.DateTimeFormatSpec.java Source code

Java tutorial

Introduction

Here is the source code for com.linkedin.pinot.common.data.DateTimeFormatSpec.java

Source

/**
 * Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
 *
 * 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 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 com.linkedin.pinot.common.data;

import com.linkedin.pinot.common.utils.EqualityUtils;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.linkedin.pinot.common.data.DateTimeFieldSpec.TimeFormat;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormatter;

/**
 * Class to represent format from {@link DateTimeFieldSpec}
 */
public class DateTimeFormatSpec {

    public static final String FORMAT_TOKENS_ERROR_STR = "format must be of pattern size:timeunit:timeformat(:pattern)";
    public static final String FORMAT_PATTERN_ERROR_STR = "format must be of format [0-9]+:<TimeUnit>:<TimeFormat>(:pattern)";
    public static final String TIME_FORMAT_ERROR_STR = "format must be of format [0-9]+:<TimeUnit>:EPOCH or [0-9]+:<TimeUnit>:SIMPLE_DATE_FORMAT:<format>";

    public static final String NUMBER_REGEX = "[1-9][0-9]*";

    public static final String COLON_SEPARATOR = ":";

    /* DateTimeFieldSpec format is of format size:timeUnit:timeformat:pattern tz(timezone)
     * tz(timezone) is optional. If not specified, UTC timezone is used */
    public static final int FORMAT_SIZE_POSITION = 0;
    public static final int FORMAT_UNIT_POSITION = 1;
    public static final int FORMAT_TIMEFORMAT_POSITION = 2;
    public static final int FORMAT_PATTERN_POSITION = 3;
    public static final int MIN_FORMAT_TOKENS = 3;
    public static final int MAX_FORMAT_TOKENS = 4;

    private String _format;
    private int _size;
    private DateTimeFormatUnitSpec _unitSpec;
    private DateTimeFormatPatternSpec _patternSpec;

    public DateTimeFormatSpec(String format) {
        _format = format;
        if (isValidFormat(format)) {
            String[] formatTokens = format.split(COLON_SEPARATOR, MAX_FORMAT_TOKENS);
            _size = Integer.valueOf(formatTokens[FORMAT_SIZE_POSITION]);
            _unitSpec = new DateTimeFormatUnitSpec(formatTokens[FORMAT_UNIT_POSITION]);
            if (formatTokens.length == MAX_FORMAT_TOKENS) {
                _patternSpec = new DateTimeFormatPatternSpec(formatTokens[FORMAT_TIMEFORMAT_POSITION],
                        formatTokens[FORMAT_PATTERN_POSITION]);
            } else {
                _patternSpec = new DateTimeFormatPatternSpec(formatTokens[FORMAT_TIMEFORMAT_POSITION], null);
            }
        }
    }

    /**
     * Constructs a dateTimeSpec format, given the components of a format
     * @param columnSize
     * @param columnUnit
     * @param columnTimeFormat
     * @return
     */
    public DateTimeFormatSpec(int columnSize, String columnUnit, String columnTimeFormat) {
        _format = Joiner.on(COLON_SEPARATOR).join(columnSize, columnUnit, columnTimeFormat);
        isValidFormat(_format);

        _size = columnSize;
        _unitSpec = new DateTimeFormatUnitSpec(columnUnit);
        _patternSpec = new DateTimeFormatPatternSpec(columnTimeFormat, null);
    }

    /**
     * Constructs a dateTimeSpec format, given the components of a format
     * @param columnSize
     * @param columnUnit
     * @param columnTimeFormat
     * @param sdfPattern and tz
     * @return
     */
    public DateTimeFormatSpec(int columnSize, String columnUnit, String columnTimeFormat, String sdfPattern) {
        _format = Joiner.on(COLON_SEPARATOR).join(columnSize, columnUnit, columnTimeFormat, sdfPattern);
        isValidFormat(_format);

        _size = columnSize;
        _unitSpec = new DateTimeFormatUnitSpec(columnUnit);
        _patternSpec = new DateTimeFormatPatternSpec(columnTimeFormat, sdfPattern);
    }

    public String getFormat() {
        return _format;
    }

    public int getColumnSize() {
        return _size;
    }

    public TimeUnit getColumnUnit() {
        return _unitSpec.getTimeUnit();
    }

    public DateTimeFormatUnitSpec.DateTimeTransformUnit getColumnDateTimeTransformUnit() {
        return _unitSpec.getDateTimeTransformUnit();
    }

    public TimeFormat getTimeFormat() {
        return _patternSpec.getTimeFormat();
    }

    public String getSDFPattern() {
        return _patternSpec.getSdfPattern();
    }

    public DateTimeZone getDateTimezone() {
        return _patternSpec.getDateTimeZone();
    }

    public DateTimeFormatter getDateTimeFormatter() {
        return _patternSpec.getDateTimeFormatter();
    }

    /**
     * <ul>
     * <li>Given a timestamp in millis, convert it to the given format
     * This method should not do validation of outputGranularity.
     * The validation should be handled by caller using {@link #isValidFormat(String)}</li>
     * <ul>
     * <li>1) given dateTimeColumnValueMS = 1498892400000 and format=1:HOURS:EPOCH,
     * dateTimeSpec.fromMillis(1498892400000) = 416359 (i.e. dateTimeColumnValueMS/(1000*60*60))</li>
     * <li>2) given dateTimeColumnValueMS = 1498892400000 and format=5:MINUTES:EPOCH,
     * dateTimeSpec.fromMillis(1498892400000) = 4996308 (i.e. timeColumnValueMS/(1000*60*5))</li>
     * <li>3) given dateTimeColumnValueMS = 1498892400000 and
     * format=1:DAYS:SIMPLE_DATE_FORMAT:yyyyMMdd, dateTimeSpec.fromMillis(1498892400000) = 20170701</li>
     * </ul>
     * </ul>
     * @param dateTimeColumnValueMS
     * @param toFormat - the format in which to convert the millis value
     * @param type - type of return value (can be int/long or string depending on time format)
     * @return dateTime column value in dateTimeFieldSpec
     */
    public <T extends Object> T fromMillisToFormat(Long dateTimeColumnValueMS, Class<T> type) {
        Preconditions.checkNotNull(dateTimeColumnValueMS);

        Object dateTimeColumnValue = null;

        if (_patternSpec.getTimeFormat().equals(TimeFormat.EPOCH)) {
            dateTimeColumnValue = _unitSpec.getTimeUnit().convert(dateTimeColumnValueMS, TimeUnit.MILLISECONDS)
                    / _size;
        } else {
            dateTimeColumnValue = _patternSpec.getDateTimeFormatter().print(dateTimeColumnValueMS);
        }
        return type.cast(dateTimeColumnValue);
    }

    /**
     * <ul>
     * <li>Convert a time value in a format, to millis.
     * This method should not do validation of outputGranularity.
     * The validation should be handled by caller using {@link #isValidFormat(String)}</li>
     * <ul>
     * <li>1) given dateTimeColumnValue = 416359 and format=1:HOURS:EPOCH
     * dateTimeSpec.toMillis(416359) = 1498892400000 (i.e. timeColumnValue*60*60*1000)</li>
     * <li>2) given dateTimeColumnValue = 4996308 and format=5:MINUTES:EPOCH
     * dateTimeSpec.toMillis(4996308) = 1498892400000 (i.e. timeColumnValue*5*60*1000)</li>
     * <li>3) given dateTimeColumnValue = 20170701 and format=1:DAYS:SIMPLE_DATE_FORMAT:yyyyMMdd
     * dateTimeSpec.toMillis(20170701) = 1498892400000</li>
     * </ul>
     * <ul>
     * @param dateTimeColumnValue - datetime Column value to convert to millis
     * @param fromFormat - the format in which the date time column value is expressed
     * @return datetime value in millis
     */
    public Long fromFormatToMillis(Object dateTimeColumnValue) {
        Preconditions.checkNotNull(dateTimeColumnValue);

        Long timeColumnValueMS = 0L;

        if (_patternSpec.getTimeFormat().equals(TimeFormat.EPOCH)) {
            timeColumnValueMS = TimeUnit.MILLISECONDS.convert((Long) dateTimeColumnValue * _size,
                    _unitSpec.getTimeUnit());
        } else {
            timeColumnValueMS = _patternSpec.getDateTimeFormatter()
                    .parseMillis(String.valueOf(dateTimeColumnValue));
        }

        return timeColumnValueMS;
    }

    public static boolean isValidFormat(String format) {
        Preconditions.checkNotNull(format);

        String[] formatTokens = format.split(COLON_SEPARATOR, MAX_FORMAT_TOKENS);
        Preconditions.checkArgument(
                formatTokens.length == MIN_FORMAT_TOKENS || formatTokens.length == MAX_FORMAT_TOKENS,
                FORMAT_TOKENS_ERROR_STR);

        Preconditions.checkArgument(formatTokens[FORMAT_SIZE_POSITION].matches(NUMBER_REGEX),
                FORMAT_PATTERN_ERROR_STR);
        Preconditions.checkArgument(DateTimeFormatUnitSpec.isValidUnitSpec(formatTokens[FORMAT_UNIT_POSITION]));
        if (formatTokens.length == MIN_FORMAT_TOKENS) {
            Preconditions.checkArgument(
                    formatTokens[FORMAT_TIMEFORMAT_POSITION].equals(TimeFormat.EPOCH.toString()),
                    TIME_FORMAT_ERROR_STR);
        } else {
            Preconditions.checkArgument(
                    formatTokens[FORMAT_TIMEFORMAT_POSITION].equals(TimeFormat.SIMPLE_DATE_FORMAT.toString()),
                    TIME_FORMAT_ERROR_STR);
        }
        return true;
    }

    @Override
    public boolean equals(Object o) {
        if (EqualityUtils.isSameReference(this, o)) {
            return true;
        }

        if (EqualityUtils.isNullOrNotSameClass(this, o)) {
            return false;
        }

        DateTimeFormatSpec that = (DateTimeFormatSpec) o;

        return EqualityUtils.isEqual(_size, that._size) && EqualityUtils.isEqual(_format, that._format)
                && EqualityUtils.isEqual(_unitSpec, that._unitSpec)
                && EqualityUtils.isEqual(_patternSpec, that._patternSpec);
    }

    @Override
    public int hashCode() {
        int result = EqualityUtils.hashCodeOf(_format);
        result = EqualityUtils.hashCodeOf(result, _size);
        result = EqualityUtils.hashCodeOf(result, _unitSpec);
        result = EqualityUtils.hashCodeOf(result, _patternSpec);
        return result;
    }

    @Override
    public String toString() {
        return "DateTimeFormatSpec{" + "_format='" + _format + '\'' + ", _size=" + _size + ", _unitSpec="
                + _unitSpec + ", _patternSpec=" + _patternSpec + '}';
    }
}