140e9b92eSEd Tanous // SPDX-License-Identifier: Apache-2.0
240e9b92eSEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
31b8b02a4SEd Tanous #include "utils/time_utils.hpp"
41b8b02a4SEd Tanous
5a93e9c77SEd Tanous #include "error_messages.hpp"
6a93e9c77SEd Tanous #include "http_response.hpp"
7a93e9c77SEd Tanous #include "logging.hpp"
8a93e9c77SEd Tanous
967b2e53bSEd Tanous #include <version>
1067b2e53bSEd Tanous
11a8770740SEd Tanous #if __cpp_lib_chrono < 201907L
121b8b02a4SEd Tanous #include "utils/extern/date.h"
13a8770740SEd Tanous #endif
141b8b02a4SEd Tanous #include <array>
15a93e9c77SEd Tanous #include <charconv>
161b8b02a4SEd Tanous #include <chrono>
17a93e9c77SEd Tanous #include <cstddef>
18a93e9c77SEd Tanous #include <cstdint>
19a93e9c77SEd Tanous #include <ctime>
20a93e9c77SEd Tanous #include <format>
211b8b02a4SEd Tanous #include <optional>
22a93e9c77SEd Tanous #include <ratio>
231b8b02a4SEd Tanous #include <sstream>
241b8b02a4SEd Tanous #include <string>
251b8b02a4SEd Tanous #include <string_view>
26a93e9c77SEd Tanous #include <system_error>
27a93e9c77SEd Tanous #include <utility>
281b8b02a4SEd Tanous
291b8b02a4SEd Tanous namespace redfish::time_utils
301b8b02a4SEd Tanous {
31c51afd54SEd Tanous
32a93e9c77SEd Tanous /**
33a93e9c77SEd Tanous * @brief Convert string that represents value in Duration Format to its numeric
34a93e9c77SEd Tanous * equivalent.
35a93e9c77SEd Tanous */
fromDurationString(std::string_view v)36a93e9c77SEd Tanous std::optional<std::chrono::milliseconds> fromDurationString(std::string_view v)
37a93e9c77SEd Tanous {
38a93e9c77SEd Tanous std::chrono::milliseconds out = std::chrono::milliseconds::zero();
39a93e9c77SEd Tanous enum class ProcessingStage
40a93e9c77SEd Tanous {
41a93e9c77SEd Tanous // P1DT1H1M1.100S
42a93e9c77SEd Tanous P,
43a93e9c77SEd Tanous Days,
44a93e9c77SEd Tanous Hours,
45a93e9c77SEd Tanous Minutes,
46a93e9c77SEd Tanous Seconds,
47a93e9c77SEd Tanous Milliseconds,
48a93e9c77SEd Tanous Done,
49a93e9c77SEd Tanous };
50a93e9c77SEd Tanous ProcessingStage stage = ProcessingStage::P;
51a93e9c77SEd Tanous
52a93e9c77SEd Tanous while (!v.empty())
53a93e9c77SEd Tanous {
54a93e9c77SEd Tanous if (stage == ProcessingStage::P)
55a93e9c77SEd Tanous {
56a93e9c77SEd Tanous if (v.front() != 'P')
57a93e9c77SEd Tanous {
58a93e9c77SEd Tanous return std::nullopt;
59a93e9c77SEd Tanous }
60a93e9c77SEd Tanous v.remove_prefix(1);
61a93e9c77SEd Tanous stage = ProcessingStage::Days;
62a93e9c77SEd Tanous continue;
63a93e9c77SEd Tanous }
64a93e9c77SEd Tanous if (stage == ProcessingStage::Days)
65a93e9c77SEd Tanous {
66a93e9c77SEd Tanous if (v.front() == 'T')
67a93e9c77SEd Tanous {
68a93e9c77SEd Tanous v.remove_prefix(1);
69a93e9c77SEd Tanous stage = ProcessingStage::Hours;
70a93e9c77SEd Tanous continue;
71a93e9c77SEd Tanous }
72a93e9c77SEd Tanous }
73a93e9c77SEd Tanous uint64_t ticks = 0;
74a93e9c77SEd Tanous auto [ptr, ec] = std::from_chars(v.begin(), v.end(), ticks);
75a93e9c77SEd Tanous if (ec != std::errc())
76a93e9c77SEd Tanous {
77a93e9c77SEd Tanous BMCWEB_LOG_ERROR("Failed to convert string \"{}\" to decimal", v);
78a93e9c77SEd Tanous return std::nullopt;
79a93e9c77SEd Tanous }
80a93e9c77SEd Tanous size_t charactersRead = static_cast<size_t>(ptr - v.data());
81a93e9c77SEd Tanous if (ptr >= v.end())
82a93e9c77SEd Tanous {
83a93e9c77SEd Tanous BMCWEB_LOG_ERROR("Missing postfix");
84a93e9c77SEd Tanous return std::nullopt;
85a93e9c77SEd Tanous }
86a93e9c77SEd Tanous if (*ptr == 'D')
87a93e9c77SEd Tanous {
88a93e9c77SEd Tanous if (stage > ProcessingStage::Days)
89a93e9c77SEd Tanous {
90a93e9c77SEd Tanous return std::nullopt;
91a93e9c77SEd Tanous }
92a93e9c77SEd Tanous out += std::chrono::days(ticks);
93a93e9c77SEd Tanous }
94a93e9c77SEd Tanous else if (*ptr == 'H')
95a93e9c77SEd Tanous {
96a93e9c77SEd Tanous if (stage > ProcessingStage::Hours)
97a93e9c77SEd Tanous {
98a93e9c77SEd Tanous return std::nullopt;
99a93e9c77SEd Tanous }
100a93e9c77SEd Tanous out += std::chrono::hours(ticks);
101a93e9c77SEd Tanous }
102a93e9c77SEd Tanous else if (*ptr == 'M')
103a93e9c77SEd Tanous {
104a93e9c77SEd Tanous if (stage > ProcessingStage::Minutes)
105a93e9c77SEd Tanous {
106a93e9c77SEd Tanous return std::nullopt;
107a93e9c77SEd Tanous }
108a93e9c77SEd Tanous out += std::chrono::minutes(ticks);
109a93e9c77SEd Tanous }
110a93e9c77SEd Tanous else if (*ptr == '.')
111a93e9c77SEd Tanous {
112a93e9c77SEd Tanous if (stage > ProcessingStage::Seconds)
113a93e9c77SEd Tanous {
114a93e9c77SEd Tanous return std::nullopt;
115a93e9c77SEd Tanous }
116a93e9c77SEd Tanous out += std::chrono::seconds(ticks);
117a93e9c77SEd Tanous stage = ProcessingStage::Milliseconds;
118a93e9c77SEd Tanous }
119a93e9c77SEd Tanous else if (*ptr == 'S')
120a93e9c77SEd Tanous {
121a93e9c77SEd Tanous // We could be seeing seconds for the first time, (as is the case in
122a93e9c77SEd Tanous // 1S) or for the second time (in the case of 1.1S).
123a93e9c77SEd Tanous if (stage <= ProcessingStage::Seconds)
124a93e9c77SEd Tanous {
125a93e9c77SEd Tanous out += std::chrono::seconds(ticks);
126a93e9c77SEd Tanous stage = ProcessingStage::Milliseconds;
127a93e9c77SEd Tanous }
128a93e9c77SEd Tanous else if (stage > ProcessingStage::Milliseconds)
129a93e9c77SEd Tanous {
130a93e9c77SEd Tanous BMCWEB_LOG_ERROR("Got unexpected information at end of parse");
131a93e9c77SEd Tanous return std::nullopt;
132a93e9c77SEd Tanous }
133a93e9c77SEd Tanous else
134a93e9c77SEd Tanous {
135a93e9c77SEd Tanous // Seconds could be any form of (1S, 1.1S, 1.11S, 1.111S);
136a93e9c77SEd Tanous // Handle them all milliseconds are after the decimal point,
137a93e9c77SEd Tanous // so they need right padded.
138a93e9c77SEd Tanous if (charactersRead == 1)
139a93e9c77SEd Tanous {
140a93e9c77SEd Tanous ticks *= 100;
141a93e9c77SEd Tanous }
142a93e9c77SEd Tanous else if (charactersRead == 2)
143a93e9c77SEd Tanous {
144a93e9c77SEd Tanous ticks *= 10;
145a93e9c77SEd Tanous }
146a93e9c77SEd Tanous out += std::chrono::milliseconds(ticks);
147a93e9c77SEd Tanous stage = ProcessingStage::Milliseconds;
148a93e9c77SEd Tanous }
149a93e9c77SEd Tanous }
150a93e9c77SEd Tanous else
151a93e9c77SEd Tanous {
152a93e9c77SEd Tanous BMCWEB_LOG_ERROR("Unknown postfix {}", *ptr);
153a93e9c77SEd Tanous return std::nullopt;
154a93e9c77SEd Tanous }
155a93e9c77SEd Tanous
156a93e9c77SEd Tanous v.remove_prefix(charactersRead + 1U);
157a93e9c77SEd Tanous }
158a93e9c77SEd Tanous return out;
159a93e9c77SEd Tanous }
160a93e9c77SEd Tanous
161a93e9c77SEd Tanous /**
162a93e9c77SEd Tanous * @brief Convert time value into duration format that is based on ISO 8601.
163a93e9c77SEd Tanous * Example output: "P12DT1M5.5S"
164a93e9c77SEd Tanous * Ref: Redfish Specification, Section 9.4.4. Duration values
165a93e9c77SEd Tanous */
toDurationString(std::chrono::milliseconds ms)166a93e9c77SEd Tanous std::string toDurationString(std::chrono::milliseconds ms)
167a93e9c77SEd Tanous {
168a93e9c77SEd Tanous if (ms < std::chrono::milliseconds::zero())
169a93e9c77SEd Tanous {
170a93e9c77SEd Tanous return "";
171a93e9c77SEd Tanous }
172a93e9c77SEd Tanous
173a93e9c77SEd Tanous std::chrono::days days = std::chrono::floor<std::chrono::days>(ms);
174a93e9c77SEd Tanous ms -= days;
175a93e9c77SEd Tanous
176a93e9c77SEd Tanous std::chrono::hours hours = std::chrono::floor<std::chrono::hours>(ms);
177a93e9c77SEd Tanous ms -= hours;
178a93e9c77SEd Tanous
179a93e9c77SEd Tanous std::chrono::minutes minutes = std::chrono::floor<std::chrono::minutes>(ms);
180a93e9c77SEd Tanous ms -= minutes;
181a93e9c77SEd Tanous
182a93e9c77SEd Tanous std::chrono::seconds seconds = std::chrono::floor<std::chrono::seconds>(ms);
183a93e9c77SEd Tanous ms -= seconds;
184a93e9c77SEd Tanous std::string daysStr;
185a93e9c77SEd Tanous if (days.count() > 0)
186a93e9c77SEd Tanous {
187a93e9c77SEd Tanous daysStr = std::format("{}D", days.count());
188a93e9c77SEd Tanous }
189a93e9c77SEd Tanous std::string hoursStr;
190a93e9c77SEd Tanous if (hours.count() > 0)
191a93e9c77SEd Tanous {
192a93e9c77SEd Tanous hoursStr = std::format("{}H", hours.count());
193a93e9c77SEd Tanous }
194a93e9c77SEd Tanous std::string minStr;
195a93e9c77SEd Tanous if (minutes.count() > 0)
196a93e9c77SEd Tanous {
197a93e9c77SEd Tanous minStr = std::format("{}M", minutes.count());
198a93e9c77SEd Tanous }
199a93e9c77SEd Tanous std::string secStr;
200a93e9c77SEd Tanous if (seconds.count() != 0 || ms.count() != 0)
201a93e9c77SEd Tanous {
202a93e9c77SEd Tanous secStr = std::format("{}.{:03}S", seconds.count(), ms.count());
203a93e9c77SEd Tanous }
204a93e9c77SEd Tanous
205a93e9c77SEd Tanous return std::format("P{}T{}{}{}", daysStr, hoursStr, minStr, secStr);
206a93e9c77SEd Tanous }
207a93e9c77SEd Tanous
toDurationStringFromUint(uint64_t timeMs)208a93e9c77SEd Tanous std::optional<std::string> toDurationStringFromUint(uint64_t timeMs)
209a93e9c77SEd Tanous {
210a93e9c77SEd Tanous constexpr uint64_t maxTimeMs =
211a93e9c77SEd Tanous static_cast<uint64_t>(std::chrono::milliseconds::max().count());
212a93e9c77SEd Tanous
213a93e9c77SEd Tanous if (maxTimeMs < timeMs)
214a93e9c77SEd Tanous {
215a93e9c77SEd Tanous return std::nullopt;
216a93e9c77SEd Tanous }
217a93e9c77SEd Tanous
218a93e9c77SEd Tanous std::string duration = toDurationString(std::chrono::milliseconds(timeMs));
219a93e9c77SEd Tanous if (duration.empty())
220a93e9c77SEd Tanous {
221a93e9c77SEd Tanous return std::nullopt;
222a93e9c77SEd Tanous }
223a93e9c77SEd Tanous
224a93e9c77SEd Tanous return std::make_optional(duration);
225a93e9c77SEd Tanous }
226a93e9c77SEd Tanous
227a93e9c77SEd Tanous namespace details
228a93e9c77SEd Tanous {
229a93e9c77SEd Tanous // This code is left for support of gcc < 13 which didn't have support for
230a93e9c77SEd Tanous // timezones. It should be removed at some point in the future.
231a93e9c77SEd Tanous #if __cpp_lib_chrono < 201907L
232a93e9c77SEd Tanous
233a93e9c77SEd Tanous // Returns year/month/day triple in civil calendar
234a93e9c77SEd Tanous // Preconditions: z is number of days since 1970-01-01 and is in the range:
235a93e9c77SEd Tanous // [numeric_limits<Int>::min(),
236a93e9c77SEd Tanous // numeric_limits<Int>::max()-719468].
237a93e9c77SEd Tanous // Algorithm sourced from
238a93e9c77SEd Tanous // https://howardhinnant.github.io/date_algorithms.html#civil_from_days
239a93e9c77SEd Tanous // All constants are explained in the above
240a93e9c77SEd Tanous template <class IntType>
civilFromDays(IntType z)241*504af5a0SPatrick Williams constexpr std::tuple<IntType, unsigned, unsigned> civilFromDays(
242*504af5a0SPatrick Williams IntType z) noexcept
243a93e9c77SEd Tanous {
244a93e9c77SEd Tanous z += 719468;
245a93e9c77SEd Tanous IntType era = (z >= 0 ? z : z - 146096) / 146097;
246a93e9c77SEd Tanous unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
247a93e9c77SEd Tanous unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) /
248a93e9c77SEd Tanous 365; // [0, 399]
249a93e9c77SEd Tanous IntType y = static_cast<IntType>(yoe) + era * 400;
250a93e9c77SEd Tanous unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
251a93e9c77SEd Tanous unsigned mp = (5 * doy + 2) / 153; // [0, 11]
252a93e9c77SEd Tanous unsigned d = doy - (153 * mp + 2) / 5 + 1; // [1, 31]
253a93e9c77SEd Tanous unsigned m = mp < 10 ? mp + 3 : mp - 9; // [1, 12]
254a93e9c77SEd Tanous
255a93e9c77SEd Tanous return std::tuple<IntType, unsigned, unsigned>(y + (m <= 2), m, d);
256a93e9c77SEd Tanous }
257a93e9c77SEd Tanous
258a93e9c77SEd Tanous template <typename IntType, typename Period>
toISO8061ExtendedStr(std::chrono::duration<IntType,Period> t)259a93e9c77SEd Tanous std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> t)
260a93e9c77SEd Tanous {
261a93e9c77SEd Tanous using seconds = std::chrono::duration<int>;
262a93e9c77SEd Tanous using minutes = std::chrono::duration<int, std::ratio<60>>;
263a93e9c77SEd Tanous using hours = std::chrono::duration<int, std::ratio<3600>>;
264a93e9c77SEd Tanous using days = std::chrono::duration<
265a93e9c77SEd Tanous IntType, std::ratio_multiply<hours::period, std::ratio<24>>>;
266a93e9c77SEd Tanous
267a93e9c77SEd Tanous // d is days since 1970-01-01
268a93e9c77SEd Tanous days d = std::chrono::duration_cast<days>(t);
269a93e9c77SEd Tanous
270a93e9c77SEd Tanous // t is now time duration since midnight of day d
271a93e9c77SEd Tanous t -= d;
272a93e9c77SEd Tanous
273a93e9c77SEd Tanous // break d down into year/month/day
274a93e9c77SEd Tanous int year = 0;
275a93e9c77SEd Tanous int month = 0;
276a93e9c77SEd Tanous int day = 0;
277a93e9c77SEd Tanous std::tie(year, month, day) = details::civilFromDays(d.count());
278a93e9c77SEd Tanous // Check against limits. Can't go above year 9999, and can't go below epoch
279a93e9c77SEd Tanous // (1970)
280a93e9c77SEd Tanous if (year >= 10000)
281a93e9c77SEd Tanous {
282a93e9c77SEd Tanous year = 9999;
283a93e9c77SEd Tanous month = 12;
284a93e9c77SEd Tanous day = 31;
285a93e9c77SEd Tanous t = days(1) - std::chrono::duration<IntType, Period>(1);
286a93e9c77SEd Tanous }
287a93e9c77SEd Tanous else if (year < 1970)
288a93e9c77SEd Tanous {
289a93e9c77SEd Tanous year = 1970;
290a93e9c77SEd Tanous month = 1;
291a93e9c77SEd Tanous day = 1;
292a93e9c77SEd Tanous t = std::chrono::duration<IntType, Period>::zero();
293a93e9c77SEd Tanous }
294a93e9c77SEd Tanous
295a93e9c77SEd Tanous hours hr = std::chrono::duration_cast<hours>(t);
296a93e9c77SEd Tanous t -= hr;
297a93e9c77SEd Tanous
298a93e9c77SEd Tanous minutes mt = std::chrono::duration_cast<minutes>(t);
299a93e9c77SEd Tanous t -= mt;
300a93e9c77SEd Tanous
301a93e9c77SEd Tanous seconds se = std::chrono::duration_cast<seconds>(t);
302a93e9c77SEd Tanous
303a93e9c77SEd Tanous t -= se;
304a93e9c77SEd Tanous
305a93e9c77SEd Tanous std::string subseconds;
306a93e9c77SEd Tanous if constexpr (std::is_same_v<typename decltype(t)::period, std::milli>)
307a93e9c77SEd Tanous {
308a93e9c77SEd Tanous using MilliDuration = std::chrono::duration<int, std::milli>;
309a93e9c77SEd Tanous MilliDuration subsec = std::chrono::duration_cast<MilliDuration>(t);
310a93e9c77SEd Tanous subseconds = std::format(".{:03}", subsec.count());
311a93e9c77SEd Tanous }
312a93e9c77SEd Tanous else if constexpr (std::is_same_v<typename decltype(t)::period, std::micro>)
313a93e9c77SEd Tanous {
314a93e9c77SEd Tanous using MicroDuration = std::chrono::duration<int, std::micro>;
315a93e9c77SEd Tanous MicroDuration subsec = std::chrono::duration_cast<MicroDuration>(t);
316a93e9c77SEd Tanous subseconds = std::format(".{:06}", subsec.count());
317a93e9c77SEd Tanous }
318a93e9c77SEd Tanous
319a93e9c77SEd Tanous return std::format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}{}+00:00", year,
320a93e9c77SEd Tanous month, day, hr.count(), mt.count(), se.count(),
321a93e9c77SEd Tanous subseconds);
322a93e9c77SEd Tanous }
323a93e9c77SEd Tanous
324a93e9c77SEd Tanous #else
325a93e9c77SEd Tanous
326a93e9c77SEd Tanous template <typename IntType, typename Period>
327a93e9c77SEd Tanous
328a93e9c77SEd Tanous std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> dur)
329a93e9c77SEd Tanous {
330a93e9c77SEd Tanous using namespace std::literals::chrono_literals;
331a93e9c77SEd Tanous
332a93e9c77SEd Tanous using SubType = std::chrono::duration<IntType, Period>;
333a93e9c77SEd Tanous
334a93e9c77SEd Tanous // d is days since 1970-01-01
335a93e9c77SEd Tanous std::chrono::days days = std::chrono::floor<std::chrono::days>(dur);
336a93e9c77SEd Tanous std::chrono::sys_days sysDays(days);
337a93e9c77SEd Tanous std::chrono::year_month_day ymd(sysDays);
338a93e9c77SEd Tanous
339a93e9c77SEd Tanous // Enforce 3 constraints
340a93e9c77SEd Tanous // the result cant under or overflow the calculation
341a93e9c77SEd Tanous // the resulting string needs to be representable as 4 digits
342a93e9c77SEd Tanous // The resulting string can't be before epoch
343a93e9c77SEd Tanous if (dur.count() <= 0)
344a93e9c77SEd Tanous {
345a93e9c77SEd Tanous BMCWEB_LOG_WARNING("Underflow from value {}", dur.count());
346a93e9c77SEd Tanous ymd = 1970y / std::chrono::January / 1d;
347a93e9c77SEd Tanous dur = std::chrono::duration<IntType, Period>::zero();
348a93e9c77SEd Tanous }
349a93e9c77SEd Tanous else if (dur > SubType::max() - std::chrono::days(1))
350a93e9c77SEd Tanous {
351a93e9c77SEd Tanous BMCWEB_LOG_WARNING("Overflow from value {}", dur.count());
352a93e9c77SEd Tanous ymd = 9999y / std::chrono::December / 31d;
353a93e9c77SEd Tanous dur = std::chrono::days(1) - SubType(1);
354a93e9c77SEd Tanous }
355a93e9c77SEd Tanous else if (ymd.year() >= 10000y)
356a93e9c77SEd Tanous {
357a93e9c77SEd Tanous BMCWEB_LOG_WARNING("Year {} not representable", ymd.year());
358a93e9c77SEd Tanous ymd = 9999y / std::chrono::December / 31d;
359a93e9c77SEd Tanous dur = std::chrono::days(1) - SubType(1);
360a93e9c77SEd Tanous }
361a93e9c77SEd Tanous else if (ymd.year() < 1970y)
362a93e9c77SEd Tanous {
363a93e9c77SEd Tanous BMCWEB_LOG_WARNING("Year {} not representable", ymd.year());
364a93e9c77SEd Tanous ymd = 1970y / std::chrono::January / 1d;
365a93e9c77SEd Tanous dur = SubType::zero();
366a93e9c77SEd Tanous }
367a93e9c77SEd Tanous else
368a93e9c77SEd Tanous {
369a93e9c77SEd Tanous // t is now time duration since midnight of day d
370a93e9c77SEd Tanous dur -= days;
371a93e9c77SEd Tanous }
372a93e9c77SEd Tanous std::chrono::hh_mm_ss<SubType> hms(dur);
373a93e9c77SEd Tanous
374a93e9c77SEd Tanous return std::format("{}T{}+00:00", ymd, hms);
375a93e9c77SEd Tanous }
376a93e9c77SEd Tanous
377a93e9c77SEd Tanous #endif
378a93e9c77SEd Tanous } // namespace details
379a93e9c77SEd Tanous
380a93e9c77SEd Tanous // Returns the formatted date time string.
381a93e9c77SEd Tanous // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
382a93e9c77SEd Tanous // the given |secondsSinceEpoch| is too large, we return the maximum supported
383a93e9c77SEd Tanous // date.
getDateTimeUint(uint64_t secondsSinceEpoch)384a93e9c77SEd Tanous std::string getDateTimeUint(uint64_t secondsSinceEpoch)
385a93e9c77SEd Tanous {
386a93e9c77SEd Tanous using DurationType = std::chrono::duration<uint64_t>;
387a93e9c77SEd Tanous DurationType sinceEpoch(secondsSinceEpoch);
388a93e9c77SEd Tanous return details::toISO8061ExtendedStr(sinceEpoch);
389a93e9c77SEd Tanous }
390a93e9c77SEd Tanous
391a93e9c77SEd Tanous // Returns the formatted date time string with millisecond precision
392a93e9c77SEd Tanous // Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
393a93e9c77SEd Tanous // the given |secondsSinceEpoch| is too large, we return the maximum supported
394a93e9c77SEd Tanous // date.
getDateTimeUintMs(uint64_t milliSecondsSinceEpoch)395a93e9c77SEd Tanous std::string getDateTimeUintMs(uint64_t milliSecondsSinceEpoch)
396a93e9c77SEd Tanous {
397a93e9c77SEd Tanous using DurationType = std::chrono::duration<uint64_t, std::milli>;
398a93e9c77SEd Tanous DurationType sinceEpoch(milliSecondsSinceEpoch);
399a93e9c77SEd Tanous return details::toISO8061ExtendedStr(sinceEpoch);
400a93e9c77SEd Tanous }
401a93e9c77SEd Tanous
402a93e9c77SEd Tanous // Returns the formatted date time string with microsecond precision
getDateTimeUintUs(uint64_t microSecondsSinceEpoch)403a93e9c77SEd Tanous std::string getDateTimeUintUs(uint64_t microSecondsSinceEpoch)
404a93e9c77SEd Tanous {
405a93e9c77SEd Tanous using DurationType = std::chrono::duration<uint64_t, std::micro>;
406a93e9c77SEd Tanous DurationType sinceEpoch(microSecondsSinceEpoch);
407a93e9c77SEd Tanous return details::toISO8061ExtendedStr(sinceEpoch);
408a93e9c77SEd Tanous }
409a93e9c77SEd Tanous
getDateTimeStdtime(std::time_t secondsSinceEpoch)410a93e9c77SEd Tanous std::string getDateTimeStdtime(std::time_t secondsSinceEpoch)
411a93e9c77SEd Tanous {
412a93e9c77SEd Tanous using DurationType = std::chrono::duration<std::time_t>;
413a93e9c77SEd Tanous DurationType sinceEpoch(secondsSinceEpoch);
414a93e9c77SEd Tanous return details::toISO8061ExtendedStr(sinceEpoch);
415a93e9c77SEd Tanous }
416a93e9c77SEd Tanous
417a93e9c77SEd Tanous /**
418a93e9c77SEd Tanous * Returns the current Date, Time & the local Time Offset
419a93e9c77SEd Tanous * information in a pair
420a93e9c77SEd Tanous *
421a93e9c77SEd Tanous * @param[in] None
422a93e9c77SEd Tanous *
423a93e9c77SEd Tanous * @return std::pair<std::string, std::string>, which consist
424a93e9c77SEd Tanous * of current DateTime & the TimeOffset strings respectively.
425a93e9c77SEd Tanous */
getDateTimeOffsetNow()426a93e9c77SEd Tanous std::pair<std::string, std::string> getDateTimeOffsetNow()
427a93e9c77SEd Tanous {
428a93e9c77SEd Tanous std::time_t time = std::time(nullptr);
429a93e9c77SEd Tanous std::string dateTime = getDateTimeStdtime(time);
430a93e9c77SEd Tanous
431a93e9c77SEd Tanous /* extract the local Time Offset value from the
432a93e9c77SEd Tanous * received dateTime string.
433a93e9c77SEd Tanous */
434a93e9c77SEd Tanous std::string timeOffset("Z00:00");
435a93e9c77SEd Tanous std::size_t lastPos = dateTime.size();
436a93e9c77SEd Tanous std::size_t len = timeOffset.size();
437a93e9c77SEd Tanous if (lastPos > len)
438a93e9c77SEd Tanous {
439a93e9c77SEd Tanous timeOffset = dateTime.substr(lastPos - len);
440a93e9c77SEd Tanous }
441a93e9c77SEd Tanous
442a93e9c77SEd Tanous return std::make_pair(dateTime, timeOffset);
443a93e9c77SEd Tanous }
444a93e9c77SEd Tanous
445a93e9c77SEd Tanous using usSinceEpoch = std::chrono::duration<int64_t, std::micro>;
446a93e9c77SEd Tanous
447a93e9c77SEd Tanous /**
448a93e9c77SEd Tanous * @brief Returns the datetime in ISO 8601 format
449a93e9c77SEd Tanous *
450a93e9c77SEd Tanous * @param[in] std::string_view the date of item manufacture in ISO 8601 format,
451a93e9c77SEd Tanous * either as YYYYMMDD or YYYYMMDDThhmmssZ
452a93e9c77SEd Tanous * Ref: https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/yaml/
453a93e9c77SEd Tanous * xyz/openbmc_project/Inventory/Decorator/Asset.interface.yaml#L16
454a93e9c77SEd Tanous *
455a93e9c77SEd Tanous * @return std::string which consist the datetime
456a93e9c77SEd Tanous */
getDateTimeIso8601(std::string_view datetime)457a93e9c77SEd Tanous std::optional<std::string> getDateTimeIso8601(std::string_view datetime)
458a93e9c77SEd Tanous {
459a93e9c77SEd Tanous std::optional<usSinceEpoch> us = dateStringToEpoch(datetime);
460a93e9c77SEd Tanous if (!us)
461a93e9c77SEd Tanous {
462a93e9c77SEd Tanous return std::nullopt;
463a93e9c77SEd Tanous }
464a93e9c77SEd Tanous auto secondsDuration =
465a93e9c77SEd Tanous std::chrono::duration_cast<std::chrono::seconds>(*us);
466a93e9c77SEd Tanous
467a93e9c77SEd Tanous return std::make_optional(
468a93e9c77SEd Tanous getDateTimeUint(static_cast<uint64_t>(secondsDuration.count())));
469a93e9c77SEd Tanous }
470a93e9c77SEd Tanous
471a93e9c77SEd Tanous /**
472a93e9c77SEd Tanous * @brief ProductionDate report
473a93e9c77SEd Tanous */
productionDateReport(crow::Response & res,const std::string & buildDate)474a93e9c77SEd Tanous void productionDateReport(crow::Response& res, const std::string& buildDate)
475a93e9c77SEd Tanous {
476a93e9c77SEd Tanous std::optional<std::string> valueStr = getDateTimeIso8601(buildDate);
477a93e9c77SEd Tanous if (!valueStr)
478a93e9c77SEd Tanous {
479a93e9c77SEd Tanous messages::internalError();
480a93e9c77SEd Tanous return;
481a93e9c77SEd Tanous }
482a93e9c77SEd Tanous res.jsonValue["ProductionDate"] = *valueStr;
483a93e9c77SEd Tanous }
484a93e9c77SEd Tanous
dateStringToEpoch(std::string_view datetime)4851b8b02a4SEd Tanous std::optional<usSinceEpoch> dateStringToEpoch(std::string_view datetime)
4861b8b02a4SEd Tanous {
487352e3b78SHieu Huynh for (const char* format : std::to_array(
488352e3b78SHieu Huynh {"%FT%T%Ez", "%FT%TZ", "%FT%T", "%Y%m%d", "%Y%m%dT%H%M%SZ"}))
4891b8b02a4SEd Tanous {
4901b8b02a4SEd Tanous // Parse using signed so we can detect negative dates
491c51afd54SEd Tanous std::chrono::sys_time<usSinceEpoch> date;
4921b8b02a4SEd Tanous std::istringstream iss(std::string{datetime});
4931b8b02a4SEd Tanous #if __cpp_lib_chrono >= 201907L
4941b8b02a4SEd Tanous namespace chrono_from_stream = std::chrono;
4951b8b02a4SEd Tanous #else
4961b8b02a4SEd Tanous namespace chrono_from_stream = date;
4971b8b02a4SEd Tanous #endif
4981b8b02a4SEd Tanous if (chrono_from_stream::from_stream(iss, format, date))
4991b8b02a4SEd Tanous {
5001b8b02a4SEd Tanous if (date.time_since_epoch().count() < 0)
5011b8b02a4SEd Tanous {
5021b8b02a4SEd Tanous return std::nullopt;
5031b8b02a4SEd Tanous }
5041b8b02a4SEd Tanous if (iss.rdbuf()->in_avail() != 0)
5051b8b02a4SEd Tanous {
5061b8b02a4SEd Tanous // More information left at end of string.
5071b8b02a4SEd Tanous continue;
5081b8b02a4SEd Tanous }
509c51afd54SEd Tanous return date.time_since_epoch();
5101b8b02a4SEd Tanous }
5111b8b02a4SEd Tanous }
5121b8b02a4SEd Tanous return std::nullopt;
5131b8b02a4SEd Tanous }
5141b8b02a4SEd Tanous } // namespace redfish::time_utils
515