aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Common/LocalDate.h
blob: e8a53f43ae8e1d22eca08bdc45c4b8c5ff2afabe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#pragma once

#include <cstring>
#include <string>
#include <sstream>
#include <exception>
#include <Common/DateLUT.h>


/** 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!
  *
  * 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)
  */
class LocalDate
{
private:
    unsigned short m_year; /// NOLINT
    unsigned char m_month;
    unsigned char m_day;

    void init(time_t time, const DateLUTImpl & date_lut)
    {
        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');
        }
    }

public:
    explicit LocalDate(time_t time, const DateLUTImpl & time_zone = DateLUT::instance())
    {
        init(time, time_zone);
    }

    LocalDate(DayNum day_num, const DateLUTImpl & time_zone = DateLUT::instance()) /// NOLINT
    {
        const auto & values = time_zone.getValues(day_num);
        m_year  = values.year;
        m_month = values.month;
        m_day   = values.day_of_month;
    }

    explicit LocalDate(ExtendedDayNum day_num, const DateLUTImpl & time_zone = DateLUT::instance())
    {
        const auto & values = time_zone.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_) /// NOLINT
        : 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 DateLUTImpl & lut = DateLUT::instance()) const
    {
        return DayNum(lut.makeDayNum(m_year, m_month, m_day).toUnderType());
    }

    ExtendedDayNum getExtenedDayNum(const DateLUTImpl & lut = DateLUT::instance()) const
    {
        return ExtendedDayNum (lut.makeDayNum(m_year, m_month, m_day).toUnderType());
    }

    operator DayNum() const /// NOLINT
    {
        return getDayNum();
    }

    unsigned short year() const { return m_year; } /// NOLINT
    unsigned char month() const { return m_month; }
    unsigned char day() const { return m_day; }

    void year(unsigned short x) { m_year = x; } /// NOLINT
    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);
    }

    /// 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();
    }
};

static_assert(sizeof(LocalDate) == 4);