1 // Copyright 2018 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 "absl/time/civil_time.h"
16
17 #include <cstdlib>
18 #include <ostream>
19 #include <string>
20
21 #include "absl/strings/str_cat.h"
22 #include "absl/time/time.h"
23
24 namespace absl {
25 ABSL_NAMESPACE_BEGIN
26
27 namespace {
28
29 // Since a civil time has a larger year range than absl::Time (64-bit years vs
30 // 64-bit seconds, respectively) we normalize years to roughly +/- 400 years
31 // around the year 2400, which will produce an equivalent year in a range that
32 // absl::Time can handle.
NormalizeYear(civil_year_t year)33 inline civil_year_t NormalizeYear(civil_year_t year) {
34 return 2400 + year % 400;
35 }
36
37 // Formats the given CivilSecond according to the given format.
FormatYearAnd(string_view fmt,CivilSecond cs)38 std::string FormatYearAnd(string_view fmt, CivilSecond cs) {
39 const CivilSecond ncs(NormalizeYear(cs.year()), cs.month(), cs.day(),
40 cs.hour(), cs.minute(), cs.second());
41 const TimeZone utc = UTCTimeZone();
42 return StrCat(cs.year(), FormatTime(fmt, FromCivil(ncs, utc), utc));
43 }
44
45 template <typename CivilT>
ParseYearAnd(string_view fmt,string_view s,CivilT * c)46 bool ParseYearAnd(string_view fmt, string_view s, CivilT* c) {
47 // Civil times support a larger year range than absl::Time, so we need to
48 // parse the year separately, normalize it, then use absl::ParseTime on the
49 // normalized string.
50 const std::string ss = std::string(s); // TODO(absl-team): Avoid conversion.
51 const char* const np = ss.c_str();
52 char* endp;
53 errno = 0;
54 const civil_year_t y =
55 std::strtoll(np, &endp, 10); // NOLINT(runtime/deprecated_fn)
56 if (endp == np || errno == ERANGE) return false;
57 const std::string norm = StrCat(NormalizeYear(y), endp);
58
59 const TimeZone utc = UTCTimeZone();
60 Time t;
61 if (ParseTime(StrCat("%Y", fmt), norm, utc, &t, nullptr)) {
62 const auto cs = ToCivilSecond(t, utc);
63 *c = CivilT(y, cs.month(), cs.day(), cs.hour(), cs.minute(), cs.second());
64 return true;
65 }
66
67 return false;
68 }
69
70 // Tries to parse the type as a CivilT1, but then assigns the result to the
71 // argument of type CivilT2.
72 template <typename CivilT1, typename CivilT2>
ParseAs(string_view s,CivilT2 * c)73 bool ParseAs(string_view s, CivilT2* c) {
74 CivilT1 t1;
75 if (ParseCivilTime(s, &t1)) {
76 *c = CivilT2(t1);
77 return true;
78 }
79 return false;
80 }
81
82 template <typename CivilT>
ParseLenient(string_view s,CivilT * c)83 bool ParseLenient(string_view s, CivilT* c) {
84 // A fastpath for when the given string data parses exactly into the given
85 // type T (e.g., s="YYYY-MM-DD" and CivilT=CivilDay).
86 if (ParseCivilTime(s, c)) return true;
87 // Try parsing as each of the 6 types, trying the most common types first
88 // (based on csearch results).
89 if (ParseAs<CivilDay>(s, c)) return true;
90 if (ParseAs<CivilSecond>(s, c)) return true;
91 if (ParseAs<CivilHour>(s, c)) return true;
92 if (ParseAs<CivilMonth>(s, c)) return true;
93 if (ParseAs<CivilMinute>(s, c)) return true;
94 if (ParseAs<CivilYear>(s, c)) return true;
95 return false;
96 }
97 } // namespace
98
FormatCivilTime(CivilSecond c)99 std::string FormatCivilTime(CivilSecond c) {
100 return FormatYearAnd("-%m-%d%ET%H:%M:%S", c);
101 }
FormatCivilTime(CivilMinute c)102 std::string FormatCivilTime(CivilMinute c) {
103 return FormatYearAnd("-%m-%d%ET%H:%M", c);
104 }
FormatCivilTime(CivilHour c)105 std::string FormatCivilTime(CivilHour c) {
106 return FormatYearAnd("-%m-%d%ET%H", c);
107 }
FormatCivilTime(CivilDay c)108 std::string FormatCivilTime(CivilDay c) { return FormatYearAnd("-%m-%d", c); }
FormatCivilTime(CivilMonth c)109 std::string FormatCivilTime(CivilMonth c) { return FormatYearAnd("-%m", c); }
FormatCivilTime(CivilYear c)110 std::string FormatCivilTime(CivilYear c) { return FormatYearAnd("", c); }
111
ParseCivilTime(string_view s,CivilSecond * c)112 bool ParseCivilTime(string_view s, CivilSecond* c) {
113 return ParseYearAnd("-%m-%d%ET%H:%M:%S", s, c);
114 }
ParseCivilTime(string_view s,CivilMinute * c)115 bool ParseCivilTime(string_view s, CivilMinute* c) {
116 return ParseYearAnd("-%m-%d%ET%H:%M", s, c);
117 }
ParseCivilTime(string_view s,CivilHour * c)118 bool ParseCivilTime(string_view s, CivilHour* c) {
119 return ParseYearAnd("-%m-%d%ET%H", s, c);
120 }
ParseCivilTime(string_view s,CivilDay * c)121 bool ParseCivilTime(string_view s, CivilDay* c) {
122 return ParseYearAnd("-%m-%d", s, c);
123 }
ParseCivilTime(string_view s,CivilMonth * c)124 bool ParseCivilTime(string_view s, CivilMonth* c) {
125 return ParseYearAnd("-%m", s, c);
126 }
ParseCivilTime(string_view s,CivilYear * c)127 bool ParseCivilTime(string_view s, CivilYear* c) {
128 return ParseYearAnd("", s, c);
129 }
130
ParseLenientCivilTime(string_view s,CivilSecond * c)131 bool ParseLenientCivilTime(string_view s, CivilSecond* c) {
132 return ParseLenient(s, c);
133 }
ParseLenientCivilTime(string_view s,CivilMinute * c)134 bool ParseLenientCivilTime(string_view s, CivilMinute* c) {
135 return ParseLenient(s, c);
136 }
ParseLenientCivilTime(string_view s,CivilHour * c)137 bool ParseLenientCivilTime(string_view s, CivilHour* c) {
138 return ParseLenient(s, c);
139 }
ParseLenientCivilTime(string_view s,CivilDay * c)140 bool ParseLenientCivilTime(string_view s, CivilDay* c) {
141 return ParseLenient(s, c);
142 }
ParseLenientCivilTime(string_view s,CivilMonth * c)143 bool ParseLenientCivilTime(string_view s, CivilMonth* c) {
144 return ParseLenient(s, c);
145 }
ParseLenientCivilTime(string_view s,CivilYear * c)146 bool ParseLenientCivilTime(string_view s, CivilYear* c) {
147 return ParseLenient(s, c);
148 }
149
150 namespace time_internal {
151
operator <<(std::ostream & os,CivilYear y)152 std::ostream& operator<<(std::ostream& os, CivilYear y) {
153 return os << FormatCivilTime(y);
154 }
operator <<(std::ostream & os,CivilMonth m)155 std::ostream& operator<<(std::ostream& os, CivilMonth m) {
156 return os << FormatCivilTime(m);
157 }
operator <<(std::ostream & os,CivilDay d)158 std::ostream& operator<<(std::ostream& os, CivilDay d) {
159 return os << FormatCivilTime(d);
160 }
operator <<(std::ostream & os,CivilHour h)161 std::ostream& operator<<(std::ostream& os, CivilHour h) {
162 return os << FormatCivilTime(h);
163 }
operator <<(std::ostream & os,CivilMinute m)164 std::ostream& operator<<(std::ostream& os, CivilMinute m) {
165 return os << FormatCivilTime(m);
166 }
operator <<(std::ostream & os,CivilSecond s)167 std::ostream& operator<<(std::ostream& os, CivilSecond s) {
168 return os << FormatCivilTime(s);
169 }
170
AbslParseFlag(string_view s,CivilSecond * c,std::string *)171 bool AbslParseFlag(string_view s, CivilSecond* c, std::string*) {
172 return ParseLenientCivilTime(s, c);
173 }
AbslParseFlag(string_view s,CivilMinute * c,std::string *)174 bool AbslParseFlag(string_view s, CivilMinute* c, std::string*) {
175 return ParseLenientCivilTime(s, c);
176 }
AbslParseFlag(string_view s,CivilHour * c,std::string *)177 bool AbslParseFlag(string_view s, CivilHour* c, std::string*) {
178 return ParseLenientCivilTime(s, c);
179 }
AbslParseFlag(string_view s,CivilDay * c,std::string *)180 bool AbslParseFlag(string_view s, CivilDay* c, std::string*) {
181 return ParseLenientCivilTime(s, c);
182 }
AbslParseFlag(string_view s,CivilMonth * c,std::string *)183 bool AbslParseFlag(string_view s, CivilMonth* c, std::string*) {
184 return ParseLenientCivilTime(s, c);
185 }
AbslParseFlag(string_view s,CivilYear * c,std::string *)186 bool AbslParseFlag(string_view s, CivilYear* c, std::string*) {
187 return ParseLenientCivilTime(s, c);
188 }
AbslUnparseFlag(CivilSecond c)189 std::string AbslUnparseFlag(CivilSecond c) { return FormatCivilTime(c); }
AbslUnparseFlag(CivilMinute c)190 std::string AbslUnparseFlag(CivilMinute c) { return FormatCivilTime(c); }
AbslUnparseFlag(CivilHour c)191 std::string AbslUnparseFlag(CivilHour c) { return FormatCivilTime(c); }
AbslUnparseFlag(CivilDay c)192 std::string AbslUnparseFlag(CivilDay c) { return FormatCivilTime(c); }
AbslUnparseFlag(CivilMonth c)193 std::string AbslUnparseFlag(CivilMonth c) { return FormatCivilTime(c); }
AbslUnparseFlag(CivilYear c)194 std::string AbslUnparseFlag(CivilYear c) { return FormatCivilTime(c); }
195
196 } // namespace time_internal
197
198 ABSL_NAMESPACE_END
199 } // namespace absl
200