/** * 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.hadoop.hive.ql.util; import java.sql.Date; import java.sql.Timestamp; import java.util.Calendar; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import org.apache.hadoop.hive.common.type.HiveIntervalYearMonth; import org.apache.hadoop.hive.common.type.HiveIntervalDayTime; import org.apache.hadoop.hive.serde2.io.DateWritable; import org.apache.hive.common.util.DateUtils; public class DateTimeMath { private static class NanosResult { public int seconds; public int nanos; public void addNanos(int leftNanos, int rightNanos) { seconds = 0; nanos = leftNanos + rightNanos; if (nanos < 0) { seconds = -1; nanos += DateUtils.NANOS_PER_SEC; } else if (nanos >= DateUtils.NANOS_PER_SEC) { seconds = 1; nanos -= DateUtils.NANOS_PER_SEC; } } } protected Calendar calUtc = Calendar.getInstance(TimeZone.getTimeZone("UTC")); protected Calendar calLocal = Calendar.getInstance(); protected NanosResult nanosResult = new NanosResult(); // // Operations involving/returning year-month intervals // /** * Perform month arithmetic to millis value using UTC time zone. * @param millis * @param months * @return */ public long addMonthsToMillisUtc(long millis, int months) { calUtc.setTimeInMillis(millis); calUtc.add(Calendar.MONTH, months); return calUtc.getTimeInMillis(); } /** * Perform month arithmetic to millis value using local time zone. * @param millis * @param months * @return */ public long addMonthsToMillisLocal(long millis, int months) { calLocal.setTimeInMillis(millis); calLocal.add(Calendar.MONTH, months); return calLocal.getTimeInMillis(); } public long addMonthsToNanosUtc(long nanos, int months) { long result = addMonthsToMillisUtc(nanos / 1000000, months) * 1000000 + (nanos % 1000000); return result; } public long addMonthsToNanosLocal(long nanos, int months) { long result = addMonthsToMillisLocal(nanos / 1000000, months) * 1000000 + (nanos % 1000000); return result; } public long addMonthsToDays(long days, int months) { long millis = DateWritable.daysToMillis((int) days); millis = addMonthsToMillisLocal(millis, months); // Convert millis result back to days return DateWritable.millisToDays(millis); } public Timestamp add(Timestamp ts, HiveIntervalYearMonth interval) { if (ts == null || interval == null) { return null; } Timestamp tsResult = new Timestamp(0); add(ts, interval, tsResult); return tsResult; } public boolean add(Timestamp ts, HiveIntervalYearMonth interval, Timestamp result) { if (ts == null || interval == null) { return false; } // Attempt to match Oracle semantics for timestamp arithmetic, // where timestamp arithmetic is done in UTC, then converted back to local timezone long resultMillis = addMonthsToMillisUtc(ts.getTime(), interval.getTotalMonths()); result.setTime(resultMillis); result.setNanos(ts.getNanos()); return true; } public Timestamp add(HiveIntervalYearMonth interval, Timestamp ts) { if (ts == null || interval == null) { return null; } Timestamp tsResult = new Timestamp(0); add(interval, ts, tsResult); return tsResult; } public boolean add(HiveIntervalYearMonth interval, Timestamp ts, Timestamp result) { if (ts == null || interval == null) { return false; } // Attempt to match Oracle semantics for timestamp arithmetic, // where timestamp arithmetic is done in UTC, then converted back to local timezone long resultMillis = addMonthsToMillisUtc(ts.getTime(), interval.getTotalMonths()); result.setTime(resultMillis); result.setNanos(ts.getNanos()); return true; } public Date add(Date dt, HiveIntervalYearMonth interval) { if (dt == null || interval == null) { return null; } Date dtResult = new Date(0); add(dt, interval, dtResult); return dtResult; } public boolean add(Date dt, HiveIntervalYearMonth interval, Date result) { if (dt == null || interval == null) { return false; } // Since Date millis value is in local timezone representation, do date arithmetic // using local timezone so the time remains at the start of the day. long resultMillis = addMonthsToMillisLocal(dt.getTime(), interval.getTotalMonths()); result.setTime(resultMillis); return true; } public Date add(HiveIntervalYearMonth interval, Date dt) { if (dt == null || interval == null) { return null; } Date dtResult = new Date(0); add(interval, dt, dtResult); return dtResult; } public boolean add(HiveIntervalYearMonth interval, Date dt, Date result) { if (dt == null || interval == null) { return false; } // Since Date millis value is in local timezone representation, do date arithmetic // using local timezone so the time remains at the start of the day. long resultMillis = addMonthsToMillisLocal(dt.getTime(), interval.getTotalMonths()); result.setTime(resultMillis); return true; } public HiveIntervalYearMonth add(HiveIntervalYearMonth left, HiveIntervalYearMonth right) { HiveIntervalYearMonth result = null; if (left == null || right == null) { return null; } result = new HiveIntervalYearMonth(left.getTotalMonths() + right.getTotalMonths()); return result; } public Timestamp subtract(Timestamp left, HiveIntervalYearMonth right) { if (left == null || right == null) { return null; } Timestamp tsResult = new Timestamp(0); subtract(left, right, tsResult); return tsResult; } public boolean subtract(Timestamp left, HiveIntervalYearMonth right, Timestamp result) { if (left == null || right == null) { return false; } return add(left, right.negate(), result); } public Date subtract(Date left, HiveIntervalYearMonth right) { if (left == null || right == null) { return null; } Date dtResult = new Date(0); subtract(left, right, dtResult); return dtResult; } public boolean subtract(Date left, HiveIntervalYearMonth right, Date result) { if (left == null || right == null) { return false; } return add(left, right.negate(), result); } public HiveIntervalYearMonth subtract(HiveIntervalYearMonth left, HiveIntervalYearMonth right) { if (left == null || right == null) { return null; } return add(left, right.negate()); } // // Operations involving/returning day-time intervals // public Timestamp add(Timestamp ts, HiveIntervalDayTime interval) { if (ts == null || interval == null) { return null; } Timestamp tsResult = new Timestamp(0); add(ts, interval, tsResult); return tsResult; } public boolean add(Timestamp ts, HiveIntervalDayTime interval, Timestamp result) { if (ts == null || interval == null) { return false; } nanosResult.addNanos(ts.getNanos(), interval.getNanos()); long newMillis = ts.getTime() + TimeUnit.SECONDS.toMillis(interval.getTotalSeconds() + nanosResult.seconds); result.setTime(newMillis); result.setNanos(nanosResult.nanos); return true; } public Timestamp add(HiveIntervalDayTime interval, Timestamp ts) { if (ts == null || interval == null) { return null; } Timestamp tsResult = new Timestamp(0); add(interval, ts, tsResult); return tsResult; } public boolean add(HiveIntervalDayTime interval, Timestamp ts, Timestamp result) { if (ts == null || interval == null) { return false; } nanosResult.addNanos(ts.getNanos(), interval.getNanos()); long newMillis = ts.getTime() + TimeUnit.SECONDS.toMillis(interval.getTotalSeconds() + nanosResult.seconds); result.setTime(newMillis); result.setNanos(nanosResult.nanos); return true; } public HiveIntervalDayTime add(HiveIntervalDayTime left, HiveIntervalDayTime right) { if (left == null || right == null) { return null; } HiveIntervalDayTime result = new HiveIntervalDayTime(); add(left, right, result); return result; } public boolean add(HiveIntervalDayTime left, HiveIntervalDayTime right, HiveIntervalDayTime result) { if (left == null || right == null) { return false; } nanosResult.addNanos(left.getNanos(), right.getNanos()); long totalSeconds = left.getTotalSeconds() + right.getTotalSeconds() + nanosResult.seconds; result.set(totalSeconds, nanosResult.nanos); return true; } public Timestamp subtract(Timestamp left, HiveIntervalDayTime right) { if (left == null || right == null) { return null; } return add(left, right.negate()); } public boolean subtract(Timestamp left, HiveIntervalDayTime right, Timestamp result) { if (left == null || right == null) { return false; } return add(left, right.negate(), result); } public HiveIntervalDayTime subtract(HiveIntervalDayTime left, HiveIntervalDayTime right) { if (left == null || right == null) { return null; } return add(left, right.negate()); } public boolean subtract(HiveIntervalDayTime left, HiveIntervalDayTime right, HiveIntervalDayTime result) { if (left == null || right == null) { return false; } return add(left, right.negate(), result); } public HiveIntervalDayTime subtract(Timestamp left, Timestamp right) { if (left == null || right == null) { return null; } HiveIntervalDayTime result = new HiveIntervalDayTime(); subtract(left, right, result); return result; } public boolean subtract(Timestamp left, Timestamp right, HiveIntervalDayTime result) { if (left == null || right == null) { return false; } nanosResult.addNanos(left.getNanos(), -(right.getNanos())); long totalSeconds = TimeUnit.MILLISECONDS.toSeconds(left.getTime()) - TimeUnit.MILLISECONDS.toSeconds(right.getTime()) + nanosResult.seconds; result.set(totalSeconds, nanosResult.nanos); return true; } }