openMSX
Date.cc
Go to the documentation of this file.
1#include "Date.hh"
2#include <array>
3#include <concepts>
4#include <iomanip>
5#include <sstream>
6#include <string_view>
7
8namespace openmsx::Date {
9
10static constexpr std::array<std::string_view, 7> days = {
11 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
12};
13
14static constexpr std::array<std::string_view, 12> months = {
15 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
16 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
17};
18
19template<bool FIRST, unsigned MUL>
20[[nodiscard]] static constexpr bool parseDigit(unsigned c, std::integral auto& t)
21{
22 c -= '0';
23 if (c > 9) return false;
24 if constexpr (FIRST) {
25 t = c * MUL;
26 } else {
27 t += c * MUL;
28 }
29 return true;
30}
31
32time_t fromString(std::span<const char, 24> s)
33{
34 struct tm tm{};
35
36 // skip day
37
38 // space
39 if (s[3] != ' ') return INVALID_TIME_T;
40
41 // Parse month
42 switch (s[4]) {
43 case 'J': // Jan Jun Jul
44 switch (s[5]) {
45 case 'a': // Jan
46 if (s[6] != 'n') return INVALID_TIME_T;
47 tm.tm_mon = 0; break;
48 case 'u': // Jun Jul
49 switch (s[6]) {
50 case 'n': tm.tm_mon = 5; break;
51 case 'l': tm.tm_mon = 6; break;
52 default: return INVALID_TIME_T;
53 }
54 break;
55 default: return INVALID_TIME_T;
56 }
57 break;
58 case 'F': // Feb
59 if (s[5] != 'e') return INVALID_TIME_T;
60 if (s[6] != 'b') return INVALID_TIME_T;
61 tm.tm_mon = 1;
62 break;
63 case 'M': // Mar May
64 if (s[5] != 'a') return INVALID_TIME_T;
65 switch (s[6]) {
66 case 'r': tm.tm_mon = 2; break;
67 case 'y': tm.tm_mon = 4; break;
68 default: return INVALID_TIME_T;
69 }
70 break;
71 case 'A': // Apr Aug
72 switch (s[5]) {
73 case 'p': // Apr
74 if (s[6] != 'r') return INVALID_TIME_T;
75 tm.tm_mon = 3; break;
76 case 'u': // Aug
77 if (s[6] != 'g') return INVALID_TIME_T;
78 tm.tm_mon = 7; break;
79 default: return INVALID_TIME_T;
80 }
81 break;
82 case 'S': // Sep
83 if (s[5] != 'e') return INVALID_TIME_T;
84 if (s[6] != 'p') return INVALID_TIME_T;
85 tm.tm_mon = 8;
86 break;
87 case 'O': // Oct
88 if (s[5] != 'c') return INVALID_TIME_T;
89 if (s[6] != 't') return INVALID_TIME_T;
90 tm.tm_mon = 9;
91 break;
92 case 'N': // Nov
93 if (s[5] != 'o') return INVALID_TIME_T;
94 if (s[6] != 'v') return INVALID_TIME_T;
95 tm.tm_mon = 10;
96 break;
97 case 'D': // Dec
98 if (s[5] != 'e') return INVALID_TIME_T;
99 if (s[6] != 'c') return INVALID_TIME_T;
100 tm.tm_mon = 11;
101 break;
102 default: return INVALID_TIME_T;
103 }
104
105 // space
106 if (s[7] != ' ') return INVALID_TIME_T;
107
108 // parse mday
109 if (!parseDigit<true, 10>(s[8], tm.tm_mday)) return INVALID_TIME_T;
110 if (!parseDigit<false, 1>(s[9], tm.tm_mday)) return INVALID_TIME_T;
111 if ((tm.tm_mday < 1) || (31 < tm.tm_mday)) return INVALID_TIME_T;
112
113 // space
114 if (s[10] != ' ') return INVALID_TIME_T;
115
116 // parse hour
117 if (!parseDigit<true, 10>(s[11], tm.tm_hour)) return INVALID_TIME_T;
118 if (!parseDigit<false, 1>(s[12], tm.tm_hour)) return INVALID_TIME_T;
119 if ((tm.tm_hour < 0) || (23 < tm.tm_hour)) return INVALID_TIME_T;
120
121 // colon
122 if (s[13] != ':') return INVALID_TIME_T;
123
124 // parse minute
125 if (!parseDigit<true, 10>(s[14], tm.tm_min)) return INVALID_TIME_T;
126 if (!parseDigit<false, 1>(s[15], tm.tm_min)) return INVALID_TIME_T;
127 if ((tm.tm_min < 0) || (59 < tm.tm_min)) return INVALID_TIME_T;
128
129 // colon
130 if (s[16] != ':') return INVALID_TIME_T;
131
132 // parse second
133 if (!parseDigit<true, 10>(s[17], tm.tm_sec)) return INVALID_TIME_T;
134 if (!parseDigit<false, 1>(s[18], tm.tm_sec)) return INVALID_TIME_T;
135 if ((tm.tm_sec < 0) || (59 < tm.tm_sec)) return INVALID_TIME_T;
136
137 // space
138 if (s[19] != ' ') return INVALID_TIME_T;
139
140 // parse year
141 if (!parseDigit<true, 1000>(s[20], tm.tm_year)) return INVALID_TIME_T;
142 if (!parseDigit<false, 100>(s[21], tm.tm_year)) return INVALID_TIME_T;
143 if (!parseDigit<false, 10>(s[22], tm.tm_year)) return INVALID_TIME_T;
144 if (!parseDigit<false, 1>(s[23], tm.tm_year)) return INVALID_TIME_T;
145 tm.tm_year -= 1900;
146 if (tm.tm_year < 0) return INVALID_TIME_T;
147
148 tm.tm_isdst = -1;
149 return mktime(&tm);
150}
151
152std::string toString(time_t time)
153{
154 if (time < 0) time = 0;
155 struct tm* tm = localtime(&time);
156 std::ostringstream sstr;
157 sstr << std::setfill('0')
158 << days [tm->tm_wday] << ' '
159 << months[tm->tm_mon] << ' '
160 << std::setw(2) << tm->tm_mday << ' '
161 << std::setw(2) << tm->tm_hour << ':'
162 << std::setw(2) << tm->tm_min << ':'
163 << std::setw(2) << tm->tm_sec << ' '
164 << std::setw(4) << (tm->tm_year + 1900);
165 return sstr.str();
166}
167
168} // namespace openmsx::Date
TclObject t
std::string toString(time_t time)
Definition Date.cc:152
time_t fromString(std::span< const char, 24 > s)
Definition Date.cc:32
constexpr time_t INVALID_TIME_T
Definition Date.hh:11