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, ConversionsNull) {
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);
88TEST(DateTest, ConversionsDefault) {
91 EXPECT_TRUE(d.IsEmpty());
92 EXPECT_FALSE(d.HasYearMonthDay());
93 EXPECT_FALSE(d.HasYearMonth());
94 EXPECT_FALSE(d.HasMonthDay());
95 EXPECT_FALSE(d.HasYear());
96 EXPECT_FALSE(d.Year());
97 EXPECT_FALSE(d.Month());
98 EXPECT_FALSE(d.Day());
99 EXPECT_EQ(d.YearNum(), 0);
100 EXPECT_EQ(d.MonthNum(), 0);
101 EXPECT_EQ(d.DayNum(), 0);
104TEST(DateTest, ConversionsStdDate) {
105 Date d = 2019y / std::chrono::May / 28d;
107 EXPECT_FALSE(d.IsEmpty());
108 ASSERT_TRUE(d.HasYearMonthDay());
109 ASSERT_TRUE(d.HasYearMonth());
110 ASSERT_TRUE(d.HasMonthDay());
111 ASSERT_TRUE(d.HasYear());
112 EXPECT_EQ(*d.Year(), 2019y);
113 EXPECT_EQ(*d.Month(), std::chrono::May);
114 EXPECT_EQ(*d.Day(), 28d);
115 EXPECT_EQ(d.YearNum(), 2019);
116 EXPECT_EQ(d.MonthNum(), 5);
117 EXPECT_EQ(d.DayNum(), 28);
118 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
119 EXPECT_EQ(
static_cast<std::chrono::year_month_day>(d), 2019y / std::chrono::May / 28d);
120 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
121 EXPECT_EQ(
static_cast<utils::datetime::Date>(d), utils::datetime::Date(2019, 5, 28));
122 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
123 EXPECT_EQ(
static_cast<std::chrono::year_month>(d), 2019y / std::chrono::May);
124 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
125 EXPECT_EQ(
static_cast<std::chrono::month_day>(d), std::chrono::May / 28d);
126 EXPECT_EQ(d.ToChronoYear(), 2019y);
127 EXPECT_EQ(
static_cast<std::chrono::year>(d), 2019y);
128 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
130 static_cast<std::chrono::sys_days>(d),
131 static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
135TEST(DateTest, ConversionsStdYearMonth) {
136 Date d = 2019y / std::chrono::May;
138 EXPECT_FALSE(d.IsEmpty());
139 EXPECT_FALSE(d.HasYearMonthDay());
140 ASSERT_TRUE(d.HasYearMonth());
141 EXPECT_FALSE(d.HasMonthDay());
142 ASSERT_TRUE(d.HasYear());
143 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
144 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
145 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
146 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoMonthDay(), ValueError);
147 EXPECT_EQ(d.ToChronoYear(), 2019y);
148 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 1d));
149 EXPECT_EQ(
static_cast<std::chrono::sys_days>(d),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 1d));
152TEST(DateTest, ConversionsStdMonthDay) {
153 Date d = std::chrono::May / 28d;
155 EXPECT_FALSE(d.IsEmpty());
156 EXPECT_FALSE(d.HasYearMonthDay());
157 EXPECT_FALSE(d.HasYearMonth());
158 ASSERT_TRUE(d.HasMonthDay());
159 EXPECT_FALSE(d.HasYear());
160 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
161 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
162 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYearMonth(), ValueError);
163 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
164 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYear(), ValueError);
165 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoSysDays(), ValueError);
168TEST(DateTest, ConversionsStdYear) {
171 EXPECT_FALSE(d.IsEmpty());
172 EXPECT_FALSE(d.HasYearMonthDay());
173 EXPECT_FALSE(d.HasYearMonth());
174 EXPECT_FALSE(d.HasMonthDay());
175 ASSERT_TRUE(d.HasYear());
176 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
177 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
178 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYearMonth(), ValueError);
179 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoMonthDay(), ValueError);
180 EXPECT_EQ(d.ToChronoYear(), 2019y);
181 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::January / 1d));
183 static_cast<std::chrono::sys_days>(d),
184 static_cast<std::chrono::sys_days>(2019y / std::chrono::January / 1d)
188TEST(DateTest, ConversionsUserverDate) {
189 Date d = utils::datetime::Date(2019, 5, 28);
191 EXPECT_FALSE(d.IsEmpty());
192 ASSERT_TRUE(d.HasYearMonthDay());
193 ASSERT_TRUE(d.HasYearMonth());
194 ASSERT_TRUE(d.HasMonthDay());
195 ASSERT_TRUE(d.HasYear());
196 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
197 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
198 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
199 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
200 EXPECT_EQ(d.ToChronoYear(), 2019y);
201 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
203 static_cast<std::chrono::sys_days>(d),
204 static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
208TEST(DateTest, ConversionsChronoSysDays) {
209 Date d =
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d);
211 EXPECT_FALSE(d.IsEmpty());
212 ASSERT_TRUE(d.HasYearMonthDay());
213 ASSERT_TRUE(d.HasYearMonth());
214 ASSERT_TRUE(d.HasMonthDay());
215 ASSERT_TRUE(d.HasYear());
216 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
217 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
218 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
219 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
220 EXPECT_EQ(d.ToChronoYear(), 2019y);
221 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
223 static_cast<std::chrono::sys_days>(d),
224 static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
228TEST(DateTest, ConversionsInternal) {
229 Date d = Date{utils::impl::InternalTag{}, 2019, 5, 28};
231 EXPECT_FALSE(d.IsEmpty());
232 ASSERT_TRUE(d.HasYearMonthDay());
233 ASSERT_TRUE(d.HasYearMonth());
234 ASSERT_TRUE(d.HasMonthDay());
235 ASSERT_TRUE(d.HasYear());
236 EXPECT_EQ(*d.Year(), 2019y);
237 EXPECT_EQ(*d.Month(), std::chrono::May);
238 EXPECT_EQ(*d.Day(), 28d);
239 EXPECT_EQ(d.YearNum(), 2019);
240 EXPECT_EQ(d.MonthNum(), 5);
241 EXPECT_EQ(d.DayNum(), 28);
242 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
243 EXPECT_EQ(
static_cast<std::chrono::year_month_day>(d), 2019y / std::chrono::May / 28d);
244 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
245 EXPECT_EQ(
static_cast<utils::datetime::Date>(d), utils::datetime::Date(2019, 5, 28));
246 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
247 EXPECT_EQ(
static_cast<std::chrono::year_month>(d), 2019y / std::chrono::May);
248 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
249 EXPECT_EQ(
static_cast<std::chrono::month_day>(d), std::chrono::May / 28d);
250 EXPECT_EQ(d.ToChronoYear(), 2019y);
251 EXPECT_EQ(
static_cast<std::chrono::year>(d), 2019y);
252 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
254 static_cast<std::chrono::sys_days>(d),
255 static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)