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