All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.questdb.griffin.model.IntervalUtils Maven / Gradle / Ivy

/*******************************************************************************
 *     ___                  _   ____  ____
 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
 *   | | | | | | |/ _ \/ __| __| | | |  _ \
 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
 *    \__\_\\__,_|\___||___/\__|____/|____/
 *
 *  Copyright (c) 2014-2019 Appsicle
 *  Copyright (c) 2019-2020 QuestDB
 *
 *  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 io.questdb.griffin.model;

import io.questdb.griffin.SqlException;
import io.questdb.std.LongList;
import io.questdb.std.Numbers;
import io.questdb.std.NumericException;
import io.questdb.std.datetime.microtime.TimestampFormatUtils;
import io.questdb.std.datetime.microtime.Timestamps;

public final class IntervalUtils {
    public static final int LO_INDEX = 0;
    public static final int HI_INDEX = 1;
    public static final int OPERATION_PERIOD_TYPE_ADJUSTMENT_INDEX = 2;
    public static final int PERIOD_COUNT_INDEX = 3;
    public static final int STATIC_LONGS_PER_DYNAMIC_INTERVAL = 4;

    public static void addHiLoInterval(
            long lo,
            long hi,
            int period,
            char periodType,
            int periodCount,
            short operation,
            LongList out) {
        addHiLoInterval(lo, hi, period, periodType, periodCount, (short) 0, (short) 0, operation, out);
    }

    public static void addHiLoInterval(
            long lo,
            long hi,
            int period,
            char periodType,
            int periodCount,
            short adjustment,
            short dynamicIndicator,
            short operation,
            LongList out) {
        out.add(lo);
        out.add(hi);
        out.add(Numbers.encodeLowHighInts(
                Numbers.encodeLowHighShorts(operation, (short) ((int) periodType + Short.MIN_VALUE)),
                Numbers.encodeLowHighShorts(adjustment, dynamicIndicator)));
        out.add(Numbers.encodeLowHighInts(period, periodCount));
    }

    public static void addHiLoInterval(long lo,
                                       long hi,
                                       short adjustment,
                                       short dynamicIndicator,
                                       short operation,
                                       LongList out) {
        addHiLoInterval(lo, hi, 0, (char) 0, 1, adjustment, dynamicIndicator, operation, out);
    }

    public static void addHiLoInterval(long lo, long hi, short operation, LongList out) {
        addHiLoInterval(lo, hi, 0, (char) 0, 1, operation, out);
    }

    public static void apply(LongList temp, long lo, long hi, int period, char periodType, int count) {
        temp.add(lo);
        temp.add(hi);
        if (count > 1) {
            switch (periodType) {
                case 'y':
                    addYearIntervals(period, count, temp);
                    break;
                case 'M':
                    addMonthInterval(period, count, temp);
                    break;
                case 'h':
                    addMillisInterval(period * Timestamps.HOUR_MICROS, count, temp);
                    break;
                case 'm':
                    addMillisInterval(period * Timestamps.MINUTE_MICROS, count, temp);
                    break;
                case 's':
                    addMillisInterval(period * Timestamps.SECOND_MICROS, count, temp);
                    break;
                case 'd':
                    addMillisInterval(period * Timestamps.DAY_MICROS, count, temp);
                    break;
            }
        }
    }

    public static void applyLastEncodedIntervalEx(LongList intervals) {
        int index = intervals.size() - 4;
        long lo = getEncodedPeriodLo(intervals, index);
        long hi = getEncodedPeriodHi(intervals, index);
        int period = getEncodedPeriod(intervals, index);
        char periodType = getEncodedPeriodType(intervals, index);
        int count = getEncodedPeriodCount(intervals, index);

        intervals.setPos(index);
        if (periodType == 0) {
            intervals.extendAndSet(index + 1, hi);
            intervals.setQuick(index, lo);
            return;
        }
        apply(intervals, lo, hi, period, periodType, count);
    }

    public static short getEncodedAdjustment(LongList intervals, int index) {
        return Numbers.decodeLowShort(Numbers.decodeHighInt(
                intervals.getQuick(index + OPERATION_PERIOD_TYPE_ADJUSTMENT_INDEX)));
    }

    public static short getEncodedDynamicIndicator(LongList intervals, int index) {
        return Numbers.decodeHighShort(Numbers.decodeHighInt(
                intervals.getQuick(index + OPERATION_PERIOD_TYPE_ADJUSTMENT_INDEX)));
    }

    public static short getEncodedOperation(LongList intervals, int index) {
        return Numbers.decodeLowShort(
                Numbers.decodeLowInt(
                        intervals.getQuick(index + OPERATION_PERIOD_TYPE_ADJUSTMENT_INDEX)
                )
        );
    }

    public static int getEncodedPeriod(LongList intervals, int index) {
        return Numbers.decodeLowInt(
                intervals.getQuick(index + PERIOD_COUNT_INDEX)
        );
    }

    public static int getEncodedPeriodCount(LongList intervals, int index) {
        return Numbers.decodeHighInt(
                intervals.getQuick(index + PERIOD_COUNT_INDEX));
    }

    public static long getEncodedPeriodHi(LongList out, int index) {
        return out.getQuick(index + HI_INDEX);
    }

    public static long getEncodedPeriodLo(LongList out, int index) {
        return out.getQuick(index + LO_INDEX);
    }

    public static char getEncodedPeriodType(LongList intervals, int index) {
        int pts = Numbers.decodeHighShort(
                Numbers.decodeLowInt(
                        intervals.getQuick(index + OPERATION_PERIOD_TYPE_ADJUSTMENT_INDEX)
                )
        );
        return (char) (pts - (int) Short.MIN_VALUE);
    }

    public static long parseCCPartialDate(CharSequence seq, final int pos, int lim) throws NumericException {
        long ts;
        if (lim - pos < 4) {
            throw NumericException.INSTANCE;
        }
        int p = pos;
        int year = Numbers.parseInt(seq, p, p += 4);
        boolean l = Timestamps.isLeapYear(year);
        if (checkLen(p, lim)) {
            checkChar(seq, p++, lim, '-');
            int month = Numbers.parseInt(seq, p, p += 2);
            checkRange(month, 1, 12);
            if (checkLen(p, lim)) {
                checkChar(seq, p++, lim, '-');
                int day = Numbers.parseInt(seq, p, p += 2);
                checkRange(day, 1, Timestamps.getDaysPerMonth(month, l));
                if (checkLen(p, lim)) {
                    checkChar(seq, p++, lim, 'T', ' ');
                    int hour = Numbers.parseInt(seq, p, p += 2);
                    checkRange(hour, 0, 23);
                    if (checkLen(p, lim)) {
                        checkChar(seq, p++, lim, ':');
                        int min = Numbers.parseInt(seq, p, p += 2);
                        checkRange(min, 0, 59);
                        if (checkLen(p, lim)) {
                            checkChar(seq, p++, lim, ':');
                            int sec = Numbers.parseInt(seq, p, p += 2);
                            checkRange(sec, 0, 59);
                            if (lim - p > 3 && seq.charAt(p) == '.') {
                                p++;
                                int ms = Numbers.parseInt(seq, p, p += 3);
                                if (lim - p > 2 && Character.isDigit(seq.charAt(p))) {
                                    // micros
                                    int micr = Numbers.parseInt(seq, p, p += 3);
                                    ts = Timestamps.yearMicros(year, l)
                                            + Timestamps.monthOfYearMicros(month, l)
                                            + (day - 1) * Timestamps.DAY_MICROS
                                            + hour * Timestamps.HOUR_MICROS
                                            + min * Timestamps.MINUTE_MICROS
                                            + sec * Timestamps.SECOND_MICROS
                                            + ms * Timestamps.MILLI_MICROS
                                            + micr
                                            + checkTimezoneTail(seq, p, lim)
                                            + 1;
                                } else {
                                    // millis
                                    ts = Timestamps.yearMicros(year, l)
                                            + Timestamps.monthOfYearMicros(month, l)
                                            + (day - 1) * Timestamps.DAY_MICROS
                                            + hour * Timestamps.HOUR_MICROS
                                            + min * Timestamps.MINUTE_MICROS
                                            + sec * Timestamps.SECOND_MICROS
                                            + (ms + 1) * Timestamps.MILLI_MICROS
                                            + checkTimezoneTail(seq, p, lim);
                                }
                            } else {
                                // seconds
                                ts = Timestamps.yearMicros(year, l)
                                        + Timestamps.monthOfYearMicros(month, l)
                                        + (day - 1) * Timestamps.DAY_MICROS
                                        + hour * Timestamps.HOUR_MICROS
                                        + min * Timestamps.MINUTE_MICROS
                                        + (sec + 1) * Timestamps.SECOND_MICROS
                                        + checkTimezoneTail(seq, p, lim);
                            }
                        } else {
                            // minute
                            ts = Timestamps.yearMicros(year, l)
                                    + Timestamps.monthOfYearMicros(month, l)
                                    + (day - 1) * Timestamps.DAY_MICROS
                                    + hour * Timestamps.HOUR_MICROS
                                    + (min + 1) * Timestamps.MINUTE_MICROS;

                        }
                    } else {
                        // year + month + day + hour
                        ts = Timestamps.yearMicros(year, l)
                                + Timestamps.monthOfYearMicros(month, l)
                                + (day - 1) * Timestamps.DAY_MICROS
                                + (hour + 1) * Timestamps.HOUR_MICROS;

                    }
                } else {
                    // year + month + day
                    ts = Timestamps.addDays(Timestamps.yearMicros(year, l)
                            + Timestamps.monthOfYearMicros(month, l)
                            + (day - 1) * Timestamps.DAY_MICROS, 1);
                }
            } else {
                // year + month
                ts = Timestamps.addMonths(Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l), 1);
            }
        } else {
            // year
            ts = Timestamps.yearMicros(year + 1, Timestamps.isLeapYear(year + 1));
        }
        return ts;
    }

    public static long parseCCPartialDate(CharSequence seq) throws NumericException {
        return parseCCPartialDate(seq, 0, seq.length());
    }

    public static long parseFloorPartialDate(CharSequence seq, final int pos, int lim) throws NumericException {
        long ts;
        if (lim - pos < 4) {
            throw NumericException.INSTANCE;
        }
        int p = pos;
        int year = Numbers.parseInt(seq, p, p += 4);
        boolean l = Timestamps.isLeapYear(year);
        if (checkLen(p, lim)) {
            checkChar(seq, p++, lim, '-');
            int month = Numbers.parseInt(seq, p, p += 2);
            checkRange(month, 1, 12);
            if (checkLen(p, lim)) {
                checkChar(seq, p++, lim, '-');
                int day = Numbers.parseInt(seq, p, p += 2);
                checkRange(day, 1, Timestamps.getDaysPerMonth(month, l));
                if (checkLen(p, lim)) {
                    checkChar(seq, p++, lim, 'T', ' ');
                    int hour = Numbers.parseInt(seq, p, p += 2);
                    checkRange(hour, 0, 23);
                    if (checkLen(p, lim)) {
                        checkChar(seq, p++, lim, ':');
                        int min = Numbers.parseInt(seq, p, p += 2);
                        checkRange(min, 0, 59);
                        if (checkLen(p, lim)) {
                            checkChar(seq, p++, lim, ':');
                            int sec = Numbers.parseInt(seq, p, p += 2);
                            checkRange(sec, 0, 59);
                            if (lim - p > 3 && seq.charAt(p) == '.') {
                                p++;
                                int ms = Numbers.parseInt(seq, p, p += 3);
                                if (lim - p > 2 && Character.isDigit(seq.charAt(p))) {
                                    int micr = Numbers.parseInt(seq, p, p += 3);

                                    // micros
                                    ts = Timestamps.yearMicros(year, l)
                                            + Timestamps.monthOfYearMicros(month, l)
                                            + (day - 1) * Timestamps.DAY_MICROS
                                            + hour * Timestamps.HOUR_MICROS
                                            + min * Timestamps.MINUTE_MICROS
                                            + sec * Timestamps.SECOND_MICROS
                                            + ms * Timestamps.MILLI_MICROS
                                            + micr
                                            + checkTimezoneTail(seq, p, lim);
                                } else {
                                    // millis
                                    ts = Timestamps.yearMicros(year, l)
                                            + Timestamps.monthOfYearMicros(month, l)
                                            + (day - 1) * Timestamps.DAY_MICROS
                                            + hour * Timestamps.HOUR_MICROS
                                            + min * Timestamps.MINUTE_MICROS
                                            + sec * Timestamps.SECOND_MICROS
                                            + ms * Timestamps.MILLI_MICROS
                                            + checkTimezoneTail(seq, p, lim);
                                }
                            } else {
                                // seconds
                                ts = Timestamps.yearMicros(year, l)
                                        + Timestamps.monthOfYearMicros(month, l)
                                        + (day - 1) * Timestamps.DAY_MICROS
                                        + hour * Timestamps.HOUR_MICROS
                                        + min * Timestamps.MINUTE_MICROS
                                        + sec * Timestamps.SECOND_MICROS
                                        + checkTimezoneTail(seq, p, lim);
                            }
                        } else {
                            // minute
                            ts = Timestamps.yearMicros(year, l)
                                    + Timestamps.monthOfYearMicros(month, l)
                                    + (day - 1) * Timestamps.DAY_MICROS
                                    + hour * Timestamps.HOUR_MICROS
                                    + min * Timestamps.MINUTE_MICROS;

                        }
                    } else {
                        // year + month + day + hour
                        ts = Timestamps.yearMicros(year, l)
                                + Timestamps.monthOfYearMicros(month, l)
                                + (day - 1) * Timestamps.DAY_MICROS
                                + hour * Timestamps.HOUR_MICROS;

                    }
                } else {
                    // year + month + day
                    ts = Timestamps.yearMicros(year, l)
                            + Timestamps.monthOfYearMicros(month, l)
                            + (day - 1) * Timestamps.DAY_MICROS;
                }
            } else {
                // year + month
                ts = (Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l));
            }
        } else {
            // year
            ts = (Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(1, l));
        }
        return ts;
    }

    private static long checkTimezoneTail(CharSequence seq, int p, int lim) throws NumericException {
        if (lim - p == 0) {
            return 0;
        }

        if (lim - p < 2) {
            checkChar(seq, p++, lim, 'Z');
            return 0;
        }

        if (checkLen(p, lim)) {
            int tzSign = parseSign(seq, p++);
            int hour = Numbers.parseInt(seq, p, p += 2);
            checkRange(hour, 0, 23);

            if (lim - p == 3) {
                // Optional : separator between hours and mins in timezone
                checkChar(seq, p++, lim, ':');
            }

            if (checkLenStrict(p, lim, 2)) {
                int min = Numbers.parseInt(seq, p, p + 2);
                checkRange(min, 0, 59);
                return tzSign*(hour * Timestamps.HOUR_MICROS + min * Timestamps.MINUTE_MICROS);
            } else {
                return tzSign*(hour * Timestamps.HOUR_MICROS);
            }
        }
        throw NumericException.INSTANCE;
    }

    private static int parseSign(CharSequence seq, int p) throws NumericException {
        int tzSign;
        switch (seq.charAt(p)) {
            case '+':
                tzSign = 1;
                break;
            case '-':
                tzSign = -1;
                break;
            default:
                throw NumericException.INSTANCE;
        }
        return tzSign;
    }

    public static long parseFloorPartialDate(CharSequence seq) throws NumericException {
        return parseFloorPartialDate(seq, 0, seq.length());
    }

    public static void subtract(LongList intervals, int divider) {
        IntervalUtils.invert(intervals, divider);
        IntervalUtils.intersectInplace(intervals, divider);
    }

    /**
     * Intersects two lists of intervals compacted in one list in place.
     * Intervals to be chronologically ordered and result list will be ordered as well.
     * 

* Treat a as 2 lists, * a: first from 0 to divider * b: from divider to the end of list * * @param concatenatedIntervals 2 lists of intervals concatenated in 1 */ static void intersectInplace(LongList concatenatedIntervals, int dividerIndex) { final int sizeA = dividerIndex / 2; final int sizeB = sizeA + (concatenatedIntervals.size() - dividerIndex) / 2; int aLower = 0; int intervalB = sizeA; int writePoint = 0; int aUpperSize = sizeB; int aUpper = sizeB; while ((aLower < sizeA || aUpper < aUpperSize) && intervalB < sizeB) { int intervalA = aUpper < aUpperSize ? aUpper : aLower; long aLo = concatenatedIntervals.getQuick(intervalA * 2); long aHi = concatenatedIntervals.getQuick(intervalA * 2 + 1); long bLo = concatenatedIntervals.getQuick(intervalB * 2); long bHi = concatenatedIntervals.getQuick(intervalB * 2 + 1); if (aHi < bLo) { // a fully above b // a loses if (aUpper < aUpperSize) { aUpper++; } else { aLower++; } } else if (aLo > bHi) { // a fully below b // b loses intervalB++; } else { if (aHi < bHi) { // b hanging lower than a // a loses if (aUpper < aUpperSize) { aUpper++; } else { aLower++; } } else { // otherwise a lower than b // a loses intervalB++; } assert writePoint <= aLower || writePoint >= sizeA; if (writePoint == aLower && aLower < sizeA) { // We cannot keep A position, it will be overwritten, hence intervalB++; is not possible // Copy a point to A area instead concatenatedIntervals.add(concatenatedIntervals.getQuick(writePoint * 2)); concatenatedIntervals.add(concatenatedIntervals.getQuick(writePoint * 2 + 1)); aUpperSize = concatenatedIntervals.size() / 2; aLower++; } writePoint = append(concatenatedIntervals, writePoint, Math.max(aLo, bLo), Math.min(aHi, bHi)); } } concatenatedIntervals.setPos(2 * writePoint); } static int append(LongList list, int writePoint, long lo, long hi) { if (writePoint > 0) { long prevHi = list.getQuick(2 * writePoint - 1) + 1; if (prevHi >= lo) { list.setQuick(2 * writePoint - 1, hi); return writePoint; } } list.extendAndSet(2 * writePoint + 1, hi); list.setQuick(2 * writePoint, lo); return writePoint + 1; } private static void parseRange(CharSequence seq, int lo, int p, int lim, int position, short operation, LongList out) throws SqlException { char type = seq.charAt(lim - 1); int period; try { period = Numbers.parseInt(seq, p + 1, lim - 1); } catch (NumericException e) { throw SqlException.$(position, "Range not a number"); } try { int index = out.size(); parseInterval(seq, lo, p, operation, out); long low = getEncodedPeriodLo(out, index); long hi = getEncodedPeriodHi(out, index); replaceHiLoInterval(low, Timestamps.addPeriod(hi, type, period), operation, out); return; } catch (NumericException ignore) { // try date instead } try { long loMillis = TimestampFormatUtils.tryParse(seq, lo, p); addHiLoInterval(loMillis, Timestamps.addPeriod(loMillis, type, period), operation, out); } catch (NumericException e) { throw SqlException.invalidDate(position); } } private static void addMillisInterval(long period, int count, LongList out) { int k = out.size(); long lo = out.getQuick(k - 2); long hi = out.getQuick(k - 1); int writePoint = k / 2; for (int i = 0, n = count - 1; i < n; i++) { lo += period; hi += period; writePoint = append(out, writePoint, lo, hi); } } private static void addMonthInterval(int period, int count, LongList out) { int k = out.size(); long lo = out.getQuick(k - 2); long hi = out.getQuick(k - 1); int writePoint = k / 2; for (int i = 0, n = count - 1; i < n; i++) { lo = Timestamps.addMonths(lo, period); hi = Timestamps.addMonths(hi, period); writePoint = append(out, writePoint, lo, hi); } } private static void addYearIntervals(int period, int count, LongList out) { int k = out.size(); long lo = out.getQuick(k - 2); long hi = out.getQuick(k - 1); int writePoint = k / 2; for (int i = 0, n = count - 1; i < n; i++) { lo = Timestamps.addYear(lo, period); hi = Timestamps.addYear(hi, period); writePoint = append(out, writePoint, lo, hi); } } private static boolean checkLen(int p, int lim) throws NumericException { if (lim - p > 2) { return true; } if (lim <= p) { return false; } throw NumericException.INSTANCE; } private static boolean checkLenStrict(int p, int lim, int len) throws NumericException { if (lim - p == len) { return true; } if (lim <= p) { return false; } throw NumericException.INSTANCE; } private static void checkChar(CharSequence s, int p, int lim, char c) throws NumericException { if (p >= lim || s.charAt(p) != c) { throw NumericException.INSTANCE; } } private static void checkChar(CharSequence s, int p, int lim, char c1, char c2) throws NumericException { if (p >= lim || (s.charAt(p) != c1 && s.charAt(p) != c2)) { throw NumericException.INSTANCE; } } private static void checkRange(int x, int min, int max) throws NumericException { if (x < min || x > max) { throw NumericException.INSTANCE; } } /** * Inverts intervals. This method also produces inclusive edges that differ from source ones by 1 milli. * * @param intervals collection of intervals */ static void invert(LongList intervals) { invert(intervals, 0); } /** * Inverts intervals. This method also produces inclusive edges that differ from source ones by 1 milli. * * @param intervals collection of intervals */ static void invert(LongList intervals, int startIndex) { long last = Long.MIN_VALUE; int n = intervals.size(); int writeIndex = startIndex; for (int i = startIndex; i < n; i += 2) { final long lo = intervals.getQuick(i); final long hi = intervals.getQuick(i + 1); if (lo > last) { intervals.setQuick(writeIndex, last); intervals.setQuick(writeIndex + 1, lo - 1); writeIndex += 2; } last = hi + 1; } // If last hi was Long.MAX_VALUE then last will be Long.MIN_VALUE after +1 overflow if (last != Long.MIN_VALUE) { intervals.extendAndSet(writeIndex + 1, Long.MAX_VALUE); intervals.setQuick(writeIndex, last); writeIndex += 2; } intervals.setPos(writeIndex); } public static boolean isInIntervals(LongList intervals, long timestamp) { int left = 0; int right = intervals.size() / 2 - 1; while (left <= right) { int mid = (left + right) / 2; long lo = getEncodedPeriodLo(intervals, mid * 2); long hi = getEncodedPeriodHi(intervals, mid * 2); if (lo > timestamp) { right = mid - 1; } else if (hi < timestamp) { left = mid + 1; } else { return true; } } return false; } public static void parseIntervalEx(CharSequence seq, int lo, int lim, int position, LongList out, short operation) throws SqlException { int writeIndex = out.size(); int[] pos = new int[3]; int p = -1; for (int i = lo; i < lim; i++) { if (seq.charAt(i) == ';') { if (p > 1) { throw SqlException.$(position, "Invalid interval format"); } pos[++p] = i; } } switch (p) { case -1: // no semicolons, just date part, which can be interval in itself try { parseInterval(seq, lo, lim, operation, out); break; } catch (NumericException ignore) { // this must be a date then? } try { long millis = TimestampFormatUtils.tryParse(seq, lo, lim); addHiLoInterval(millis, millis, operation, out); break; } catch (NumericException e) { throw SqlException.$(position, "Not a date"); } case 0: // single semicolon, expect period format after date parseRange(seq, lo, pos[0], lim, position, operation, out); break; case 2: // 2018-01-10T10:30:00.000Z;30m;2d;2 // means 10:30-11:00 every second day starting 2018-01-10 int period; try { period = Numbers.parseInt(seq, pos[1] + 1, pos[2] - 1); } catch (NumericException e) { throw SqlException.$(position, "Period not a number"); } int count; try { count = Numbers.parseInt(seq, pos[2] + 1, lim); } catch (NumericException e) { throw SqlException.$(position, "Count not a number"); } parseRange(seq, lo, pos[0], pos[1], position, operation, out); char type = seq.charAt(pos[2] - 1); long low = getEncodedPeriodLo(out, writeIndex); long hi = getEncodedPeriodHi(out, writeIndex); replaceHiLoInterval(low, hi, period, type, count, operation, out); switch (type) { case 'y': case 'M': case 'h': case 'm': case 's': case 'd': break; default: throw SqlException.$(position, "Unknown period: " + type + " at " + (p - 1)); } break; default: throw SqlException.$(position, "Invalid interval format"); } } private static void replaceHiLoInterval(long lo, long hi, int period, char periodType, int periodCount, short operation, LongList out) { int lastIndex = out.size() - 4; out.setQuick(lastIndex, lo); out.setQuick(lastIndex + HI_INDEX, hi); out.setQuick(lastIndex + OPERATION_PERIOD_TYPE_ADJUSTMENT_INDEX, Numbers.encodeLowHighInts( Numbers.encodeLowHighShorts(operation, (short) ((int) periodType + Short.MIN_VALUE)), 0)); out.setQuick(lastIndex + PERIOD_COUNT_INDEX, Numbers.encodeLowHighInts(period, periodCount)); } private static void replaceHiLoInterval(long lo, long hi, short operation, LongList out) { replaceHiLoInterval(lo, hi, 0, (char) 0, 1, operation, out); } public static void parseInterval(CharSequence seq, int pos, int lim, short operation, LongList out) throws NumericException { if (lim - pos < 4) { throw NumericException.INSTANCE; } int p = pos; int year = Numbers.parseInt(seq, p, p += 4); boolean l = Timestamps.isLeapYear(year); if (checkLen(p, lim)) { checkChar(seq, p++, lim, '-'); int month = Numbers.parseInt(seq, p, p += 2); checkRange(month, 1, 12); if (checkLen(p, lim)) { checkChar(seq, p++, lim, '-'); int day = Numbers.parseInt(seq, p, p += 2); checkRange(day, 1, Timestamps.getDaysPerMonth(month, l)); if (checkLen(p, lim)) { checkChar(seq, p++, lim, 'T'); int hour = Numbers.parseInt(seq, p, p += 2); checkRange(hour, 0, 23); if (checkLen(p, lim)) { checkChar(seq, p++, lim, ':'); int min = Numbers.parseInt(seq, p, p += 2); checkRange(min, 0, 59); if (checkLen(p, lim)) { checkChar(seq, p++, lim, ':'); int sec = Numbers.parseInt(seq, p, p += 2); checkRange(sec, 0, 59); if (p < lim) { throw NumericException.INSTANCE; } else { // seconds addHiLoInterval(Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + (day - 1) * Timestamps.DAY_MICROS + hour * Timestamps.HOUR_MICROS + min * Timestamps.MINUTE_MICROS + sec * Timestamps.SECOND_MICROS, Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + (day - 1) * Timestamps.DAY_MICROS + hour * Timestamps.HOUR_MICROS + min * Timestamps.MINUTE_MICROS + sec * Timestamps.SECOND_MICROS + 999999, operation, out); } } else { // minute addHiLoInterval(Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + (day - 1) * Timestamps.DAY_MICROS + hour * Timestamps.HOUR_MICROS + min * Timestamps.MINUTE_MICROS, Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + (day - 1) * Timestamps.DAY_MICROS + hour * Timestamps.HOUR_MICROS + min * Timestamps.MINUTE_MICROS + 59 * Timestamps.SECOND_MICROS + 999999, operation, out); } } else { // year + month + day + hour addHiLoInterval(Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + (day - 1) * Timestamps.DAY_MICROS + hour * Timestamps.HOUR_MICROS, Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + (day - 1) * Timestamps.DAY_MICROS + hour * Timestamps.HOUR_MICROS + 59 * Timestamps.MINUTE_MICROS + 59 * Timestamps.SECOND_MICROS + 999999, operation, out); } } else { // year + month + day addHiLoInterval(Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + (day - 1) * Timestamps.DAY_MICROS, Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + +(day - 1) * Timestamps.DAY_MICROS + 23 * Timestamps.HOUR_MICROS + 59 * Timestamps.MINUTE_MICROS + 59 * Timestamps.SECOND_MICROS + 999999, operation, out); } } else { // year + month addHiLoInterval(Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l), Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(month, l) + (Timestamps.getDaysPerMonth(month, l) - 1) * Timestamps.DAY_MICROS + 23 * Timestamps.HOUR_MICROS + 59 * Timestamps.MINUTE_MICROS + 59 * Timestamps.SECOND_MICROS + 999999, operation, out); } } else { // year addHiLoInterval(Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(1, l), Timestamps.yearMicros(year, l) + Timestamps.monthOfYearMicros(12, l) + (Timestamps.getDaysPerMonth(12, l) - 1) * Timestamps.DAY_MICROS + 23 * Timestamps.HOUR_MICROS + 59 * Timestamps.MINUTE_MICROS + 59 * Timestamps.SECOND_MICROS + 999999, operation, out); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy