1 #include "utils/time_utils.hpp"
2 
3 #include <chrono>
4 #include <cstdint>
5 #include <ctime>
6 #include <limits>
7 #include <optional>
8 
9 #include <gtest/gtest.h>
10 
11 namespace redfish::time_utils
12 {
13 namespace
14 {
15 
TEST(FromDurationTest,PositiveTests)16 TEST(FromDurationTest, PositiveTests)
17 {
18     EXPECT_EQ(fromDurationString("PT12S"), std::chrono::milliseconds(12000));
19     EXPECT_EQ(fromDurationString("PT0.204S"), std::chrono::milliseconds(204));
20     EXPECT_EQ(fromDurationString("PT0.2S"), std::chrono::milliseconds(200));
21     EXPECT_EQ(fromDurationString("PT50M"), std::chrono::milliseconds(3000000));
22     EXPECT_EQ(fromDurationString("PT23H"), std::chrono::milliseconds(82800000));
23     EXPECT_EQ(fromDurationString("P51D"),
24               std::chrono::milliseconds(4406400000));
25     EXPECT_EQ(fromDurationString("PT2H40M10.1S"),
26               std::chrono::milliseconds(9610100));
27     EXPECT_EQ(fromDurationString("P20DT2H40M10.1S"),
28               std::chrono::milliseconds(1737610100));
29     EXPECT_EQ(fromDurationString(""), std::chrono::milliseconds(0));
30 }
31 
TEST(FromDurationTest,NegativeTests)32 TEST(FromDurationTest, NegativeTests)
33 {
34     EXPECT_EQ(fromDurationString("PTS"), std::nullopt);
35     EXPECT_EQ(fromDurationString("P1T"), std::nullopt);
36     EXPECT_EQ(fromDurationString("PT100M1000S100"), std::nullopt);
37     EXPECT_EQ(fromDurationString("PDTHMS"), std::nullopt);
38     EXPECT_EQ(fromDurationString("P9999999999999999999999999DT"), std::nullopt);
39     EXPECT_EQ(fromDurationString("PD222T222H222M222.222S"), std::nullopt);
40     EXPECT_EQ(fromDurationString("PT99999H9999999999999999999999M99999999999S"),
41               std::nullopt);
42     EXPECT_EQ(fromDurationString("PT-9H"), std::nullopt);
43 }
TEST(ToDurationTest,PositiveTests)44 TEST(ToDurationTest, PositiveTests)
45 {
46     EXPECT_EQ(toDurationString(std::chrono::milliseconds(12000)), "PT12.000S");
47     EXPECT_EQ(toDurationString(std::chrono::milliseconds(204)), "PT0.204S");
48     EXPECT_EQ(toDurationString(std::chrono::milliseconds(200)), "PT0.200S");
49     EXPECT_EQ(toDurationString(std::chrono::milliseconds(3000000)), "PT50M");
50     EXPECT_EQ(toDurationString(std::chrono::milliseconds(82800000)), "PT23H");
51     EXPECT_EQ(toDurationString(std::chrono::milliseconds(4406400000)), "P51DT");
52     EXPECT_EQ(toDurationString(std::chrono::milliseconds(9610100)),
53               "PT2H40M10.100S");
54     EXPECT_EQ(toDurationString(std::chrono::milliseconds(1737610100)),
55               "P20DT2H40M10.100S");
56 }
57 
TEST(ToDurationTest,NegativeTests)58 TEST(ToDurationTest, NegativeTests)
59 {
60     EXPECT_EQ(toDurationString(std::chrono::milliseconds(-250)), "");
61 }
62 
TEST(ToDurationStringFromUintTest,PositiveTests)63 TEST(ToDurationStringFromUintTest, PositiveTests)
64 {
65     uint64_t maxAcceptedTimeMs =
66         static_cast<uint64_t>(std::chrono::milliseconds::max().count());
67 
68     EXPECT_NE(toDurationStringFromUint(maxAcceptedTimeMs), std::nullopt);
69     EXPECT_EQ(toDurationStringFromUint(0), "PT");
70     EXPECT_EQ(toDurationStringFromUint(250), "PT0.250S");
71     EXPECT_EQ(toDurationStringFromUint(5000), "PT5.000S");
72 }
73 
TEST(ToDurationStringFromUintTest,NegativeTests)74 TEST(ToDurationStringFromUintTest, NegativeTests)
75 {
76     uint64_t minNotAcceptedTimeMs =
77         static_cast<uint64_t>(std::chrono::milliseconds::max().count()) + 1;
78 
79     EXPECT_EQ(toDurationStringFromUint(minNotAcceptedTimeMs), std::nullopt);
80     EXPECT_EQ(toDurationStringFromUint(static_cast<uint64_t>(-1)),
81               std::nullopt);
82 }
83 
TEST(GetDateTimeStdtime,ConversionTests)84 TEST(GetDateTimeStdtime, ConversionTests)
85 {
86     // some time before the epoch
87     EXPECT_EQ(getDateTimeStdtime(std::time_t{-1234567}),
88               "1970-01-01T00:00:00+00:00");
89 
90     // epoch
91     EXPECT_EQ(getDateTimeStdtime(std::time_t{0}), "1970-01-01T00:00:00+00:00");
92 
93     // Limits
94     EXPECT_EQ(getDateTimeStdtime(std::numeric_limits<std::time_t>::max()),
95               "9999-12-31T23:59:59+00:00");
96     EXPECT_EQ(getDateTimeStdtime(std::numeric_limits<std::time_t>::min()),
97               "1970-01-01T00:00:00+00:00");
98 }
99 
TEST(GetDateTimeUint,ConversionTests)100 TEST(GetDateTimeUint, ConversionTests)
101 {
102     EXPECT_EQ(getDateTimeUint(uint64_t{1638312095}),
103               "2021-11-30T22:41:35+00:00");
104     // some time in the future, beyond 2038
105     EXPECT_EQ(getDateTimeUint(uint64_t{41638312095}),
106               "3289-06-18T21:48:15+00:00");
107     // the maximum time we support
108     EXPECT_EQ(getDateTimeUint(uint64_t{253402300799}),
109               "9999-12-31T23:59:59+00:00");
110 
111     // returns the maximum Redfish date
112     EXPECT_EQ(getDateTimeUint(std::numeric_limits<uint64_t>::max()),
113               "9999-12-31T23:59:59+00:00");
114 
115     EXPECT_EQ(getDateTimeUint(std::numeric_limits<uint64_t>::min()),
116               "1970-01-01T00:00:00+00:00");
117 }
118 
TEST(GetDateTimeUintMs,ConverstionTests)119 TEST(GetDateTimeUintMs, ConverstionTests)
120 {
121     EXPECT_EQ(getDateTimeUintMs(uint64_t{1638312095123}),
122               "2021-11-30T22:41:35.123+00:00");
123     // returns the maximum Redfish date
124     EXPECT_EQ(getDateTimeUintMs(std::numeric_limits<uint64_t>::max()),
125               "9999-12-31T23:59:59.999+00:00");
126     EXPECT_EQ(getDateTimeUintMs(std::numeric_limits<uint64_t>::min()),
127               "1970-01-01T00:00:00.000+00:00");
128 }
129 
TEST(Utility,GetDateTimeUintUs)130 TEST(Utility, GetDateTimeUintUs)
131 {
132     EXPECT_EQ(getDateTimeUintUs(uint64_t{1638312095123456}),
133               "2021-11-30T22:41:35.123456+00:00");
134     // returns the maximum Redfish date
135     EXPECT_EQ(getDateTimeUintUs(std::numeric_limits<uint64_t>::max()),
136               "9999-12-31T23:59:59.999999+00:00");
137     EXPECT_EQ(getDateTimeUintUs(std::numeric_limits<uint64_t>::min()),
138               "1970-01-01T00:00:00.000000+00:00");
139 }
140 
TEST(Utility,DateStringToEpoch)141 TEST(Utility, DateStringToEpoch)
142 {
143     EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35.123456+00:00"),
144               usSinceEpoch{1638312095123456});
145     // no timezone
146     EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35.123456"),
147               usSinceEpoch{1638312095123456});
148     // Milliseconds precision
149     EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35.123"),
150               usSinceEpoch{1638312095123000});
151     // Seconds precision
152     EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35"),
153               usSinceEpoch{1638312095000000});
154 
155     // valid datetime format
156     EXPECT_EQ(dateStringToEpoch("20230531T000000Z"),
157               usSinceEpoch{1685491200000000});
158 
159     // valid datetime format
160     EXPECT_EQ(dateStringToEpoch("20230531"), usSinceEpoch{1685491200000000});
161 
162     // Non zero timezone
163     EXPECT_EQ(dateStringToEpoch("2021-11-30T22:41:35.123456+04:00"),
164               usSinceEpoch{1638297695123456});
165 
166     // Epoch
167     EXPECT_EQ(dateStringToEpoch("1970-01-01T00:00:00.000000+00:00"),
168               usSinceEpoch{0});
169 
170     // Max time
171     EXPECT_EQ(dateStringToEpoch("9999-12-31T23:59:59.999999+00:00"),
172               usSinceEpoch{253402300799999999});
173 
174     // Underflow
175     EXPECT_EQ(dateStringToEpoch("1969-12-30T23:59:59.999999+00:00"),
176               std::nullopt);
177 }
178 
TEST(Utility,DateStringToEpochWithInvalidDateTimeFormats)179 TEST(Utility, DateStringToEpochWithInvalidDateTimeFormats)
180 {
181     // invalid month (13)
182     EXPECT_EQ(dateStringToEpoch("2024-13-01T12:00:00Z"), std::nullopt);
183 
184     // invalid character for month
185     EXPECT_EQ(dateStringToEpoch("2024-X-01T12:00:00Z"), std::nullopt);
186 
187     // invalid day (32)
188     EXPECT_EQ(dateStringToEpoch("2024-07-32T12:00:00Z"), std::nullopt);
189 
190     // invalid character for day
191     EXPECT_EQ(dateStringToEpoch("2024-07-XT12:00:00Z"), std::nullopt);
192 
193     // invalid hour (25)
194     EXPECT_EQ(dateStringToEpoch("2024-07-01T25:00:00Z"), std::nullopt);
195 
196     // invalid character for hour
197     EXPECT_EQ(dateStringToEpoch("2024-07-01TX:00:00Z"), std::nullopt);
198 
199     // invalid minute (60)
200     // Date.h and std::chrono seem to disagree about whether there is a 60th
201     // minute in an hour.  Not clear if this is intended or not, but really
202     // isn't that important.  Let std::chrono pass with 61
203 #if __cpp_lib_chrono >= 201907L
204     EXPECT_EQ(dateStringToEpoch("2024-07-01T12:61:00Z"), std::nullopt);
205 #else
206     EXPECT_EQ(dateStringToEpoch("2024-07-01T12:60:00Z"), std::nullopt);
207 #endif
208 
209     // invalid character for minute
210     EXPECT_EQ(dateStringToEpoch("2024-13-01T12:X:00Z"), std::nullopt);
211 
212     // invalid second (60)
213     EXPECT_EQ(dateStringToEpoch("2024-07-01T12:00:XZ"), std::nullopt);
214 
215     // invalid character for second
216     EXPECT_EQ(dateStringToEpoch("2024-13-01T12:00:00Z"), std::nullopt);
217 
218     // invalid timezone
219     EXPECT_EQ(dateStringToEpoch("2024-07-01T12:00:00X"), std::nullopt);
220 
221     // invalid datetime format
222     EXPECT_EQ(dateStringToEpoch("202305"), std::nullopt);
223 
224     // invalid month (13), day (99)
225     EXPECT_EQ(dateStringToEpoch("19991399"), std::nullopt);
226 }
227 
TEST(Utility,GetDateTimeIso8601)228 TEST(Utility, GetDateTimeIso8601)
229 {
230     EXPECT_EQ(getDateTimeIso8601("20230531"), "2023-05-31T00:00:00+00:00");
231 
232     EXPECT_EQ(getDateTimeIso8601("20230531T000000Z"),
233               "2023-05-31T00:00:00+00:00");
234 
235     // invalid datetime
236     EXPECT_EQ(getDateTimeIso8601("202305"), std::nullopt);
237 }
238 
239 } // namespace
240 } // namespace redfish::time_utils
241