1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <cstdint>
16 #include <limits>
17 #include <string>
18
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include "absl/time/internal/test_util.h"
22 #include "absl/time/time.h"
23
24 using testing::HasSubstr;
25
26 namespace {
27
28 // A helper that tests the given format specifier by itself, and with leading
29 // and trailing characters. For example: TestFormatSpecifier(t, "%a", "Thu").
TestFormatSpecifier(absl::Time t,absl::TimeZone tz,const std::string & fmt,const std::string & ans)30 void TestFormatSpecifier(absl::Time t, absl::TimeZone tz,
31 const std::string& fmt, const std::string& ans) {
32 EXPECT_EQ(ans, absl::FormatTime(fmt, t, tz));
33 EXPECT_EQ("xxx " + ans, absl::FormatTime("xxx " + fmt, t, tz));
34 EXPECT_EQ(ans + " yyy", absl::FormatTime(fmt + " yyy", t, tz));
35 EXPECT_EQ("xxx " + ans + " yyy",
36 absl::FormatTime("xxx " + fmt + " yyy", t, tz));
37 }
38
39 //
40 // Testing FormatTime()
41 //
42
TEST(FormatTime,Basics)43 TEST(FormatTime, Basics) {
44 absl::TimeZone tz = absl::UTCTimeZone();
45 absl::Time t = absl::FromTimeT(0);
46
47 // Starts with a couple basic edge cases.
48 EXPECT_EQ("", absl::FormatTime("", t, tz));
49 EXPECT_EQ(" ", absl::FormatTime(" ", t, tz));
50 EXPECT_EQ(" ", absl::FormatTime(" ", t, tz));
51 EXPECT_EQ("xxx", absl::FormatTime("xxx", t, tz));
52 std::string big(128, 'x');
53 EXPECT_EQ(big, absl::FormatTime(big, t, tz));
54 // Cause the 1024-byte buffer to grow.
55 std::string bigger(100000, 'x');
56 EXPECT_EQ(bigger, absl::FormatTime(bigger, t, tz));
57
58 t += absl::Hours(13) + absl::Minutes(4) + absl::Seconds(5);
59 t += absl::Milliseconds(6) + absl::Microseconds(7) + absl::Nanoseconds(8);
60 EXPECT_EQ("1970-01-01", absl::FormatTime("%Y-%m-%d", t, tz));
61 EXPECT_EQ("13:04:05", absl::FormatTime("%H:%M:%S", t, tz));
62 EXPECT_EQ("13:04:05.006", absl::FormatTime("%H:%M:%E3S", t, tz));
63 EXPECT_EQ("13:04:05.006007", absl::FormatTime("%H:%M:%E6S", t, tz));
64 EXPECT_EQ("13:04:05.006007008", absl::FormatTime("%H:%M:%E9S", t, tz));
65 }
66
TEST(FormatTime,LocaleSpecific)67 TEST(FormatTime, LocaleSpecific) {
68 const absl::TimeZone tz = absl::UTCTimeZone();
69 absl::Time t = absl::FromTimeT(0);
70
71 TestFormatSpecifier(t, tz, "%a", "Thu");
72 TestFormatSpecifier(t, tz, "%A", "Thursday");
73 TestFormatSpecifier(t, tz, "%b", "Jan");
74 TestFormatSpecifier(t, tz, "%B", "January");
75
76 // %c should at least produce the numeric year and time-of-day.
77 const std::string s =
78 absl::FormatTime("%c", absl::FromTimeT(0), absl::UTCTimeZone());
79 EXPECT_THAT(s, HasSubstr("1970"));
80 EXPECT_THAT(s, HasSubstr("00:00:00"));
81
82 TestFormatSpecifier(t, tz, "%p", "AM");
83 TestFormatSpecifier(t, tz, "%x", "01/01/70");
84 TestFormatSpecifier(t, tz, "%X", "00:00:00");
85 }
86
TEST(FormatTime,ExtendedSeconds)87 TEST(FormatTime, ExtendedSeconds) {
88 const absl::TimeZone tz = absl::UTCTimeZone();
89
90 // No subseconds.
91 absl::Time t = absl::FromTimeT(0) + absl::Seconds(5);
92 EXPECT_EQ("05", absl::FormatTime("%E*S", t, tz));
93 EXPECT_EQ("05.000000000000000", absl::FormatTime("%E15S", t, tz));
94
95 // With subseconds.
96 t += absl::Milliseconds(6) + absl::Microseconds(7) + absl::Nanoseconds(8);
97 EXPECT_EQ("05.006007008", absl::FormatTime("%E*S", t, tz));
98 EXPECT_EQ("05", absl::FormatTime("%E0S", t, tz));
99 EXPECT_EQ("05.006007008000000", absl::FormatTime("%E15S", t, tz));
100
101 // Times before the Unix epoch.
102 t = absl::FromUnixMicros(-1);
103 EXPECT_EQ("1969-12-31 23:59:59.999999",
104 absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
105
106 // Here is a "%E*S" case we got wrong for a while. While the first
107 // instant below is correctly rendered as "...:07.333304", the second
108 // one used to appear as "...:07.33330499999999999".
109 t = absl::FromUnixMicros(1395024427333304);
110 EXPECT_EQ("2014-03-17 02:47:07.333304",
111 absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
112 t += absl::Microseconds(1);
113 EXPECT_EQ("2014-03-17 02:47:07.333305",
114 absl::FormatTime("%Y-%m-%d %H:%M:%E*S", t, tz));
115 }
116
TEST(FormatTime,RFC1123FormatPadsYear)117 TEST(FormatTime, RFC1123FormatPadsYear) { // locale specific
118 absl::TimeZone tz = absl::UTCTimeZone();
119
120 // A year of 77 should be padded to 0077.
121 absl::Time t = absl::FromCivil(absl::CivilSecond(77, 6, 28, 9, 8, 7), tz);
122 EXPECT_EQ("Mon, 28 Jun 0077 09:08:07 +0000",
123 absl::FormatTime(absl::RFC1123_full, t, tz));
124 EXPECT_EQ("28 Jun 0077 09:08:07 +0000",
125 absl::FormatTime(absl::RFC1123_no_wday, t, tz));
126 }
127
TEST(FormatTime,InfiniteTime)128 TEST(FormatTime, InfiniteTime) {
129 absl::TimeZone tz = absl::time_internal::LoadTimeZone("America/Los_Angeles");
130
131 // The format and timezone are ignored.
132 EXPECT_EQ("infinite-future",
133 absl::FormatTime("%H:%M blah", absl::InfiniteFuture(), tz));
134 EXPECT_EQ("infinite-past",
135 absl::FormatTime("%H:%M blah", absl::InfinitePast(), tz));
136 }
137
138 //
139 // Testing ParseTime()
140 //
141
TEST(ParseTime,Basics)142 TEST(ParseTime, Basics) {
143 absl::Time t = absl::FromTimeT(1234567890);
144 std::string err;
145
146 // Simple edge cases.
147 EXPECT_TRUE(absl::ParseTime("", "", &t, &err)) << err;
148 EXPECT_EQ(absl::UnixEpoch(), t); // everything defaulted
149 EXPECT_TRUE(absl::ParseTime(" ", " ", &t, &err)) << err;
150 EXPECT_TRUE(absl::ParseTime(" ", " ", &t, &err)) << err;
151 EXPECT_TRUE(absl::ParseTime("x", "x", &t, &err)) << err;
152 EXPECT_TRUE(absl::ParseTime("xxx", "xxx", &t, &err)) << err;
153
154 EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
155 "2013-06-28 19:08:09 -0800", &t, &err))
156 << err;
157 const auto ci = absl::FixedTimeZone(-8 * 60 * 60).At(t);
158 EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
159 EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
160 }
161
TEST(ParseTime,NullErrorString)162 TEST(ParseTime, NullErrorString) {
163 absl::Time t;
164 EXPECT_FALSE(absl::ParseTime("%Q", "invalid format", &t, nullptr));
165 EXPECT_FALSE(absl::ParseTime("%H", "12 trailing data", &t, nullptr));
166 EXPECT_FALSE(
167 absl::ParseTime("%H out of range", "42 out of range", &t, nullptr));
168 }
169
TEST(ParseTime,WithTimeZone)170 TEST(ParseTime, WithTimeZone) {
171 const absl::TimeZone tz =
172 absl::time_internal::LoadTimeZone("America/Los_Angeles");
173 absl::Time t;
174 std::string e;
175
176 // We can parse a std::string without a UTC offset if we supply a timezone.
177 EXPECT_TRUE(
178 absl::ParseTime("%Y-%m-%d %H:%M:%S", "2013-06-28 19:08:09", tz, &t, &e))
179 << e;
180 auto ci = tz.At(t);
181 EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
182 EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
183
184 // But the timezone is ignored when a UTC offset is present.
185 EXPECT_TRUE(absl::ParseTime("%Y-%m-%d %H:%M:%S %z",
186 "2013-06-28 19:08:09 +0800", tz, &t, &e))
187 << e;
188 ci = absl::FixedTimeZone(8 * 60 * 60).At(t);
189 EXPECT_EQ(absl::CivilSecond(2013, 6, 28, 19, 8, 9), ci.cs);
190 EXPECT_EQ(absl::ZeroDuration(), ci.subsecond);
191 }
192
TEST(ParseTime,ErrorCases)193 TEST(ParseTime, ErrorCases) {
194 absl::Time t = absl::FromTimeT(0);
195 std::string err;
196
197 EXPECT_FALSE(absl::ParseTime("%S", "123", &t, &err)) << err;
198 EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
199
200 // Can't parse an illegal format specifier.
201 err.clear();
202 EXPECT_FALSE(absl::ParseTime("%Q", "x", &t, &err)) << err;
203 // Exact contents of "err" are platform-dependent because of
204 // differences in the strptime implementation between macOS and Linux.
205 EXPECT_FALSE(err.empty());
206
207 // Fails because of trailing, unparsed data "blah".
208 EXPECT_FALSE(absl::ParseTime("%m-%d", "2-3 blah", &t, &err)) << err;
209 EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
210
211 // Feb 31 requires normalization.
212 EXPECT_FALSE(absl::ParseTime("%m-%d", "2-31", &t, &err)) << err;
213 EXPECT_THAT(err, HasSubstr("Out-of-range"));
214
215 // Check that we cannot have spaces in UTC offsets.
216 EXPECT_TRUE(absl::ParseTime("%z", "-0203", &t, &err)) << err;
217 EXPECT_FALSE(absl::ParseTime("%z", "- 2 3", &t, &err)) << err;
218 EXPECT_THAT(err, HasSubstr("Failed to parse"));
219 EXPECT_TRUE(absl::ParseTime("%Ez", "-02:03", &t, &err)) << err;
220 EXPECT_FALSE(absl::ParseTime("%Ez", "- 2: 3", &t, &err)) << err;
221 EXPECT_THAT(err, HasSubstr("Failed to parse"));
222
223 // Check that we reject other malformed UTC offsets.
224 EXPECT_FALSE(absl::ParseTime("%Ez", "+-08:00", &t, &err)) << err;
225 EXPECT_THAT(err, HasSubstr("Failed to parse"));
226 EXPECT_FALSE(absl::ParseTime("%Ez", "-+08:00", &t, &err)) << err;
227 EXPECT_THAT(err, HasSubstr("Failed to parse"));
228
229 // Check that we do not accept "-0" in fields that allow zero.
230 EXPECT_FALSE(absl::ParseTime("%Y", "-0", &t, &err)) << err;
231 EXPECT_THAT(err, HasSubstr("Failed to parse"));
232 EXPECT_FALSE(absl::ParseTime("%E4Y", "-0", &t, &err)) << err;
233 EXPECT_THAT(err, HasSubstr("Failed to parse"));
234 EXPECT_FALSE(absl::ParseTime("%H", "-0", &t, &err)) << err;
235 EXPECT_THAT(err, HasSubstr("Failed to parse"));
236 EXPECT_FALSE(absl::ParseTime("%M", "-0", &t, &err)) << err;
237 EXPECT_THAT(err, HasSubstr("Failed to parse"));
238 EXPECT_FALSE(absl::ParseTime("%S", "-0", &t, &err)) << err;
239 EXPECT_THAT(err, HasSubstr("Failed to parse"));
240 EXPECT_FALSE(absl::ParseTime("%z", "+-000", &t, &err)) << err;
241 EXPECT_THAT(err, HasSubstr("Failed to parse"));
242 EXPECT_FALSE(absl::ParseTime("%Ez", "+-0:00", &t, &err)) << err;
243 EXPECT_THAT(err, HasSubstr("Failed to parse"));
244 EXPECT_FALSE(absl::ParseTime("%z", "-00-0", &t, &err)) << err;
245 EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
246 EXPECT_FALSE(absl::ParseTime("%Ez", "-00:-0", &t, &err)) << err;
247 EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
248 }
249
TEST(ParseTime,ExtendedSeconds)250 TEST(ParseTime, ExtendedSeconds) {
251 std::string err;
252 absl::Time t;
253
254 // Here is a "%E*S" case we got wrong for a while. The fractional
255 // part of the first instant is less than 2^31 and was correctly
256 // parsed, while the second (and any subsecond field >=2^31) failed.
257 t = absl::UnixEpoch();
258 EXPECT_TRUE(absl::ParseTime("%E*S", "0.2147483647", &t, &err)) << err;
259 EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
260 absl::Nanoseconds(1) / 2,
261 t);
262 t = absl::UnixEpoch();
263 EXPECT_TRUE(absl::ParseTime("%E*S", "0.2147483648", &t, &err)) << err;
264 EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
265 absl::Nanoseconds(3) / 4,
266 t);
267
268 // We should also be able to specify long strings of digits far
269 // beyond the current resolution and have them convert the same way.
270 t = absl::UnixEpoch();
271 EXPECT_TRUE(absl::ParseTime(
272 "%E*S", "0.214748364801234567890123456789012345678901234567890123456789",
273 &t, &err))
274 << err;
275 EXPECT_EQ(absl::UnixEpoch() + absl::Nanoseconds(214748364) +
276 absl::Nanoseconds(3) / 4,
277 t);
278 }
279
TEST(ParseTime,ExtendedOffsetErrors)280 TEST(ParseTime, ExtendedOffsetErrors) {
281 std::string err;
282 absl::Time t;
283
284 // %z against +-HHMM.
285 EXPECT_FALSE(absl::ParseTime("%z", "-123", &t, &err)) << err;
286 EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
287
288 // %z against +-HH.
289 EXPECT_FALSE(absl::ParseTime("%z", "-1", &t, &err)) << err;
290 EXPECT_THAT(err, HasSubstr("Failed to parse"));
291
292 // %Ez against +-HH:MM.
293 EXPECT_FALSE(absl::ParseTime("%Ez", "-12:3", &t, &err)) << err;
294 EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
295
296 // %Ez against +-HHMM.
297 EXPECT_FALSE(absl::ParseTime("%Ez", "-123", &t, &err)) << err;
298 EXPECT_THAT(err, HasSubstr("Illegal trailing data"));
299
300 // %Ez against +-HH.
301 EXPECT_FALSE(absl::ParseTime("%Ez", "-1", &t, &err)) << err;
302 EXPECT_THAT(err, HasSubstr("Failed to parse"));
303 }
304
TEST(ParseTime,InfiniteTime)305 TEST(ParseTime, InfiniteTime) {
306 absl::Time t;
307 std::string err;
308 EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-future", &t, &err));
309 EXPECT_EQ(absl::InfiniteFuture(), t);
310
311 // Surrounding whitespace.
312 EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-future", &t, &err));
313 EXPECT_EQ(absl::InfiniteFuture(), t);
314 EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-future ", &t, &err));
315 EXPECT_EQ(absl::InfiniteFuture(), t);
316 EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-future ", &t, &err));
317 EXPECT_EQ(absl::InfiniteFuture(), t);
318
319 EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-past", &t, &err));
320 EXPECT_EQ(absl::InfinitePast(), t);
321
322 // Surrounding whitespace.
323 EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-past", &t, &err));
324 EXPECT_EQ(absl::InfinitePast(), t);
325 EXPECT_TRUE(absl::ParseTime("%H:%M blah", "infinite-past ", &t, &err));
326 EXPECT_EQ(absl::InfinitePast(), t);
327 EXPECT_TRUE(absl::ParseTime("%H:%M blah", " infinite-past ", &t, &err));
328 EXPECT_EQ(absl::InfinitePast(), t);
329
330 // "infinite-future" as literal std::string
331 absl::TimeZone tz = absl::UTCTimeZone();
332 EXPECT_TRUE(absl::ParseTime("infinite-future %H:%M", "infinite-future 03:04",
333 &t, &err));
334 EXPECT_NE(absl::InfiniteFuture(), t);
335 EXPECT_EQ(3, tz.At(t).cs.hour());
336 EXPECT_EQ(4, tz.At(t).cs.minute());
337
338 // "infinite-past" as literal std::string
339 EXPECT_TRUE(
340 absl::ParseTime("infinite-past %H:%M", "infinite-past 03:04", &t, &err));
341 EXPECT_NE(absl::InfinitePast(), t);
342 EXPECT_EQ(3, tz.At(t).cs.hour());
343 EXPECT_EQ(4, tz.At(t).cs.minute());
344
345 // The input doesn't match the format.
346 EXPECT_FALSE(absl::ParseTime("infinite-future %H:%M", "03:04", &t, &err));
347 EXPECT_FALSE(absl::ParseTime("infinite-past %H:%M", "03:04", &t, &err));
348 }
349
TEST(ParseTime,FailsOnUnrepresentableTime)350 TEST(ParseTime, FailsOnUnrepresentableTime) {
351 const absl::TimeZone utc = absl::UTCTimeZone();
352 absl::Time t;
353 EXPECT_FALSE(
354 absl::ParseTime("%Y-%m-%d", "-292277022657-01-27", utc, &t, nullptr));
355 EXPECT_TRUE(
356 absl::ParseTime("%Y-%m-%d", "-292277022657-01-28", utc, &t, nullptr));
357 EXPECT_TRUE(
358 absl::ParseTime("%Y-%m-%d", "292277026596-12-04", utc, &t, nullptr));
359 EXPECT_FALSE(
360 absl::ParseTime("%Y-%m-%d", "292277026596-12-05", utc, &t, nullptr));
361 }
362
363 //
364 // Roundtrip test for FormatTime()/ParseTime().
365 //
366
TEST(FormatParse,RoundTrip)367 TEST(FormatParse, RoundTrip) {
368 const absl::TimeZone lax =
369 absl::time_internal::LoadTimeZone("America/Los_Angeles");
370 const absl::Time in =
371 absl::FromCivil(absl::CivilSecond(1977, 6, 28, 9, 8, 7), lax);
372 const absl::Duration subseconds = absl::Nanoseconds(654321);
373 std::string err;
374
375 // RFC3339, which renders subseconds.
376 {
377 absl::Time out;
378 const std::string s =
379 absl::FormatTime(absl::RFC3339_full, in + subseconds, lax);
380 EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
381 << s << ": " << err;
382 EXPECT_EQ(in + subseconds, out); // RFC3339_full includes %Ez
383 }
384
385 // RFC1123, which only does whole seconds.
386 {
387 absl::Time out;
388 const std::string s = absl::FormatTime(absl::RFC1123_full, in, lax);
389 EXPECT_TRUE(absl::ParseTime(absl::RFC1123_full, s, &out, &err))
390 << s << ": " << err;
391 EXPECT_EQ(in, out); // RFC1123_full includes %z
392 }
393
394 // `absl::FormatTime()` falls back to strftime() for "%c", which appears to
395 // work. On Windows, `absl::ParseTime()` falls back to std::get_time() which
396 // appears to fail on "%c" (or at least on the "%c" text produced by
397 // `strftime()`). This makes it fail the round-trip test.
398 //
399 // Under the emscripten compiler `absl::ParseTime() falls back to
400 // `strptime()`, but that ends up using a different definition for "%c"
401 // compared to `strftime()`, also causing the round-trip test to fail
402 // (see https://github.com/kripken/emscripten/pull/7491).
403 #if !defined(_MSC_VER) && !defined(__EMSCRIPTEN__)
404 // Even though we don't know what %c will produce, it should roundtrip,
405 // but only in the 0-offset timezone.
406 {
407 absl::Time out;
408 const std::string s = absl::FormatTime("%c", in, absl::UTCTimeZone());
409 EXPECT_TRUE(absl::ParseTime("%c", s, &out, &err)) << s << ": " << err;
410 EXPECT_EQ(in, out);
411 }
412 #endif // !_MSC_VER && !__EMSCRIPTEN__
413 }
414
TEST(FormatParse,RoundTripDistantFuture)415 TEST(FormatParse, RoundTripDistantFuture) {
416 const absl::TimeZone tz = absl::UTCTimeZone();
417 const absl::Time in =
418 absl::FromUnixSeconds(std::numeric_limits<int64_t>::max());
419 std::string err;
420
421 absl::Time out;
422 const std::string s = absl::FormatTime(absl::RFC3339_full, in, tz);
423 EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
424 << s << ": " << err;
425 EXPECT_EQ(in, out);
426 }
427
TEST(FormatParse,RoundTripDistantPast)428 TEST(FormatParse, RoundTripDistantPast) {
429 const absl::TimeZone tz = absl::UTCTimeZone();
430 const absl::Time in =
431 absl::FromUnixSeconds(std::numeric_limits<int64_t>::min());
432 std::string err;
433
434 absl::Time out;
435 const std::string s = absl::FormatTime(absl::RFC3339_full, in, tz);
436 EXPECT_TRUE(absl::ParseTime(absl::RFC3339_full, s, &out, &err))
437 << s << ": " << err;
438 EXPECT_EQ(in, out);
439 }
440
441 } // namespace
442