1#include <gmock/gmock.h>
2#include <gtest/gtest.h>
4#include <userver/proto-structs/date.hpp>
5#include <userver/proto-structs/exceptions.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<std::chrono::days>(std::chrono::system_clock::time_point{
46 std::chrono::years(9000 )})),
50 []() { [[maybe_unused]] Date d(2025y, std::chrono::February, 29d); },
51 ::testing::ThrowsMessage<ValueError>(::testing::HasSubstr(
"2025y/2m/29d"))
54 []() { [[maybe_unused]] Date d(utils::impl::InternalTag{}, 10, -10, 5); },
55 ::testing::ThrowsMessage<ValueError>(::testing::HasSubstr(
"10y/-10m/5d"))
58 EXPECT_TRUE(Date::IsValid(std::nullopt, std::nullopt, std::nullopt));
59 EXPECT_TRUE(Date::IsValid(2024y, std::chrono::February, 29d));
60 EXPECT_TRUE(Date::IsValid(2025y, std::chrono::March, std::nullopt));
61 EXPECT_TRUE(Date::IsValid(2025y, std::nullopt, std::nullopt));
62 EXPECT_TRUE(Date::IsValid(std::nullopt, std::chrono::March, 30d));
63 EXPECT_TRUE(Date::IsValid(std::nullopt, std::chrono::February, 29d));
66TEST(DateTest, Conversions) {
67 Date d{std::nullopt, std::nullopt, std::nullopt};
69 EXPECT_TRUE(d.IsEmpty());
70 EXPECT_FALSE(d.HasYearMonthDay());
71 EXPECT_FALSE(d.HasYearMonth());
72 EXPECT_FALSE(d.HasMonthDay());
73 EXPECT_FALSE(d.HasYear());
74 EXPECT_FALSE(d.Year());
75 EXPECT_FALSE(d.Month());
76 EXPECT_FALSE(d.Day());
77 EXPECT_EQ(d.YearNum(), 0);
78 EXPECT_EQ(d.MonthNum(), 0);
79 EXPECT_EQ(d.DayNum(), 0);
80 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
81 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::year_month_day>(d), ValueError);
82 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
83 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<utils::datetime::Date>(d), ValueError);
84 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYearMonth(), ValueError);
85 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::year_month>(d), ValueError);
86 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoMonthDay(), ValueError);
87 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::month_day>(d), ValueError);
88 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYear(), ValueError);
89 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::year>(d), ValueError);
90 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoSysDays(), ValueError);
91 EXPECT_THROW([[maybe_unused]]
auto val =
static_cast<std::chrono::sys_days>(d), ValueError);
95 EXPECT_TRUE(d.IsEmpty());
96 EXPECT_FALSE(d.HasYearMonthDay());
97 EXPECT_FALSE(d.HasYearMonth());
98 EXPECT_FALSE(d.HasMonthDay());
99 EXPECT_FALSE(d.HasYear());
100 EXPECT_FALSE(d.Year());
101 EXPECT_FALSE(d.Month());
102 EXPECT_FALSE(d.Day());
103 EXPECT_EQ(d.YearNum(), 0);
104 EXPECT_EQ(d.MonthNum(), 0);
105 EXPECT_EQ(d.DayNum(), 0);
107 d = 2019y / std::chrono::May / 28d;
109 EXPECT_FALSE(d.IsEmpty());
110 ASSERT_TRUE(d.HasYearMonthDay());
111 ASSERT_TRUE(d.HasYearMonth());
112 ASSERT_TRUE(d.HasMonthDay());
113 ASSERT_TRUE(d.HasYear());
114 EXPECT_EQ(*d.Year(), 2019y);
115 EXPECT_EQ(*d.Month(), std::chrono::May);
116 EXPECT_EQ(*d.Day(), 28d);
117 EXPECT_EQ(d.YearNum(), 2019);
118 EXPECT_EQ(d.MonthNum(), 5);
119 EXPECT_EQ(d.DayNum(), 28);
120 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
121 EXPECT_EQ(
static_cast<std::chrono::year_month_day>(d), 2019y / std::chrono::May / 28d);
122 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
123 EXPECT_EQ(
static_cast<utils::datetime::Date>(d), utils::datetime::Date(2019, 5, 28));
124 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
125 EXPECT_EQ(
static_cast<std::chrono::year_month>(d), 2019y / std::chrono::May);
126 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
127 EXPECT_EQ(
static_cast<std::chrono::month_day>(d), std::chrono::May / 28d);
128 EXPECT_EQ(d.ToChronoYear(), 2019y);
129 EXPECT_EQ(
static_cast<std::chrono::year>(d), 2019y);
130 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
132 static_cast<std::chrono::sys_days>(d),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
135 d = 2019y / std::chrono::May;
137 EXPECT_FALSE(d.IsEmpty());
138 EXPECT_FALSE(d.HasYearMonthDay());
139 ASSERT_TRUE(d.HasYearMonth());
140 EXPECT_FALSE(d.HasMonthDay());
141 ASSERT_TRUE(d.HasYear());
142 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
143 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
144 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
145 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoMonthDay(), ValueError);
146 EXPECT_EQ(d.ToChronoYear(), 2019y);
147 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 1d));
148 EXPECT_EQ(
static_cast<std::chrono::sys_days>(d),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 1d));
150 d = std::chrono::May / 28d;
152 EXPECT_FALSE(d.IsEmpty());
153 EXPECT_FALSE(d.HasYearMonthDay());
154 EXPECT_FALSE(d.HasYearMonth());
155 ASSERT_TRUE(d.HasMonthDay());
156 EXPECT_FALSE(d.HasYear());
157 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
158 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
159 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYearMonth(), ValueError);
160 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
161 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYear(), ValueError);
162 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoSysDays(), ValueError);
166 EXPECT_FALSE(d.IsEmpty());
167 EXPECT_FALSE(d.HasYearMonthDay());
168 EXPECT_FALSE(d.HasYearMonth());
169 EXPECT_FALSE(d.HasMonthDay());
170 ASSERT_TRUE(d.HasYear());
171 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoDate(), ValueError);
172 EXPECT_THROW([[maybe_unused]]
auto val = d.ToUserverDate(), ValueError);
173 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoYearMonth(), ValueError);
174 EXPECT_THROW([[maybe_unused]]
auto val = d.ToChronoMonthDay(), ValueError);
175 EXPECT_EQ(d.ToChronoYear(), 2019y);
176 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::January / 1d));
178 static_cast<std::chrono::sys_days>(d),
static_cast<std::chrono::sys_days>(2019y / std::chrono::January / 1d)
181 d = utils::datetime::Date(2019, 5, 28);
183 EXPECT_FALSE(d.IsEmpty());
184 ASSERT_TRUE(d.HasYearMonthDay());
185 ASSERT_TRUE(d.HasYearMonth());
186 ASSERT_TRUE(d.HasMonthDay());
187 ASSERT_TRUE(d.HasYear());
188 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
189 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
190 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
191 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
192 EXPECT_EQ(d.ToChronoYear(), 2019y);
193 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
195 static_cast<std::chrono::sys_days>(d),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
198 d =
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d);
200 EXPECT_FALSE(d.IsEmpty());
201 ASSERT_TRUE(d.HasYearMonthDay());
202 ASSERT_TRUE(d.HasYearMonth());
203 ASSERT_TRUE(d.HasMonthDay());
204 ASSERT_TRUE(d.HasYear());
205 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
206 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
207 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
208 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
209 EXPECT_EQ(d.ToChronoYear(), 2019y);
210 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
212 static_cast<std::chrono::sys_days>(d),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)
215 d = Date{utils::impl::InternalTag{}, 2019, 5, 28};
217 EXPECT_FALSE(d.IsEmpty());
218 ASSERT_TRUE(d.HasYearMonthDay());
219 ASSERT_TRUE(d.HasYearMonth());
220 ASSERT_TRUE(d.HasMonthDay());
221 ASSERT_TRUE(d.HasYear());
222 EXPECT_EQ(*d.Year(), 2019y);
223 EXPECT_EQ(*d.Month(), std::chrono::May);
224 EXPECT_EQ(*d.Day(), 28d);
225 EXPECT_EQ(d.YearNum(), 2019);
226 EXPECT_EQ(d.MonthNum(), 5);
227 EXPECT_EQ(d.DayNum(), 28);
228 EXPECT_EQ(d.ToChronoDate(), 2019y / std::chrono::May / 28d);
229 EXPECT_EQ(
static_cast<std::chrono::year_month_day>(d), 2019y / std::chrono::May / 28d);
230 EXPECT_EQ(d.ToUserverDate(), utils::datetime::Date(2019, 5, 28));
231 EXPECT_EQ(
static_cast<utils::datetime::Date>(d), utils::datetime::Date(2019, 5, 28));
232 EXPECT_EQ(d.ToChronoYearMonth(), 2019y / std::chrono::May);
233 EXPECT_EQ(
static_cast<std::chrono::year_month>(d), 2019y / std::chrono::May);
234 EXPECT_EQ(d.ToChronoMonthDay(), std::chrono::May / 28d);
235 EXPECT_EQ(
static_cast<std::chrono::month_day>(d), std::chrono::May / 28d);
236 EXPECT_EQ(d.ToChronoYear(), 2019y);
237 EXPECT_EQ(
static_cast<std::chrono::year>(d), 2019y);
238 EXPECT_EQ(d.ToChronoSysDays(),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d));
240 static_cast<std::chrono::sys_days>(d),
static_cast<std::chrono::sys_days>(2019y / std::chrono::May / 28d)