Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
BroadcastTimeDefs.cxx
Go to the documentation of this file.
1
36#include "BroadcastTimeDefs.hxx"
37
38#include <string>
39
41
42namespace openlcb
43{
44
45extern "C"
46{
47// normally requires _GNU_SOURCE
48char *strptime(const char *, const char *, struct tm *);
49}
50
51//
52// BroadcastTimeDefs::time_to_string()
53//
54std::string BroadcastTimeDefs::time_to_string(int hour, int min)
55{
56 if (hour < 0 || hour > 23)
57 {
58 hour = 0;
59 }
60 if (min < 0 || min > 59)
61 {
62 min = 0;
63 }
64
65 struct tm tm;
66 tm.tm_hour = hour;
67 tm.tm_min = min;
68 char value[6];
69 if (strftime(value, 6, "%R", &tm) != 0)
70 {
71 return value;
72 }
73 else
74 {
75 return "Error";
76 }
77}
78
79//
80// BroadcastTimeDefs::rate_quarters_to_string()
81//
83{
84 std::string result;
85 if (rate < 0)
86 {
87 result.push_back('-');
88 rate = -rate;
89 }
90 uint16_t whole = rate >> 2;
91 uint16_t frac = rate & 0x3;
92
93 result += integer_to_string(whole);
94
95 switch (frac)
96 {
97 default:
98 case 0:
99 result.append(".00");
100 break;
101 case 1:
102 result.append(".25");
103 break;
104 case 2:
105 result.append(".50");
106 break;
107 case 3:
108 result.append(".75");
109 break;
110 }
111 return result;
112}
113
114//
115// BroadcastTimeDefs::date_to_string()
116//
117std::string BroadcastTimeDefs::date_to_string(int year, int month, int day)
118{
119 struct tm tm = {};
120 tm.tm_year = year - 1900;
121 tm.tm_mon = month - 1;
122 tm.tm_mday = day;
123 char value[13];
124 if (strftime(value, 13, "%b %e, %Y", &tm) != 0)
125 {
126 return value;
127 }
128 else
129 {
130 return "Error";
131 }
132}
133
134//
135// BroadcastTimeDefs::string_to_time()
136//
138 const std::string &stime, int *hour, int *min)
139{
140 struct tm tm;
141 if (strptime(stime.c_str(), "%R", &tm) == nullptr)
142 {
143 return false;
144 }
145
146 if (hour)
147 {
148 *hour = tm.tm_hour;
149 }
150 if (min)
151 {
152 *min = tm.tm_min;
153 }
154 return true;
155}
156
157//
158// BroadcastTimeDefs::string_to_rate_quaraters()
159//
160int16_t BroadcastTimeDefs::string_to_rate_quarters(const std::string &srate)
161{
162 const char *rate_c_str = srate.c_str();
163 char *end;
164 float rate = strtof(rate_c_str, &end);
165
166 if (end == rate_c_str)
167 {
168 // None of the string processed.
169 return 4;
170 }
171 if (rate < -512)
172 {
173 // set to minimum valid rate
174 rate = -512;
175 }
176 else if (rate > 511.75)
177 {
178 // set to maximum valid rate
179 rate = 511.75;
180 }
181 rate *= 4;
182 rate += rate < 0 ? -0.5 : 0.5;
183 return rate;
184}
185
186//
187// BroadcastTimeDefs::string_to_date()
188//
190 const std::string &sdate, int *year, int *month, int *day)
191{
192 struct tm tm;
193 memset(&tm, 0, sizeof(tm));
194 if (strptime(sdate.c_str(), "%b %e, %Y", &tm) == nullptr)
195 {
196 return false;
197 }
198
199 // newlib does not have the proper boundary checking for strptime().
200 // Therefore we use mktime() to determine if the time we have is really
201 // valid or not. In newlib, mktime() can actually correct some invalid
202 // struct tm values by making some educated guesses.
203 //
204 // While glibc does have proper boundary checking for strptime(), it
205 // can still use mktime() to correct some invalid struct tm values by
206 // making some educated guesses.
207 time_t t = mktime(&tm);
208
209 // newlib does not correctly set the errno value when mktime()
210 // encounters an error. Instead it "only" returns -1, which is technically
211 // a valid time. We are counting on the fact that we zeroed out the struct
212 // tm above, and subsequently -1 cannot be an expected result.
213 if (t == (time_t)-1)
214 {
215 return false;
216 }
217
218 if (tm.tm_year < (0 - 1900) || tm.tm_year > (4095 - 1900))
219 {
220 // Out of range for openlcb.
221 return false;
222 }
223 *year = tm.tm_year + 1900;
224 *month = tm.tm_mon + 1;
225 *day = tm.tm_mday;
226 return true;
227}
228
230{
231 int r = string_to_rate_quarters(*srate);
232 string out = rate_quarters_to_string(r);
233 if (out != *srate)
234 {
235 *srate = std::move(out);
236 return true;
237 }
238 return false;
239}
240
242{
243 int y = 1970, m = 1, d = 1;
244 string_to_date(*sdate, &y, &m, &d);
245 string out = date_to_string(y, m, d);
246 if (out != *sdate)
247 {
248 *sdate = std::move(out);
249 return true;
250 }
251 return false;
252}
253
255{
256 int h = 0, m = 0;
257 string_to_time(*stime, &h, &m);
258 string out = time_to_string(h, m);
259 if (out != *stime)
260 {
261 *stime = std::move(out);
262 return true;
263 }
264 return false;
265}
266
267} // namespace openlcb
static std::string time_to_string(int hour, int min)
Convert time in integer hours/minutes to a string ("hh:mm").
static std::string rate_quarters_to_string(int16_t rate)
Convert rate in integer rate quarters to a string (float).
static bool string_to_time(const std::string &stime, int *hour, int *min)
Convert a string (hh:mm) to hour and minute component integers.
static int16_t string_to_rate_quarters(const std::string &srate)
Convert a string (float) to rate quarters.
static bool canonicalize_time_string(std::string *stime)
Verifies that a user-provided string parses as time, and canonicalizes the string format.
static std::string date_to_string(int year, int month, int day)
Converts a date to a string "Mmm dd, yyyy".
static bool canonicalize_rate_string(std::string *srate)
Verifies that a user-provided string parses as rate quarters, and canonicalizes the string format.
static bool canonicalize_date_string(std::string *sdate)
Verifies that a user-provided string parses as date, and canonicalizes the string format.
static bool string_to_date(const std::string &sdate, int *year, int *month, int *day)
Converts a (user-provided) string "Mmm dd, yyyy" to date.