1#include <gtest/gtest.h>
3#include <userver/proto-structs/date.hpp>
4#include <userver/proto-structs/exceptions.hpp>
5#include <userver/utest/assert_macros.hpp>
6#include <userver/utils/impl/internal_tag.hpp>
8using namespace std::literals::chrono_literals;
10USERVER_NAMESPACE_BEGIN
12namespace proto_structs::tests {
14TEST(DateTest, IsValid) {
15 EXPECT_FALSE(Date::IsValid(2025y, std::nullopt, 10d));
16 EXPECT_FALSE(Date::IsValid(std::nullopt, std::nullopt, 10d));
17 EXPECT_FALSE(Date::IsValid(std::nullopt, std::chrono::March, std::nullopt));
18 EXPECT_FALSE(Date::IsValid(0y, std::chrono::January, 1d));
19 EXPECT_FALSE(Date::IsValid(2025y, std::chrono::month{0}, 1d));
20 EXPECT_FALSE(Date::IsValid(2025y, std::chrono::January, std::chrono::day{0}));
21 EXPECT_FALSE(Date::IsValid(2025y, std::chrono::June, 31d));
22 EXPECT_FALSE(Date::IsValid(std::nullopt, std::chrono::June, 31d));
23 EXPECT_FALSE(Date::IsValid(2025y, std::chrono::February, 29d));
24 EXPECT_FALSE(Date::IsValid(10'000y, std::chrono::January, 1d));
25 EXPECT_FALSE(Date::IsValid(10'000y, std::nullopt, std::nullopt));
26 EXPECT_FALSE(Date::IsValid(-1y, std::chrono::January, 1d));
27 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, 2025, 0, 10));
28 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, 0, 0, 10));
29 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, 0, 3, 0));
30 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, -1, 1, 1));
31 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, 10'000, 1, 1));
32 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, 2025, -10, 1));
33 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, 2025, 10000, 1));
34 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, 2025, 1, -10));
35 EXPECT_FALSE(Date::IsValid(utils::impl::InternalTag{}, 2025, 1, 10000));
37 EXPECT_THROW(Date(2025y, std::nullopt, 10d), ValueError);
38 EXPECT_THROW(Date(2025y / 2 / 29), ValueError);
39 EXPECT_THROW(Date(std::chrono::year_month{2025y, std::chrono::month{20}}), ValueError);
40 EXPECT_THROW(Date(10000y), ValueError);
41 EXPECT_THROW(Date(std::chrono::month_day{std::chrono::June, 31d}), ValueError);
42 EXPECT_THROW(Date(utils::datetime::Date{10000, 1, 1}), ValueError);
43 EXPECT_THROW(Date(utils::
impl::InternalTag{}, 2025, 0, 10), ValueError);
45 Date(std::chrono::time_point_cast<
46 std::chrono::days>(std::chrono::system_clock::time_point{std::chrono::years(9000 )})),
49 UEXPECT_THROW_MSG(Date(2025y, std::chrono::February, 29d), ValueError,
"2025y/2m/29d");
50 UEXPECT_THROW_MSG(Date(utils::impl::InternalTag{}, 10, -10, 5), ValueError,
"10y/-10m/5d");
52 EXPECT_TRUE(Date::IsValid(std::nullopt, std::nullopt, std::nullopt));
53 EXPECT_TRUE(Date::IsValid(2024y, std::chrono::February, 29d));
54 EXPECT_TRUE(Date::IsValid(2025y, std::chrono::March, std::nullopt));
55 EXPECT_TRUE(Date::IsValid(2025y, std::nullopt, std::nullopt));
56 EXPECT_TRUE(Date::IsValid(std::nullopt, std::chrono::March, 30d));
57 EXPECT_TRUE(Date::IsValid(std::nullopt, std::chrono::February, 29d));
60TEST(DateTest, Conversions) {
61 Date d{std::nullopt, std::nullopt, std::nullopt};
63 EXPECT_TRUE(d.IsEmpty());
64 EXPECT_FALSE(d.HasYearMonthDay());
65 EXPECT_FALSE(d.HasYearMonth());
66 EXPECT_FALSE(d.HasMonthDay());
67 EXPECT_FALSE(d.HasYear());
68 EXPECT_FALSE(d.Year());
69 EXPECT_FALSE(d.Month());
70 EXPECT_FALSE(d.Day());
71 EXPECT_EQ(d.YearNum(), 0);
72 EXPECT_EQ(d.MonthNum(), 0);
73 EXPECT_EQ(d.DayNum(), 0);
74 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
75 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::year_month_day>(d), ValueError);
76 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
77 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<utils::datetime::Date>(d), ValueError);
78 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYearMonth(), ValueError);
79 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::year_month>(d), ValueError);
80 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoMonthDay(), ValueError);
81 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::month_day>(d), ValueError);
82 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYear(), ValueError);
83 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::year>(d), ValueError);
84 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoSysDays(), ValueError);
85 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::sys_days>(d), ValueError);
89 EXPECT_TRUE(d.IsEmpty());
90 EXPECT_FALSE(d.HasYearMonthDay());
91 EXPECT_FALSE(d.HasYearMonth());
92 EXPECT_FALSE(d.HasMonthDay());
93 EXPECT_FALSE(d.HasYear());
94 EXPECT_FALSE(d.Year());
95 EXPECT_FALSE(d.Month());
96 EXPECT_FALSE(d.Day());
97 EXPECT_EQ(d.YearNum(), 0);
98 EXPECT_EQ(d.MonthNum(), 0);
99 EXPECT_EQ(d.DayNum(), 0);
101 d = 2019y / std::chrono::May / 28d;
103 EXPECT_FALSE(d.IsEmpty());
104 ASSERT_TRUE(d.HasYearMonthDay());
105 ASSERT_TRUE(d.HasYearMonth());
106 ASSERT_TRUE(d.HasMonthDay());
107 ASSERT_TRUE(d.HasYear());
108 EXPECT_EQ(*d.Year(), 2019y);
109 EXPECT_EQ(*d.Month(), std::chrono::May);
110 EXPECT_EQ(*d.Day(), 28d);
111 EXPECT_EQ(d.YearNum(), 2019);
112 EXPECT_EQ(d.MonthNum(), 5);
113 EXPECT_EQ(d.DayNum(), 28);
114 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
115 EXPECT_EQ(
static_cast<std::chrono::year_month_day>(d), 2019y / std::chrono::May / 28d);
116 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
117 EXPECT_EQ(
static_cast<utils::datetime::Date>(d), utils::datetime::Date(2019, 5, 28));
118 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
119 EXPECT_EQ(
static_cast<std::chrono::year_month>(d), 2019y / std::chrono::May);
120 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
121 EXPECT_EQ(
static_cast<std::chrono::month_day>(d), std::chrono::May / 28d);
122 EXPECT_EQ(d.ToChronoYear(), 2019y);
123 EXPECT_EQ(
static_cast<std::chrono::year>(d), 2019y);
124 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
126 static_cast<std::chrono::sys_days>(d),
127 static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
130 d = 2019y / std::chrono::May;
132 EXPECT_FALSE(d.IsEmpty());
133 EXPECT_FALSE(d.HasYearMonthDay());
134 ASSERT_TRUE(d.HasYearMonth());
135 EXPECT_FALSE(d.HasMonthDay());
136 ASSERT_TRUE(d.HasYear());
137 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
138 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
139 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
140 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoMonthDay(), ValueError);
141 EXPECT_EQ(d.ToChronoYear(), 2019y);
142 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 1d));
143 EXPECT_EQ(
static_cast<std::chrono::sys_days>(d),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 1d));
145 d = std::chrono::May / 28d;
147 EXPECT_FALSE(d.IsEmpty());
148 EXPECT_FALSE(d.HasYearMonthDay());
149 EXPECT_FALSE(d.HasYearMonth());
150 ASSERT_TRUE(d.HasMonthDay());
151 EXPECT_FALSE(d.HasYear());
152 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
153 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
154 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYearMonth(), ValueError);
155 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
156 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYear(), ValueError);
157 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoSysDays(), ValueError);
161 EXPECT_FALSE(d.IsEmpty());
162 EXPECT_FALSE(d.HasYearMonthDay());
163 EXPECT_FALSE(d.HasYearMonth());
164 EXPECT_FALSE(d.HasMonthDay());
165 ASSERT_TRUE(d.HasYear());
166 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
167 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
168 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYearMonth(), ValueError);
169 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoMonthDay(), ValueError);
170 EXPECT_EQ(d.ToChronoYear(), 2019y);
171 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::January / 1d));
173 static_cast<std::chrono::sys_days>(d),
174 static_cast<std::chrono::sys_days>(2019y / std::chrono::January / 1d)
177 d = utils::datetime::Date(2019, 5, 28);
179 EXPECT_FALSE(d.IsEmpty());
180 ASSERT_TRUE(d.HasYearMonthDay());
181 ASSERT_TRUE(d.HasYearMonth());
182 ASSERT_TRUE(d.HasMonthDay());
183 ASSERT_TRUE(d.HasYear());
184 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
185 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
186 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
187 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
188 EXPECT_EQ(d.ToChronoYear(), 2019y);
189 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
191 static_cast<std::chrono::sys_days>(d),
192 static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
195 d =
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d);
197 EXPECT_FALSE(d.IsEmpty());
198 ASSERT_TRUE(d.HasYearMonthDay());
199 ASSERT_TRUE(d.HasYearMonth());
200 ASSERT_TRUE(d.HasMonthDay());
201 ASSERT_TRUE(d.HasYear());
202 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
203 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
204 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
205 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
206 EXPECT_EQ(d.ToChronoYear(), 2019y);
207 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
209 static_cast<std::chrono::sys_days>(d),
210 static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
213 d = Date{utils::impl::InternalTag{}, 2019, 5, 28};
215 EXPECT_FALSE(d.IsEmpty());
216 ASSERT_TRUE(d.HasYearMonthDay());
217 ASSERT_TRUE(d.HasYearMonth());
218 ASSERT_TRUE(d.HasMonthDay());
219 ASSERT_TRUE(d.HasYear());
220 EXPECT_EQ(*d.Year(), 2019y);
221 EXPECT_EQ(*d.Month(), std::chrono::May);
222 EXPECT_EQ(*d.Day(), 28d);
223 EXPECT_EQ(d.YearNum(), 2019);
224 EXPECT_EQ(d.MonthNum(), 5);
225 EXPECT_EQ(d.DayNum(), 28);
226 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
227 EXPECT_EQ(
static_cast<std::chrono::year_month_day>(d), 2019y / std::chrono::May / 28d);
228 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
229 EXPECT_EQ(
static_cast<utils::datetime::Date>(d), utils::datetime::Date(2019, 5, 28));
230 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
231 EXPECT_EQ(
static_cast<std::chrono::year_month>(d), 2019y / std::chrono::May);
232 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
233 EXPECT_EQ(
static_cast<std::chrono::month_day>(d), std::chrono::May / 28d);
234 EXPECT_EQ(d.ToChronoYear(), 2019y);
235 EXPECT_EQ(
static_cast<std::chrono::year>(d), 2019y);
236 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
238 static_cast<std::chrono::sys_days>(d),
239 static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)