blob: 1aebd7872c1f5b08f028469740b1bb944a56aed5 [file] [log] [blame]
// 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.
#ifndef IMPALA_RUNTIME_TIMESTAMP_VALUE_INLINE_H
#define IMPALA_RUNTIME_TIMESTAMP_VALUE_INLINE_H
#include "runtime/timestamp-value.h"
#include <boost/date_time/compiler_config.hpp>
#include <boost/date_time/posix_time/conversion.hpp>
#include <chrono>
#include "exprs/timezone_db.h"
#include "kudu/util/int128.h"
#include "gutil/walltime.h"
#include "util/arithmetic-util.h"
namespace impala {
template <int32_t TICKS_PER_SEC>
inline TimestampValue TimestampValue::UtcFromUnixTimeTicks(int64_t unix_time_ticks) {
static const boost::gregorian::date EPOCH(1970,1,1);
int64_t days = SplitTime<(uint64_t)TICKS_PER_SEC*SECONDS_PER_DAY>(&unix_time_ticks);
return TimestampValue(EPOCH + boost::gregorian::date_duration(days),
boost::posix_time::nanoseconds(unix_time_ticks*(NANOS_PER_SEC/TICKS_PER_SEC)));
}
inline TimestampValue TimestampValue::UtcFromUnixTimeMicros(int64_t unix_time_micros) {
return UtcFromUnixTimeTicks<MICROS_PER_SEC>(unix_time_micros);
}
inline TimestampValue TimestampValue::FromUnixTimeMicros(int64_t unix_time_micros,
const Timezone& local_tz) {
int64_t ts_seconds = SplitTime<MICROS_PER_SEC>(&unix_time_micros);
TimestampValue result = FromUnixTime(ts_seconds, local_tz);
if (result.HasDate()) result.time_ += boost::posix_time::microseconds(unix_time_micros);
return result;
}
inline TimestampValue TimestampValue::UtcFromUnixTimeMillis(int64_t unix_time_millis) {
return UtcFromUnixTimeTicks<MILLIS_PER_SEC>(unix_time_millis);
}
inline TimestampValue TimestampValue::FromSubsecondUnixTime(
double unix_time, const Timezone& local_tz) {
int64_t unix_time_whole = unix_time;
int64_t nanos = (unix_time - unix_time_whole) / ONE_BILLIONTH;
return FromUnixTimeNanos(unix_time_whole, nanos, local_tz);
}
inline TimestampValue TimestampValue::UtcFromUnixTimeLimitedRangeNanos(
int64_t unix_time_nanos) {
return UtcFromUnixTimeTicks<NANOS_PER_SEC>(unix_time_nanos);
}
inline TimestampValue TimestampValue::FromUnixTimeNanos(time_t unix_time, int64_t nanos,
const Timezone& local_tz) {
unix_time =
ArithmeticUtil::AsUnsigned<std::plus>(unix_time, SplitTime<NANOS_PER_SEC>(&nanos));
TimestampValue result = FromUnixTime(unix_time, local_tz);
// 'nanos' is guaranteed to be between [0,NANOS_PER_SEC) at this point, so the
// next addition cannot change the day or step to a different timezone.
if (result.HasDate()) {
DCHECK_GE(nanos, 0);
DCHECK_LT(nanos, NANOS_PER_SEC);
result.time_ += boost::posix_time::nanoseconds(nanos);
}
return result;
}
inline TimestampValue TimestampValue::FromDaysSinceUnixEpoch(int64_t days) {
static const boost::gregorian::date EPOCH(1970, 1, 1);
static const boost::posix_time::time_duration t(0, 0, 0);
return TimestampValue(EPOCH + boost::gregorian::date_duration(days), t);
}
inline int64_t TimestampValue::DaysSinceUnixEpoch() const {
DCHECK(HasDate());
static const boost::gregorian::date epoch(1970,1,1);
return (date_ - epoch).days();
}
/// Interpret 'this' as a timestamp in UTC and convert to unix time.
/// Returns false if the conversion failed ('unix_time' will be undefined), otherwise
/// true.
inline bool TimestampValue::UtcToUnixTime(time_t* unix_time) const {
DCHECK(unix_time != nullptr);
if (UNLIKELY(!HasDateAndTime())) return false;
*unix_time = DaysSinceUnixEpoch() * SECONDS_PER_DAY + time_.total_seconds();
return true;
}
/// Interpret 'this' as a timestamp in UTC and convert to unix time in microseconds.
/// Nanoseconds are rounded to the nearest microsecond supported by Impala. Returns
/// false if the conversion failed ('unix_time_micros' will be undefined), otherwise
/// true.
inline bool TimestampValue::UtcToUnixTimeMicros(int64_t* unix_time_micros) const {
const static int64_t MAX_UNIXTIME = 253402300799; // 9999-12-31 23:59:59
const static int64_t MAX_UNIXTIME_MICROS =
MAX_UNIXTIME * MICROS_PER_SEC + (MICROS_PER_SEC - 1);
DCHECK(unix_time_micros != nullptr);
time_t unixtime_seconds;
if (UNLIKELY(!UtcToUnixTime(&unixtime_seconds))) return false;
DCHECK(HasTime());
*unix_time_micros =
(static_cast<int64_t>(unixtime_seconds) * MICROS_PER_SEC) +
((time_.fractional_seconds() + (NANOS_PER_MICRO / 2)) / NANOS_PER_MICRO);
// Rounding may result in the timestamp being MAX_UNIXTIME_MICROS+1 and should be
// truncated.
DCHECK_LE(*unix_time_micros, MAX_UNIXTIME_MICROS + 1);
*unix_time_micros = std::min(MAX_UNIXTIME_MICROS, *unix_time_micros);
return true;
}
inline bool TimestampValue::FloorUtcToUnixTimeMicros(int64_t* unix_time_micros) const {
DCHECK(unix_time_micros != nullptr);
if (UNLIKELY(!HasDateAndTime())) return false;
*unix_time_micros = DaysSinceUnixEpoch() * SECONDS_PER_DAY * MICROS_PER_SEC
+ time_.total_microseconds();
return true;
}
inline bool TimestampValue::FloorUtcToUnixTimeMillis(int64_t* unix_time_millis) const {
DCHECK(unix_time_millis != nullptr);
if (UNLIKELY(!HasDateAndTime())) return false;
*unix_time_millis = DaysSinceUnixEpoch() * SECONDS_PER_DAY * MILLIS_PER_SEC
+ time_.total_milliseconds();
return true;
}
inline bool TimestampValue::UtcToUnixTimeLimitedRangeNanos(
int64_t* unix_time_nanos) const {
DCHECK(unix_time_nanos != nullptr);
time_t unixtime_seconds;
if (UNLIKELY(!UtcToUnixTime(&unixtime_seconds))) return false;
DCHECK(HasTime());
// TODO: consider optimizing this (IMPALA-8268)
kudu::int128_t nanos128 =
static_cast<kudu::int128_t>(unixtime_seconds) * NANOS_PER_SEC
+ time_.fractional_seconds();
if (nanos128 < std::numeric_limits<int64_t>::min()
|| nanos128 > std::numeric_limits<int64_t>::max()) {
return false;
}
*unix_time_nanos = static_cast<int64_t>(nanos128);
return true;
}
/// Converts to Unix time (seconds since the Unix epoch) representation. The time
/// zone interpretation of the TimestampValue instance is determined by
/// FLAGS_use_local_tz_for_unix_timestamp_conversions. If the flag is true, the
/// instance is interpreted as a value in the 'local_tz' time zone. If the flag is false,
/// UTC is assumed.
/// Returns false if the conversion failed (unix_time will be undefined), otherwise
/// true.
inline bool TimestampValue::ToUnixTime(const Timezone& local_tz,
time_t* unix_time) const {
DCHECK(unix_time != nullptr);
if (UNLIKELY(!HasDateAndTime())) return false;
if (!FLAGS_use_local_tz_for_unix_timestamp_conversions) return UtcToUnixTime(unix_time);
cctz::civil_second cs(date_.year(), date_.month(), date_.day(), time_.hours(),
time_.minutes(), time_.seconds());
cctz::time_point<cctz::sys_seconds> tp = cctz::convert(cs, local_tz);
cctz::sys_seconds seconds = tp.time_since_epoch();
*unix_time = seconds.count();
return true;
}
/// Converts to Unix time with fractional seconds. The time zone interpretation of the
/// TimestampValue instance is determined as above.
/// Returns false if the conversion failed (unix_time will be undefined), otherwise
/// true.
inline bool TimestampValue::ToSubsecondUnixTime(const Timezone& local_tz,
double* unix_time) const {
DCHECK(unix_time != nullptr);
time_t temp;
if (UNLIKELY(!ToUnixTime(local_tz, &temp))) return false;
*unix_time = static_cast<double>(temp);
DCHECK(HasTime());
*unix_time += time_.fractional_seconds() * ONE_BILLIONTH;
return true;
}
}
#endif