blob: 535eb5ecb7e4bc9851d89831a0349932be9527b8 [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.
#include <algorithm>
#include <cstring>
#include <boost/assign/list_of.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include "common/status.h"
#include "exprs/timezone_db.h"
#include "runtime/datetime-simple-date-format-parser.h"
#include "runtime/raw-value.inline.h"
#include "runtime/timestamp-value.h"
#include "runtime/timestamp-value.inline.h"
#include "testutil/gtest-util.h"
#include "util/string-parser.h"
#include "common/names.h"
using boost::assign::list_of;
using boost::date_time::Dec;
using boost::date_time::not_a_date_time;
using boost::gregorian::date;
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
namespace impala {
using namespace datetime_parse_util;
// Used for defining a custom date/time format test. The structure can be used to
// indicate whether the format or value is expected to fail. In a happy path test,
// the values for year, month, day etc will be validated against the parsed result.
// Further validation will also be performed if the should_format flag is enabled,
// whereby the parsed date/time will be translated back to a string and checked
// against the expected value.
struct TimestampTC {
const char* fmt;
const char* str;
bool fmt_should_fail;
bool str_should_fail;
bool should_format;
int expected_year;
int expected_month;
int expected_day;
int expected_hours;
int expected_minutes;
int expected_seconds;
int expected_fraction;
bool fmt_has_date_toks;
bool fmt_has_time_toks;
TimestampTC(const char* fmt, const char* str, bool fmt_should_fail = true,
bool str_should_fail = true)
: fmt(fmt),
str(str),
fmt_should_fail(fmt_should_fail),
str_should_fail(str_should_fail),
should_format(true),
expected_year(0),
expected_month(0),
expected_day(0),
expected_hours(0),
expected_minutes(0),
expected_seconds(0),
expected_fraction(0),
fmt_has_date_toks(false),
fmt_has_time_toks(false) {
}
TimestampTC(const char* fmt, const char* str, bool should_format,
bool fmt_has_date_toks, bool fmt_has_time_toks, int expected_year,
int expected_month, int expected_day, int expected_hours = 0,
int expected_minutes = 0, int expected_seconds = 0,
int expected_fraction = 0)
: fmt(fmt),
str(str),
fmt_should_fail(false),
str_should_fail(false),
should_format(should_format),
expected_year(expected_year),
expected_month(expected_month),
expected_day(expected_day),
expected_hours(expected_hours),
expected_minutes(expected_minutes),
expected_seconds(expected_seconds),
expected_fraction(expected_fraction),
fmt_has_date_toks(fmt_has_date_toks),
fmt_has_time_toks(fmt_has_time_toks) {
}
};
// Used to test custom date/time output test cases i.e. timestamp value -> string.
struct TimestampFormatTC {
const long ts;
const char* fmt;
const char* str;
bool should_fail;
TimestampFormatTC(long ts, const char* fmt, const char* str, bool should_fail = false)
: ts(ts),
fmt(fmt),
str(str),
should_fail(should_fail) {
}
};
// Used to represent a parsed timestamp token. For example, it may represent a year.
struct TimestampToken {
const char* fmt;
int val;
const char* str;
TimestampToken(const char* fmt, int val)
: fmt(fmt),
val(val),
str(NULL) {
}
TimestampToken(const char* fmt, int val, const char* str)
: fmt(fmt),
val(val),
str(str) {
}
friend bool operator<(const TimestampToken& lhs, const TimestampToken& rhs) {
return strcmp(lhs.fmt, rhs.fmt) < 0;
}
};
inline void ValidateTimestamp(TimestampValue& tv, string& fmt, string& val,
string& fmt_val, int year, int month, int day, int hours, int mins, int secs,
int frac) {
boost::gregorian::date not_a_date;
boost::gregorian::date cust_date = tv.date();
boost::posix_time::time_duration cust_time = tv.time();
EXPECT_NE(not_a_date, cust_date) << fmt_val;
EXPECT_NE(not_a_date_time, cust_time) << fmt_val;
EXPECT_EQ(year, cust_date.year()) << fmt_val;
EXPECT_EQ(month, cust_date.month()) << fmt_val;
EXPECT_EQ(day, cust_date.day()) << fmt_val;
EXPECT_EQ(hours, cust_time.hours()) << fmt_val;
EXPECT_EQ(mins, cust_time.minutes()) << fmt_val;
EXPECT_EQ(secs, cust_time.seconds()) << fmt_val;
EXPECT_EQ(frac, cust_time.fractional_seconds()) << fmt_val;
}
// This function will generate all permutations of tokens to test that the parsing and
// formatting is correct (position of tokens should be irrelevant). Note that separators
// are also combined with EACH token permutation to get the widest coverage on formats.
// This forces out the parsing and format logic edge cases.
void TestTimestampTokens(vector<TimestampToken>* toks, int year, int month,
int day, int hours, int mins, int secs, int frac) {
const char* SEPARATORS = " ~!@%^&*_+-:;|\\,./";
int toks_len = toks->size();
sort(toks->begin(), toks->end());
string fmt;
string val;
do {
// Validate we can parse date/time raw tokens (no separators)
{
for (int i = 0; i < toks_len; ++i) {
fmt.append((*toks)[i].fmt);
if ((*toks)[i].str != NULL) {
val.append(string((*toks)[i].str));
} else {
val.append(lexical_cast<string>((*toks)[i].val));
}
}
string fmt_val = "Format: " + fmt + ", Val: " + val;
DateTimeFormatContext dt_ctx(fmt.c_str());
ASSERT_TRUE(SimpleDateFormatTokenizer::Tokenize(&dt_ctx)) << fmt_val;
TimestampValue tv =
TimestampValue::ParseSimpleDateFormat(val.c_str(), val.length(), dt_ctx);
ValidateTimestamp(tv, fmt, val, fmt_val, year, month, day, hours, mins, secs,
frac);
string buff = tv.Format(dt_ctx);
EXPECT_TRUE(!buff.empty()) << fmt_val;
EXPECT_LE(buff.length(), dt_ctx.fmt_len) << fmt_val;
EXPECT_EQ(buff, val) << fmt_val << " " << buff;
fmt.clear();
val.clear();
}
// Validate we can parse date/time with separators
{
for (const char* separator = SEPARATORS; *separator != 0;
++separator) {
for (int i = 0; i < toks_len; ++i) {
fmt.append((*toks)[i].fmt);
if (i + 1 < toks_len) fmt.push_back(*separator);
if ((*toks)[i].str != NULL) {
val.append(string((*toks)[i].str));
} else {
val.append(lexical_cast<string>((*toks)[i].val));
}
if (i + 1 < toks_len) val.push_back(*separator);
}
string fmt_val = "Format: " + fmt + ", Val: " + val;
DateTimeFormatContext dt_ctx(fmt.c_str());
ASSERT_TRUE(SimpleDateFormatTokenizer::Tokenize(&dt_ctx)) << fmt_val;
TimestampValue tv =
TimestampValue::ParseSimpleDateFormat(val.c_str(), val.length(), dt_ctx);
ValidateTimestamp(tv, fmt, val, fmt_val, year, month, day, hours, mins, secs,
frac);
string buff = tv.Format(dt_ctx);
EXPECT_TRUE(!buff.empty()) << fmt_val;
EXPECT_LE(buff.length(), dt_ctx.fmt_len) << fmt_val;
EXPECT_EQ(buff, val) << fmt_val << " " << buff;
fmt.clear();
val.clear();
}
}
} while (next_permutation(toks->begin(), toks->end()));
}
// Checks that FromSubsecondUnixTime gives the correct result. Conversion to double
// is lossy so the result is expected to be within a certain range of 'expected'.
// The fraction part of 'nanos' can express sub-nanoseconds - the current logic is to
// truncate to the next whole nanosecond (towards 0).
void TestFromDoubleUnixTime(
int64_t seconds, int64_t millis, double nanos, const TimestampValue& expected) {
auto tz = TimezoneDatabase::GetUtcTimezone();
TimestampValue from_double = TimestampValue::FromSubsecondUnixTime(
1.0 * seconds + 0.001 * millis + nanos / NANOS_PER_SEC, tz);
if (!expected.HasDate()) EXPECT_FALSE(from_double.HasDate());
else {
// Conversion to double is lossy so the timestamp can be a bit different.
int64_t expected_rounded_to_micros, from_double_rounded_to_micros;
EXPECT_TRUE(expected.UtcToUnixTimeMicros(&expected_rounded_to_micros));
EXPECT_TRUE(from_double.UtcToUnixTimeMicros(&from_double_rounded_to_micros));
// The difference can be more than a microsec in case of timestamps far from 1970.
int64_t MARGIN_OF_ERROR = 8;
EXPECT_LT(abs(expected_rounded_to_micros - from_double_rounded_to_micros),
MARGIN_OF_ERROR);
}
}
// Checks that all sub-second From*UnixTime gives the same result and that the result
// is the same as 'expected'.
// If 'expected' is nullptr then the result is expected to be invalid (out of range).
void TestFromSubSecondFunctions(int64_t seconds, int64_t millis, const char* expected) {
auto tz = TimezoneDatabase::GetUtcTimezone();
TimestampValue from_millis =
TimestampValue::UtcFromUnixTimeMillis(seconds * 1000 + millis);
if (expected == nullptr) EXPECT_FALSE(from_millis.HasDate());
else EXPECT_EQ(expected, from_millis.ToString());
EXPECT_EQ(from_millis, TimestampValue::UtcFromUnixTimeMicros(
seconds * MICROS_PER_SEC + millis * 1000));
EXPECT_EQ(from_millis, TimestampValue::FromUnixTimeMicros(
seconds * MICROS_PER_SEC + millis * 1000, tz));
// Check the same timestamp shifted with some sub-nanosecs.
vector<double> sub_nanosec_offsets =
{0.0, 0.1, 0.9, 0.000001, 0.999999, 2.2250738585072020e-308};
for (double sub_nanos: sub_nanosec_offsets) {
TestFromDoubleUnixTime(seconds, millis, sub_nanos, from_millis);
TestFromDoubleUnixTime(seconds, millis, -sub_nanos, from_millis);
}
// Test FromUnixTimeNanos with shifted sec + subsec pairs.
vector<int64_t> signs = {-1, 1};
vector<int64_t> offsets = {0, 1, 2, 60, 60*60, 24*60*60};
for (int64_t sign: signs) {
for (int64_t offset: offsets) {
int64_t shifted_seconds = seconds + sign * offset;
int64_t shifted_nanos = (millis - 1000 * sign * offset) * 1000 * 1000;
EXPECT_EQ(from_millis,
TimestampValue::FromUnixTimeNanos(shifted_seconds, shifted_nanos, tz));
}
}
// Test UtcFromUnixTimeLimitedRangeNanos only for timestamps that fit to its range.
int128_t total_nanos = int128_t {seconds} * NANOS_PER_SEC + millis * 1000 * 1000;
if (std::numeric_limits<int64_t>::min() >= total_nanos &&
std::numeric_limits<int64_t>::max() <= total_nanos) {
EXPECT_EQ(from_millis,
TimestampValue::UtcFromUnixTimeLimitedRangeNanos((int64_t)total_nanos));
}
}
// Convenience functions for TimestampValue->Unix time conversion that assume that
// the conversion is successful.
int64_t FloorToSeconds(const TimestampValue& ts) {
EXPECT_TRUE(ts.HasDateAndTime());
int64_t result = 0;
EXPECT_TRUE(ts.UtcToUnixTime(&result));
return result;
}
int64_t RoundToMicros(const TimestampValue& ts) {
EXPECT_TRUE(ts.HasDateAndTime());
int64_t result = 0;
EXPECT_TRUE(ts.UtcToUnixTimeMicros(&result));
return result;
}
int64_t FloorToMicros(const TimestampValue& ts) {
EXPECT_TRUE(ts.HasDateAndTime());
int64_t result = 0;
EXPECT_TRUE(ts.FloorUtcToUnixTimeMicros(&result));
return result;
}
int64_t FloorToMillis(const TimestampValue& ts) {
EXPECT_TRUE(ts.HasDateAndTime());
int64_t result = 0;
EXPECT_TRUE(ts.FloorUtcToUnixTimeMillis(&result));
return result;
}
TEST(TimestampTest, Basic) {
// Fix current time to determine the behavior parsing 2-digit year format
// Set it to 03/01 to test 02/29 edge cases.
TimestampValue now(date(1980, 3, 1), time_duration(16, 14, 24));
Timezone utc_tz = cctz::utc_time_zone();
char s1[] = "2012-01-20 01:10:01";
char s2[] = "1990-10-20 10:10:10.123456789 ";
char s3[] = " 1990-10-20 10:10:10.123456789";
TimestampValue v1 = TimestampValue::ParseSimpleDateFormat(s1, strlen(s1));
TimestampValue v2 = TimestampValue::ParseSimpleDateFormat(s2, strlen(s2));
TimestampValue v3 = TimestampValue::ParseSimpleDateFormat(s3, strlen(s3));
EXPECT_EQ(v1.date().year(), 2012);
EXPECT_EQ(v1.date().month(), 1);
EXPECT_EQ(v1.date().day(), 20);
EXPECT_EQ(v1.time().hours(), 1);
EXPECT_EQ(v1.time().minutes(), 10);
EXPECT_EQ(v1.time().seconds(), 1);
EXPECT_EQ(v1.time().fractional_seconds(), 0);
EXPECT_EQ(v2.time().fractional_seconds(), 123456789);
EXPECT_NE(v1, v2);
EXPECT_EQ(v2, v3);
EXPECT_LT(v2, v1);
EXPECT_LE(v2, v1);
EXPECT_GT(v1, v2);
EXPECT_GE(v2, v3);
EXPECT_NE(RawValue::GetHashValue(&v1, TYPE_TIMESTAMP, 0),
RawValue::GetHashValue(&v2, TYPE_TIMESTAMP, 0));
EXPECT_EQ(RawValue::GetHashValue(&v3, TYPE_TIMESTAMP, 0),
RawValue::GetHashValue(&v2, TYPE_TIMESTAMP, 0));
char s4[] = "2012-01-20T01:10:01";
char s5[] = "1990-10-20T10:10:10.123456789";
TimestampValue v4 = TimestampValue::ParseSimpleDateFormat(s4, strlen(s4));
TimestampValue v5 = TimestampValue::ParseSimpleDateFormat(s5, strlen(s5));
EXPECT_EQ(v4.date().year(), 2012);
EXPECT_EQ(v4.date().month(), 1);
EXPECT_EQ(v4.date().day(), 20);
EXPECT_EQ(v4.time().hours(), 1);
EXPECT_EQ(v4.time().minutes(), 10);
EXPECT_EQ(v4.time().seconds(), 1);
EXPECT_EQ(v4.time().fractional_seconds(), 0);
EXPECT_EQ(v5.date().year(), 1990);
EXPECT_EQ(v5.date().month(), 10);
EXPECT_EQ(v5.date().day(), 20);
EXPECT_EQ(v5.time().hours(), 10);
EXPECT_EQ(v5.time().minutes(), 10);
EXPECT_EQ(v5.time().seconds(), 10);
EXPECT_EQ(v5.time().fractional_seconds(), 123456789);
// Test Dates and Times as timestamps.
char d1[] = "2012-01-20";
char d2[] = "1990-10-20";
TimestampValue dv1 = TimestampValue::ParseSimpleDateFormat(d1, strlen(d1));
TimestampValue dv2 = TimestampValue::ParseSimpleDateFormat(d2, strlen(d2));
EXPECT_NE(dv1, dv2);
EXPECT_LT(dv1, v1);
EXPECT_LE(dv1, v1);
EXPECT_GT(v1, dv1);
EXPECT_GE(v1, dv1);
EXPECT_NE(dv2, v2);
EXPECT_EQ(dv1.date().year(), 2012);
EXPECT_EQ(dv1.date().month(), 1);
EXPECT_EQ(dv1.date().day(), 20);
char t1[] = "10:11:12.123456789";
char t2[] = "00:00:00";
TimestampValue tv1 = TimestampValue::ParseSimpleDateFormat(t1, strlen(t1));
TimestampValue tv2 = TimestampValue::ParseSimpleDateFormat(t2, strlen(t2));
EXPECT_NE(tv1, tv2);
EXPECT_NE(tv1, v2);
EXPECT_EQ(tv1.time().hours(), 10);
EXPECT_EQ(tv1.time().minutes(), 11);
EXPECT_EQ(tv1.time().seconds(), 12);
EXPECT_EQ(tv1.time().fractional_seconds(), 123456789);
EXPECT_EQ(tv2.time().fractional_seconds(), 0);
// Test variable fraction lengths
const char* FRACTION_MAX_STR = "123456789";
const char* TEST_VALS[] = { "2013-12-10 12:04:17.", "2013-12-10T12:04:17.",
"12:04:17." };
const int TEST_VAL_CNT = sizeof(TEST_VALS) / sizeof(char*);
for (int i = 0; i < TEST_VAL_CNT; ++i) {
const int VAL_LEN = strlen(TEST_VALS[i]);
int fraction_len = strlen(FRACTION_MAX_STR);
char frac_buff[VAL_LEN + fraction_len + 1];
while (fraction_len > 0) {
memcpy(frac_buff, TEST_VALS[i], VAL_LEN);
memcpy(frac_buff + VAL_LEN, FRACTION_MAX_STR, fraction_len);
*(frac_buff + VAL_LEN + fraction_len) = '\0';
TimestampValue tv_frac =
TimestampValue::ParseSimpleDateFormat(frac_buff, strlen(frac_buff));
if (frac_buff[4] == '-') {
EXPECT_EQ(tv_frac.date().year(), 2013);
EXPECT_EQ(tv_frac.date().month(), 12);
EXPECT_EQ(tv_frac.date().day(), 10);
}
EXPECT_EQ(tv_frac.time().hours(), 12);
EXPECT_EQ(tv_frac.time().minutes(), 4);
EXPECT_EQ(tv_frac.time().seconds(), 17);
StringParser::ParseResult status;
int32_t fraction =
StringParser::StringToInt<int32_t>(FRACTION_MAX_STR, fraction_len, &status);
EXPECT_TRUE(StringParser::PARSE_SUCCESS == status);
for (int i = fraction_len; i < 9; ++i) fraction *= 10;
EXPECT_EQ(tv_frac.time().fractional_seconds(), fraction);
--fraction_len;
}
}
// Bad formats
char b1[] = "1990-10 10:10:10.123456789";
TimestampValue bv1 = TimestampValue::ParseSimpleDateFormat(b1, strlen(b1));
boost::gregorian::date not_a_date;
EXPECT_EQ(bv1.date(), not_a_date);
EXPECT_EQ(bv1.time(), not_a_date_time);
char b2[] = "1991-10-10 99:10:10.123456789";
TimestampValue bv2 = TimestampValue::ParseSimpleDateFormat(b2, strlen(b2));
EXPECT_EQ(bv2.time(), not_a_date_time);
EXPECT_EQ(bv2.date(), not_a_date);
char b3[] = "1990-10- 10:10:10.123456789";
TimestampValue bv3 = TimestampValue::ParseSimpleDateFormat(b3, strlen(b3));
EXPECT_EQ(bv3.date(), not_a_date);
EXPECT_EQ(bv3.time(), not_a_date_time);
char b4[] = "10:1010.123456789";
TimestampValue bv4 = TimestampValue::ParseSimpleDateFormat(b4, strlen(b4));
EXPECT_EQ(bv4.date(), not_a_date);
EXPECT_EQ(bv4.time(), not_a_date_time);
char b5[] = "10:11:12.123456 1991-10-10";
TimestampValue bv5 = TimestampValue::ParseSimpleDateFormat(b5, strlen(b5));
EXPECT_EQ(bv5.date(), not_a_date);
EXPECT_EQ(bv5.time(), not_a_date_time);
char b6[] = "2012-01-20 01:10:00.123.466";
TimestampValue bv6 = TimestampValue::ParseSimpleDateFormat(b6, strlen(b6));
EXPECT_EQ(bv6.date(), not_a_date);
EXPECT_EQ(bv6.time(), not_a_date_time);
char b7[] = "2012-01-20 01:10:00.123 477 ";
TimestampValue bv7 = TimestampValue::ParseSimpleDateFormat(b7, strlen(b7));
EXPECT_EQ(bv7.date(), not_a_date);
EXPECT_EQ(bv7.time(), not_a_date_time);
// Test custom formats by generating all permutations of tokens to check parsing and
// formatting is behaving correctly (position of tokens should be irrelevant). Note
// that separators are also combined with EACH token permutation to get the widest
// coverage on formats.
const int YEAR = 2013;
const int MONTH = 10;
const int DAY = 14;
const int HOURS = 14;
const int MINS = 25;
const int SECS = 44;
const int FRAC = 123456789;
// Test parsing/formatting with numeric date/time tokens
vector<TimestampToken> dt_toks = list_of
(TimestampToken("dd", DAY))(TimestampToken("MM", MONTH))
(TimestampToken("yyyy", YEAR))(TimestampToken("HH", HOURS))
(TimestampToken("mm", MINS))(TimestampToken("ss", SECS))
(TimestampToken("SSSSSSSSS", FRAC));
TestTimestampTokens(&dt_toks, YEAR, MONTH, DAY, HOURS, MINS, SECS, FRAC);
// Test literal months
const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"
};
// Test parsing/formatting of literal months (short)
const int MONTH_CNT = (sizeof(months) / sizeof(char**));
for (int i = 0; i < MONTH_CNT; ++i) {
// Test parsing/formatting with short literal months
vector<TimestampToken> dt_lm_toks = list_of
(TimestampToken("dd", DAY))(TimestampToken("MMM", i + 1, months[i]))
(TimestampToken("yyyy", YEAR))(TimestampToken("HH", HOURS))
(TimestampToken("mm", MINS))(TimestampToken("ss", SECS))
(TimestampToken("SSSSSSSSS", FRAC));
TestTimestampTokens(&dt_lm_toks, YEAR, i + 1, DAY, HOURS, MINS, SECS, FRAC);
}
// Test parsing/formatting of complex date/time formats
vector<TimestampTC> test_cases = boost::assign::list_of
// Test year upper/lower bound
(TimestampTC("yyyy-MM-dd HH:mm:ss", "1400-01-01 00:00:00",
false, true, false, 1400, 1, 1))
(TimestampTC("yyyy-MM-dd HH:mm:ss", "1399-12-31 23:59:59",
false, true))
(TimestampTC("yyyy-MM-dd HH:mm:ss", "9999-12-31 23:59:59",
false, true, false, 9999, 12, 31, 23, 59, 59))
(TimestampTC("yyyy-MM-dd HH:mm:ss +hh", "1400-01-01 01:00:00 +01", false, true, false,
1400, 1, 1, 0, 0, 0))
(TimestampTC("yyyy-MM-dd HH:mm:ss +hh", "1400-01-01 01:00:00 +02", false, true))
(TimestampTC("yyyy-MM-dd HH:mm:ss +hh", "9999-12-31 22:00:00 -01", false, true, false,
9999, 12, 31, 23, 0, 0))
(TimestampTC("yyyy-MM-dd HH:mm:ss +hh", "9999-12-31 22:00:00 -02", false, true))
// Test case on literal short months
(TimestampTC("yyyy-MMM-dd", "2013-OCT-01", false, true, false, 2013, 10, 1))
// Test case on literal short months
(TimestampTC("yyyy-MMM-dd", "2013-oct-01", false, true, false, 2013, 10, 1))
// Test case on literal short months
(TimestampTC("yyyy-MMM-dd", "2013-oCt-01", false, true, false, 2013, 10, 1))
// Test padding on numeric and literal tokens (short)
(TimestampTC("MMMyyyyyydd", "Apr00201309", true, true, false, 2013, 4, 9))
// Test duplicate tokens
(TimestampTC("yyyy MM dd ddMMMyyyy (HH:mm:ss.SSSS)",
"2013 05 12 16Apr1952 (16:53:21.1234)", false, true, true, 1952, 4, 16, 16,
53, 21, 123400000))
// Test missing separator on short date format
(TimestampTC("Myyd", "4139", true, true))
// Test bad year format
(TimestampTC("YYYYmmdd", "20131001"))
// Test unknown formatting character
(TimestampTC("yyyyUUdd", "2013001001"))
// Test markers
(TimestampTC("TTZZ", "TTZZ"))
// Test numeric formatting character
(TimestampTC("yyyyMM1dd", "201301111"))
// Test out of range year
(TimestampTC("yyyyyMMdd", "120130101", false, true))
// Test out of range month
(TimestampTC("yyyyMMdd", "20131301", false, true))
// Test out of range month
(TimestampTC("yyyyMMdd", "20130001", false, true))
// Test out of range day
(TimestampTC("yyyyMMdd", "20130132", false, true))
// Test out of range day
(TimestampTC("yyyyMMdd", "20130100", false, true))
// Test out of range hour
(TimestampTC("HH:mm:ss", "24:01:01", false, true))
// Test out of range minute
(TimestampTC("HH:mm:ss", "23:60:01", false, true))
// Test out of range second
(TimestampTC("HH:mm:ss", "23:01:60", false, true))
// Test characters where numbers should be
(TimestampTC("HH:mm:ss", "aa:01:01", false, true))
// Test missing year
(TimestampTC("MMdd", "1201", false, true))
// Test missing month
(TimestampTC("yyyydd", "201301", false, true))
(TimestampTC("yydd", "1301", false, true))
// Test missing day
(TimestampTC("yyyyMM", "201301", false, true))
(TimestampTC("yyMM", "8512", false, true))
// Test missing month and day
(TimestampTC("yyyy", "2013", false, true))
(TimestampTC("yy", "13", false, true))
// Test short year token
(TimestampTC("y-MM-dd", "2013-11-13", false, true, false, 2013, 11, 13))
(TimestampTC("y-MM-dd", "13-11-13", false, true, false, 1913, 11, 13))
// Test 2-digit year format
(TimestampTC("yy-MM-dd", "17-08-31", false, true, false, 1917, 8, 31))
(TimestampTC("yy-MM-dd", "99-08-31", false, true, false, 1999, 8, 31))
// Test 02/29 edge cases of 2-digit year format
(TimestampTC("yy-MM-dd", "00-02-28", false, true, false, 2000, 2, 28))
(TimestampTC("yy-MM-dd", "00-02-29", false, true, false, 2000, 2, 29))
(TimestampTC("yy-MM-dd", "00-03-01", false, true, false, 2000, 3, 1))
(TimestampTC("yy-MM-dd", "00-03-02", false, true, false, 1900, 3, 2))
(TimestampTC("yy-MM-dd", "04-02-29", false, true, false, 1904, 2, 29))
(TimestampTC("yy-MM-dd", "99-02-29", false, true))
// Test 1-digit year format with time to show the exact boundary
// Before the cutoff. Year should be 2000
(TimestampTC("y-MM-dd HH:mm:ss", "00-02-29 16:14:23", false, true, false,
2000, 2, 29, 16, 14, 23))
// After the cutoff but 02/29/1900 is invalid
(TimestampTC("y-MM-dd HH:mm:ss", "00-02-29 16:14:24", false, true))
// Test short month token
(TimestampTC("yyyy-M-dd", "2013-11-13", false, true, false, 2013, 11, 13))
(TimestampTC("yyyy-M-dd", "2013-1-13", false, true, false, 2013, 1, 13))
// Test short day token
(TimestampTC("yyyy-MM-d", "2013-11-13", false, true, false, 2013, 11, 13))
(TimestampTC("yyyy-MM-d", "2013-11-3", false, true, false, 2013, 11, 3))
// Test short all date tokens
(TimestampTC("y-M-d", "2013-11-13", false, true, false, 2013, 11, 13))
(TimestampTC("y-M-d", "13-1-3", false, true, false, 1913, 1, 3))
// Test short hour token
(TimestampTC("H:mm:ss", "14:24:34", false, false, true, 0, 0, 0, 14, 24, 34))
(TimestampTC("H:mm:ss", "4:24:34", false, false, true, 0, 0, 0, 4, 24, 34))
// Test short minute token
(TimestampTC("HH:m:ss", "14:24:34", false, false, true, 0, 0, 0, 14, 24, 34))
(TimestampTC("HH:m:ss", "1:24:34", false, true))
// Test short second token
(TimestampTC("HH:mm:s", "14:24:34", false, false, true, 0, 0, 0, 14, 24, 34))
// Test short all time tokens
(TimestampTC("H:m:s", "11:22:33", false, false, true, 0, 0, 0, 11, 22, 33))
(TimestampTC("H:m:s", "1:2:3", false, false, true, 0, 0, 0, 1, 2, 3))
// Test short fraction token
(TimestampTC("HH:mm:ss:S", "14:24:34:1234", false, false, true, 0, 0, 0, 14, 24, 34,
123400000));
// Loop through custom parse/format test cases and execute each one. Each test case
// will be explicitly set with a pass/fail expectation related to either the format
// or literal value.
for (int i = 0; i < test_cases.size(); ++i) {
TimestampTC test_case = test_cases[i];
DateTimeFormatContext dt_ctx(test_case.fmt);
dt_ctx.SetCenturyBreakAndCurrentTime(now);
bool parse_result = SimpleDateFormatTokenizer::Tokenize(&dt_ctx);
if (test_case.fmt_should_fail) {
EXPECT_FALSE(parse_result) << "TC: " << i;
continue;
} else {
ASSERT_TRUE(parse_result) << "TC: " << i;
}
TimestampValue cust_tv = TimestampValue::ParseSimpleDateFormat(test_case.str,
strlen(test_case.str), dt_ctx);
boost::gregorian::date cust_date = cust_tv.date();
boost::posix_time::time_duration cust_time = cust_tv.time();
if (test_case.str_should_fail) {
EXPECT_EQ(not_a_date, cust_date) << "TC: " << i;
EXPECT_EQ(cust_time, not_a_date_time) << "TC: " << i;
continue;
}
// Check that we have something valid in the timestamp value
EXPECT_TRUE((!cust_date.is_special())
|| (cust_time != not_a_date_time)) << "TC: " << i;
// Check the date component (based on any date format tokens being present)
if (test_case.fmt_has_date_toks) {
EXPECT_NE(cust_date, not_a_date) << "TC: " << i;
EXPECT_EQ(test_case.expected_year, cust_date.year()) << "TC: " << i;
EXPECT_EQ(test_case.expected_month, cust_date.month()) << "TC: " << i;
EXPECT_EQ(test_case.expected_day, cust_date.day()) << "TC: " << i;
} else {
EXPECT_EQ(not_a_date, cust_date) << "TC: " << i;
}
// Check the time component (based on any time format tokens being present). Note
// that if the date is specified, the time will at least be 00:00:00.0
if (test_case.fmt_has_time_toks || test_case.fmt_has_date_toks) {
EXPECT_NE(cust_time, not_a_date_time) << "TC: " << i;
EXPECT_EQ(test_case.expected_hours, cust_time.hours()) << "TC: " << i;
EXPECT_EQ(test_case.expected_minutes, cust_time.minutes()) << "TC: " << i;
EXPECT_EQ(test_case.expected_seconds, cust_time.seconds()) << "TC: " << i;
EXPECT_EQ(test_case.expected_fraction, cust_time.fractional_seconds()) << "TC: "
<< i;
if (!test_case.should_format) continue;
string buff = cust_tv.Format(dt_ctx);
EXPECT_TRUE(!buff.empty()) << "TC: " << i;
EXPECT_LE(buff.length(), dt_ctx.fmt_len) << "TC: " << i;
EXPECT_EQ(string(test_case.str, strlen(test_case.str)), buff) << "TC: " << i;
} else {
EXPECT_EQ(cust_time, not_a_date_time) << test_case.fmt << " " << test_case.str;
}
}
// Test complex formatting of date/times
vector<TimestampFormatTC> fmt_test_cases = list_of
(TimestampFormatTC(1382337792, "yyyy-MM-dd HH:mm:ss.SSSSSSSSS",
"2013-10-21 06:43:12.000000000"))
// Test just formatting time tokens
(TimestampFormatTC(1382337792, "HH:mm:ss.SSSSSSSSS", "06:43:12.000000000"))
(TimestampFormatTC(0, "yyyy-MM-ddTHH:mm:SS.SSSSSSSSSZ",
"1970-01-01T00:00:00.000000000Z"))
// Test just formatting date tokens
(TimestampFormatTC(965779200, "yyyy-MM-dd", "2000-08-09"))
// Test short form date tokens
(TimestampFormatTC(965779200, "yyyy-M-d", "2000-8-9"))
// Test short form tokens on wide dates
(TimestampFormatTC(1382337792, "d", "21"))
// Test month expansion
(TimestampFormatTC(965779200, "MMM/MM/M", "Aug/08/8"))
// Test padding on single digits
(TimestampFormatTC(965779200, "dddddd/dd/d", "000009/09/9"))
// Test padding on double digits
(TimestampFormatTC(1382337792, "dddddd/dd/dd", "000021/21/21"))
// Test just formatting time tokens on a ts value generated from a date
(TimestampFormatTC(965779200, "HH:mm:ss", "00:00:00"));
// Loop through format test cases
for (int i = 0; i < fmt_test_cases.size(); ++i) {
TimestampFormatTC test_case = fmt_test_cases[i];
DateTimeFormatContext dt_ctx(test_case.fmt);
ASSERT_TRUE(SimpleDateFormatTokenizer::Tokenize(&dt_ctx)) << "TC: " << i;
TimestampValue cust_tv = TimestampValue::FromUnixTime(test_case.ts, utc_tz);
EXPECT_NE(cust_tv.date(), not_a_date) << "TC: " << i;
EXPECT_NE(cust_tv.time(), not_a_date_time) << "TC: " << i;
EXPECT_GE(dt_ctx.fmt_out_len, dt_ctx.fmt_len);
string buff = cust_tv.Format(dt_ctx);
EXPECT_TRUE(!buff.empty()) << "TC: " << i;
EXPECT_LE(buff.length(), dt_ctx.fmt_out_len) << "TC: " << i;
EXPECT_EQ(buff, string(test_case.str, strlen(test_case.str))) << "TC: " << i;
}
// Test rounding near edge cases.
const int64_t MIN_DATE_AS_UNIX_TIME = -17987443200;
{
// Check lowest valid timestamp.
const TimestampValue ts = TimestampValue::ParseSimpleDateFormat("1400-01-01");
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME, FloorToSeconds(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MICROS_PER_SEC, RoundToMicros(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MICROS_PER_SEC, FloorToMicros(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MILLIS_PER_SEC, FloorToMillis(ts));
}
{
// Check that 250 nanoseconds is rounded/floored to last microsecond.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("1400-01-01 00:00:00.000000250");
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME, FloorToSeconds(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MICROS_PER_SEC, RoundToMicros(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MICROS_PER_SEC, FloorToMicros(ts));
}
{
// Check that 500 nanosecond is rounded up to the next microsecond, while floored to
// the last microsecond.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("1400-01-01 00:00:00.000000500");
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME, FloorToSeconds(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MICROS_PER_SEC + 1, RoundToMicros(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MICROS_PER_SEC, FloorToMicros(ts));
}
{
// Check that 250 microseconds is floored to last millisecond.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("1400-01-01 00:00:00.000250");
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME, FloorToSeconds(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MILLIS_PER_SEC, FloorToMillis(ts));
}
{
// Check that 500 microseconds is floored to last millisecond.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("1400-01-01 00:00:00.000500");
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME, FloorToSeconds(ts));
EXPECT_EQ(MIN_DATE_AS_UNIX_TIME * MILLIS_PER_SEC, FloorToMillis(ts));
}
EXPECT_EQ("1400-01-01 00:00:00",
TimestampValue::FromUnixTime(MIN_DATE_AS_UNIX_TIME, utc_tz).ToString());
TimestampValue too_early = TimestampValue::FromUnixTime(MIN_DATE_AS_UNIX_TIME - 1,
utc_tz);
EXPECT_FALSE(too_early.HasDate());
EXPECT_FALSE(too_early.HasTime());
int64_t dummy;
EXPECT_FALSE(too_early.UtcToUnixTimeMicros(&dummy));
EXPECT_FALSE(too_early.FloorUtcToUnixTimeMicros(&dummy));
EXPECT_FALSE(too_early.FloorUtcToUnixTimeMillis(&dummy));
// Sub-second FromUnixTime functions incorrectly accepted the last second of 1399
// as valid, because validation logic checked the nearest second rounded towards 0
// (IMPALA-5664).
TestFromSubSecondFunctions(MIN_DATE_AS_UNIX_TIME, -100, nullptr);
TestFromSubSecondFunctions(MIN_DATE_AS_UNIX_TIME, 100,
"1400-01-01 00:00:00.100000000");
const int64_t MAX_DATE_AS_UNIX_TIME = 253402300799;
{
// Test the max supported date that can be represented in seconds.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("9999-12-31 23:59:59");
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME, FloorToSeconds(ts));
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MICROS_PER_SEC, RoundToMicros(ts));
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MICROS_PER_SEC, FloorToMicros(ts));
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MILLIS_PER_SEC, FloorToMillis(ts));
}
{
// Check that 250 nanoseconds is rounded/floored to last microsecond.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("9999-12-31 23:59:59.000000250");
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MICROS_PER_SEC, RoundToMicros(ts));
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MICROS_PER_SEC, FloorToMicros(ts));
}
{
// Check that 500 nanosecond is rounded to the next microsecond, while floored to the
// last microsecond.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("9999-12-31 23:59:59.000000500");
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MICROS_PER_SEC + 1, RoundToMicros(ts));
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MICROS_PER_SEC, FloorToMicros(ts));
}
{
// Check that 250 microseconds is floored to last millisecond.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("9999-12-31 23:59:59.000250");
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME, FloorToSeconds(ts));
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MILLIS_PER_SEC, FloorToMillis(ts));
}
{
// Check that 500 microseconds is floored to last millisecond.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("9999-12-31 23:59:59.000500");
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MILLIS_PER_SEC, FloorToMillis(ts));
}
{
// The max date that can be represented with the maximum number of nanoseconds. Unlike
// the cases above, rounding to microsecond does not round up to the
// next microsecond because that time is not supported by Impala.
const TimestampValue ts =
TimestampValue::ParseSimpleDateFormat("9999-12-31 23:59:59.999999999");
// The result is the maximum date with the maximum number of microseconds supported by
// Impala.
EXPECT_EQ(MAX_DATE_AS_UNIX_TIME * MICROS_PER_SEC + 999999, RoundToMicros(ts));
}
EXPECT_EQ("9999-12-31 23:59:59",
TimestampValue::FromUnixTime(MAX_DATE_AS_UNIX_TIME, utc_tz).ToString());
TimestampValue too_late = TimestampValue::FromUnixTime(MAX_DATE_AS_UNIX_TIME + 1,
utc_tz);
EXPECT_FALSE(too_late.HasDate());
EXPECT_FALSE(too_late.HasTime());
// Checking sub-second FromUnixTime functions near 10000.01.01
TestFromSubSecondFunctions(MAX_DATE_AS_UNIX_TIME, MILLIS_PER_SEC - 1,
"9999-12-31 23:59:59.999000000");
TestFromSubSecondFunctions(MAX_DATE_AS_UNIX_TIME, MILLIS_PER_SEC, nullptr);
// Regression tests for IMPALA-1676, Unix times overflow int32 during year 2038
EXPECT_EQ("2038-01-19 03:14:08",
TimestampValue::FromUnixTime(2147483648, utc_tz).ToString());
EXPECT_EQ("2038-01-19 03:14:09",
TimestampValue::FromUnixTime(2147483649, utc_tz).ToString());
// Tests for the cases where abs(nanoseconds) >= 1e9.
EXPECT_EQ("2018-01-10 16:00:00",
TimestampValue::FromUnixTimeNanos(1515600000, 0, utc_tz).ToString());
EXPECT_EQ("2018-01-10 16:00:00.999999999",
TimestampValue::FromUnixTimeNanos(1515600000, 999999999, utc_tz).ToString());
EXPECT_EQ("2018-01-10 15:59:59.000000001",
TimestampValue::FromUnixTimeNanos(1515600000, -999999999, utc_tz).ToString());
EXPECT_EQ("2018-01-10 16:00:01",
TimestampValue::FromUnixTimeNanos(1515600000, 1000000000, utc_tz).ToString());
EXPECT_EQ("2018-01-10 15:59:59",
TimestampValue::FromUnixTimeNanos(1515600000, -1000000000, utc_tz).ToString());
EXPECT_EQ("2018-01-10 16:30:00",
TimestampValue::FromUnixTimeNanos(1515600000, 1800000000000, utc_tz).ToString());
EXPECT_EQ("2018-01-10 15:30:00",
TimestampValue::FromUnixTimeNanos(1515600000, -1800000000000, utc_tz).ToString());
// Test FromUnixTime around the boundary of the values that can be represented with
// int64 in nanosecond precision. Tests 1 second before and after these bounds.
const int64_t MIN_BOOST_CONVERT_UNIX_TIME = -9223372036;
const int64_t MAX_BOOST_CONVERT_UNIX_TIME = 9223372036;
const TimestampValue MIN_INT64_NANO_MINUS_1_SEC =
TimestampValue::FromUnixTime(MIN_BOOST_CONVERT_UNIX_TIME - 1, utc_tz);
const TimestampValue MIN_INT64_NANO =
TimestampValue::FromUnixTime(MIN_BOOST_CONVERT_UNIX_TIME, utc_tz);
const TimestampValue MAX_INT64_NANO =
TimestampValue::FromUnixTime(MAX_BOOST_CONVERT_UNIX_TIME, utc_tz);
const TimestampValue MAX_INT64_NANO_PLUS_1_SEC =
TimestampValue::FromUnixTime(MAX_BOOST_CONVERT_UNIX_TIME + 1, utc_tz);
EXPECT_EQ("1677-09-21 00:12:43", MIN_INT64_NANO_MINUS_1_SEC.ToString());
EXPECT_EQ("1677-09-21 00:12:44", MIN_INT64_NANO.ToString());
EXPECT_EQ("2262-04-11 23:47:16", MAX_INT64_NANO.ToString());
EXPECT_EQ("2262-04-11 23:47:17", MAX_INT64_NANO_PLUS_1_SEC.ToString());
// Test UtcToUnixTimeLimitedRangeNanos() near the edge values.
int64 int64_nano_value = 0;
EXPECT_TRUE(MIN_INT64_NANO.UtcToUnixTimeLimitedRangeNanos(&int64_nano_value));
EXPECT_EQ(MIN_BOOST_CONVERT_UNIX_TIME * NANOS_PER_SEC, int64_nano_value);
EXPECT_TRUE(MAX_INT64_NANO.UtcToUnixTimeLimitedRangeNanos(&int64_nano_value));
EXPECT_EQ(MAX_BOOST_CONVERT_UNIX_TIME * NANOS_PER_SEC, int64_nano_value);
EXPECT_FALSE(
MIN_INT64_NANO_MINUS_1_SEC.UtcToUnixTimeLimitedRangeNanos(&int64_nano_value));
EXPECT_FALSE(
MAX_INT64_NANO_PLUS_1_SEC.UtcToUnixTimeLimitedRangeNanos(&int64_nano_value));
// Test the exact bounderies of nanoseconds stored as int64.
EXPECT_EQ("1677-09-21 00:12:43.145224192",
TimestampValue::UtcFromUnixTimeLimitedRangeNanos(
std::numeric_limits<int64_t>::min()).ToString());
EXPECT_EQ("2262-04-11 23:47:16.854775807",
TimestampValue::UtcFromUnixTimeLimitedRangeNanos(
std::numeric_limits<int64_t>::max()).ToString());
// Test a leap second in 1998 represented by the UTC time 1998-12-31 23:59:60.
// Unix time cannot represent the leap second, which repeats 915148800.
EXPECT_EQ("1998-12-31 23:59:59",
TimestampValue::FromUnixTime(915148799, utc_tz).ToString());
EXPECT_EQ("1999-01-01 00:00:00",
TimestampValue::FromUnixTime(915148800, utc_tz).ToString());
// The leap second doesn't parse in Impala.
TimestampValue leap_tv =
TimestampValue::ParseSimpleDateFormat("1998-12-31 23:59:60.00");
EXPECT_FALSE(leap_tv.HasDateAndTime());
// The leap second can be parsed by ptime, though it is just converted to the time
// that the Unix time would represent (i.e. the second after the new year). This shows
// both times constructed via ptime compare equally.
ptime leap_ptime1 = boost::posix_time::time_from_string("1998-12-31 23:59:60");
ptime leap_ptime2 = boost::posix_time::time_from_string("1999-01-01 00:00:00");
TimestampValue leap_tv1 = TimestampValue(leap_ptime1);
TimestampValue leap_tv2 = TimestampValue(leap_ptime2);
EXPECT_TRUE(leap_tv1.HasDateAndTime());
EXPECT_TRUE(leap_tv1 == TimestampValue(leap_ptime2));
time_t leap_time_t;
EXPECT_TRUE(leap_tv1.ToUnixTime(utc_tz, &leap_time_t));
EXPECT_EQ(915148800, leap_time_t);
EXPECT_EQ("1999-01-01 00:00:00", leap_tv1.ToString());
EXPECT_TRUE(leap_tv2.ToUnixTime(utc_tz, &leap_time_t));
EXPECT_EQ(915148800, leap_time_t);
EXPECT_EQ("1999-01-01 00:00:00", leap_tv2.ToString());
// Test Unix time as a float
double result;
EXPECT_TRUE(
TimestampValue::ParseSimpleDateFormat("2013-10-21 06:43:12.07").ToSubsecondUnixTime(
utc_tz, &result));
EXPECT_EQ(1382337792.07, result);
EXPECT_EQ("1970-01-01 00:00:00.008000000",
TimestampValue::FromSubsecondUnixTime(0.008, utc_tz).ToString());
}
// Test subsecond unix time conversion for non edge cases.
TEST(TimestampTest, SubSecond) {
// Test with millisec precision.
TestFromSubSecondFunctions(0, 0, "1970-01-01 00:00:00");
TestFromSubSecondFunctions(0, 100, "1970-01-01 00:00:00.100000000");
TestFromSubSecondFunctions(0, 1100, "1970-01-01 00:00:01.100000000");
TestFromSubSecondFunctions(0, 24*60*60*1000, "1970-01-02 00:00:00");
TestFromSubSecondFunctions(0, 2*24*60*60*1000 + 100, "1970-01-03 00:00:00.100000000");
TestFromSubSecondFunctions(0, -100, "1969-12-31 23:59:59.900000000");
TestFromSubSecondFunctions(0, -1100, "1969-12-31 23:59:58.900000000");
TestFromSubSecondFunctions(0, -24*60*60*1000, "1969-12-31 00:00:00");
TestFromSubSecondFunctions(0, -2*24*60*60*1000 + 100, "1969-12-30 00:00:00.100000000");
TestFromSubSecondFunctions(-1, 0, "1969-12-31 23:59:59");
TestFromSubSecondFunctions(-1, 100, "1969-12-31 23:59:59.100000000");
TestFromSubSecondFunctions(-1, 1100, "1970-01-01 00:00:00.100000000");
TestFromSubSecondFunctions(-1, 24*60*60*1000, "1970-01-01 23:59:59");
TestFromSubSecondFunctions(-1, 2*24*60*60*1000 + 100, "1970-01-02 23:59:59.100000000");
TestFromSubSecondFunctions(-1, -100, "1969-12-31 23:59:58.900000000");
TestFromSubSecondFunctions(-1, -1100, "1969-12-31 23:59:57.900000000");
TestFromSubSecondFunctions(-1, -24*60*60*1000, "1969-12-30 23:59:59");
TestFromSubSecondFunctions(-1, -2*24*60*60*1000 + 100, "1969-12-29 23:59:59.100000000");
// A few test with sub-millisec precision.
auto tz = TimezoneDatabase::GetUtcTimezone();
EXPECT_EQ("1970-01-01 00:00:00.000001000",
TimestampValue::UtcFromUnixTimeMicros(1).ToString());
EXPECT_EQ("1969-12-31 23:59:59.999999000",
TimestampValue::UtcFromUnixTimeMicros(-1).ToString());
EXPECT_EQ("1970-01-01 00:00:00.000001000",
TimestampValue::FromUnixTimeMicros(1, tz).ToString());
EXPECT_EQ("1969-12-31 23:59:59.999999000",
TimestampValue::FromUnixTimeMicros(-1, tz).ToString());
EXPECT_EQ("1970-01-01 00:00:00.000000001",
TimestampValue::FromUnixTimeNanos(0, 1, tz).ToString());
EXPECT_EQ("1969-12-31 23:59:59.999999999",
TimestampValue::FromUnixTimeNanos(0, -1, tz).ToString());
EXPECT_EQ("1970-01-01 00:00:00.000000001",
TimestampValue::UtcFromUnixTimeLimitedRangeNanos(1).ToString());
EXPECT_EQ("1969-12-31 23:59:59.999999999",
TimestampValue::UtcFromUnixTimeLimitedRangeNanos(-1).ToString());
}
// Convenience function to create TimestampValues from strings.
TimestampValue StrToTs(const char * str) {
return TimestampValue::ParseSimpleDateFormat(str);
}
TEST(TimestampTest, TimezoneConversions) {
const string& path = Substitute("$0/testdata/tzdb_tiny", getenv("IMPALA_HOME"));
Status status = TimezoneDatabase::LoadZoneInfoBeTestOnly(path);
ASSERT_TRUE(status.ok());
const Timezone* tz = TimezoneDatabase::FindTimezone("CET");
ASSERT_NE(tz, nullptr);
// Timestamp that does not fall to DST change.
const TimestampValue unique_utc = StrToTs("2017-01-01 00:00:00");
const TimestampValue unique_local = StrToTs("2017-01-01 01:00:00");
{
// UtcToLocal / LocalToUtc changes the TimestampValue, so an extra copy is needed
// to avoid changing the original.
TimestampValue tmp = unique_utc;
TimestampValue repeated_period_start, repeated_period_end;
tmp.UtcToLocal(*tz, &repeated_period_start, &repeated_period_end);
EXPECT_EQ(tmp, unique_local);
EXPECT_FALSE(repeated_period_start.HasDate());
EXPECT_FALSE(repeated_period_end.HasDate());
tmp.LocalToUtc(*tz);
EXPECT_EQ(tmp, unique_utc);
}
// Timestamps that fall to UTC+2->UTC+1 DST change:
// - Up to 2017-10-29 02:59:59.999999999 AM offset was UTC+02:00.
// - At 2017-10-29 03:00:00 AM clocks were moved backward to 2017-10-29 02:00:00 AM,
// so offset became UTC+01:00.
const TimestampValue repeated_utc1 = StrToTs("2017-10-29 00:30:00");
const TimestampValue repeated_utc2 = StrToTs("2017-10-29 01:30:00");
const TimestampValue repeated_local = StrToTs("2017-10-29 02:30:00");
{
TimestampValue tmp = repeated_utc1;
TimestampValue repeated_period_start1, repeated_period_end1;
TimestampValue repeated_period_start2, repeated_period_end2;
tmp.UtcToLocal(*tz, &repeated_period_start1, &repeated_period_end1);
EXPECT_EQ(tmp, repeated_local);
tmp = repeated_utc2;
tmp.UtcToLocal(*tz, &repeated_period_start2, &repeated_period_end2);
EXPECT_EQ(tmp, repeated_local);
tmp.LocalToUtc(*tz);
EXPECT_FALSE(tmp.HasDate());
EXPECT_EQ(repeated_period_start1, repeated_period_start2);
EXPECT_EQ(repeated_period_end1, repeated_period_end2);
EXPECT_EQ(repeated_period_start1.ToString(), "2017-10-29 02:00:00");
EXPECT_EQ(repeated_period_end1.ToString(), "2017-10-29 02:59:59.999999999");
}
// Timestamp that falls to UTC+1->UTC+2 DST change:
// - Up to 2017-03-26 01:59:59.999999999 AM offset was UTC+01:00.
// - At 2017-03-26 02:00:00 AM Clocks were moved forward to 2017-03-26 03:00:00 AM,
// so offset became UTC+02:00.
const TimestampValue skipped_local = StrToTs("2017-03-26 02:30:00");
{
TimestampValue tmp = skipped_local;
tmp.LocalToUtc(*tz);
EXPECT_FALSE(tmp.HasDate());
}
}
}