ClickHouse/base/common/LocalDate.h

159 lines
4.3 KiB
C++
Raw Normal View History

2014-04-08 06:51:53 +00:00
#pragma once
2011-03-09 19:18:01 +00:00
2011-03-15 20:56:42 +00:00
#include <string.h>
2011-03-09 19:18:01 +00:00
#include <string>
#include <sstream>
#include <exception>
2015-09-29 19:19:54 +00:00
#include <common/DateLUT.h>
2011-03-09 19:18:01 +00:00
2017-08-04 23:16:43 +00:00
/** Stores a calendar date in broken-down form (year, month, day-in-month).
* Could be initialized from date in text form, like '2011-01-01' or from time_t with rounding to date.
* Also could be initialized from date in text form like '20110101... (only first 8 symbols are used).
* Could be implicitly casted to time_t.
* NOTE: Transforming between time_t and LocalDate is done in local time zone!
2011-03-18 20:26:54 +00:00
*
2017-08-04 23:16:43 +00:00
* When local time was shifted backwards (due to daylight saving time or whatever reason)
* - then to resolve the ambiguity of transforming to time_t, lowest of two possible values is selected.
*
* packed - for memcmp to work naturally (but because m_year is 2 bytes, on little endian, comparison is correct only before year 2047)
2011-03-18 20:26:54 +00:00
*/
class LocalDate
2011-03-09 19:18:01 +00:00
{
private:
unsigned short m_year;
unsigned char m_month;
unsigned char m_day;
void init(time_t time)
{
const auto & date_lut = DateLUT::instance();
const auto & values = date_lut.getValues(time);
m_year = values.year;
m_month = values.month;
m_day = values.day_of_month;
}
void init(const char * s, size_t length)
{
if (length < 8)
throw std::runtime_error("Cannot parse LocalDate: " + std::string(s, length));
m_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0');
if (s[4] == '-')
{
if (length < 10)
throw std::runtime_error("Cannot parse LocalDate: " + std::string(s, length));
m_month = (s[5] - '0') * 10 + (s[6] - '0');
m_day = (s[8] - '0') * 10 + (s[9] - '0');
}
else
{
m_month = (s[4] -'0') * 10 + (s[5] -'0');
m_day = (s[6] - '0')* 10 + (s[7] -'0');
}
}
2011-03-10 20:31:02 +00:00
public:
explicit LocalDate(time_t time)
{
init(time);
}
LocalDate(DayNum day_num)
{
const auto & values = DateLUT::instance().getValues(day_num);
m_year = values.year;
m_month = values.month;
m_day = values.day_of_month;
}
LocalDate(unsigned short year_, unsigned char month_, unsigned char day_)
: m_year(year_), m_month(month_), m_day(day_)
{
}
explicit LocalDate(const std::string & s)
{
init(s.data(), s.size());
}
LocalDate(const char * data, size_t length)
{
init(data, length);
}
LocalDate() : m_year(0), m_month(0), m_day(0)
{
}
LocalDate(const LocalDate &) noexcept = default;
LocalDate & operator= (const LocalDate &) noexcept = default;
DayNum getDayNum() const
{
Extended range of DateTime64 to years 1925 - 2238 The Year 1925 is a starting point because most of the timezones switched to saner (mostly 15-minutes based) offsets somewhere during 1924 or before. And that significantly simplifies implementation. 2238 is to simplify arithmetics for sanitizing LUT index access; there are less than 0x1ffff days from 1925. * Extended DateLUTImpl internal LUT to 0x1ffff items, some of which represent negative (pre-1970) time values. As a collateral benefit, Date now correctly supports dates up to 2149 (instead of 2106). * Added a new strong typedef ExtendedDayNum, which represents dates pre-1970 and post 2149. * Functions that used to return DayNum now return ExtendedDayNum. * Refactored DateLUTImpl to untie DayNum from the dual role of being a value and an index (due to negative time). Index is now a different type LUTIndex with explicit conversion functions from DatNum, time_t, and ExtendedDayNum. * Updated DateLUTImpl to properly support values close to epoch start (1970-01-01 00:00), including negative ones. * Reduced resolution of DateLUTImpl::Values::time_at_offset_change to multiple of 15-minutes to allow storing 64-bits of time_t in DateLUTImpl::Value while keeping same size. * Minor performance updates to DateLUTImpl when building month LUT by skipping non-start-of-month days. * Fixed extractTimeZoneFromFunctionArguments to work correctly with DateTime64. * New unit-tests and stateless integration tests for both DateTime and DateTime64.
2020-04-17 13:26:44 +00:00
const auto & lut = DateLUT::instance();
return DayNum(lut.makeDayNum(m_year, m_month, m_day).toUnderType());
}
operator DayNum() const
{
return getDayNum();
}
unsigned short year() const { return m_year; }
unsigned char month() const { return m_month; }
unsigned char day() const { return m_day; }
void year(unsigned short x) { m_year = x; }
void month(unsigned char x) { m_month = x; }
void day(unsigned char x) { m_day = x; }
bool operator< (const LocalDate & other) const
{
return 0 > memcmp(this, &other, sizeof(*this));
}
bool operator> (const LocalDate & other) const
{
return 0 < memcmp(this, &other, sizeof(*this));
}
bool operator<= (const LocalDate & other) const
{
return 0 >= memcmp(this, &other, sizeof(*this));
}
bool operator>= (const LocalDate & other) const
{
return 0 <= memcmp(this, &other, sizeof(*this));
}
bool operator== (const LocalDate & other) const
{
return 0 == memcmp(this, &other, sizeof(*this));
}
bool operator!= (const LocalDate & other) const
{
return !(*this == other);
}
2017-08-04 23:16:43 +00:00
/// NOTE Inefficient.
std::string toString(char separator = '-') const
{
std::stringstream ss;
if (separator)
ss << year() << separator << (month() / 10) << (month() % 10)
<< separator << (day() / 10) << (day() % 10);
else
ss << year() << (month() / 10) << (month() % 10)
<< (day() / 10) << (day() % 10);
return ss.str();
}
2011-03-09 19:18:01 +00:00
};
static_assert(sizeof(LocalDate) == 4);