openMSX
Date.cc
Go to the documentation of this file.
1 #include "Date.hh"
2 #include <concepts>
3 #include <iomanip>
4 #include <sstream>
5 
6 namespace openmsx::Date {
7 
8 const char* const days[7] = {
9  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
10 };
11 
12 const char* const months[12] = {
13  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
14  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
15 };
16 
17 template<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 
30 time_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 
151 std::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