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