Merge branch 'daemon-ext'
[libi2ncommon] / src / week.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 #include <week.hpp>
21
22 #include <sstream>
23 #include <stdexcept>
24
25 #include <i18n.h>
26
27 using namespace std;
28
29 namespace I2n {
30 namespace Time {
31
32 /**
33  * Constructor
34  * @param daystring Active days as string. 0 is Sunday. Call is_sane() afterwards.
35  */
36 Week::Week(const std::string& daystring)
37 {
38     IsValid = set(daystring);
39 }
40
41 /**
42  * Constructor
43  * @param days Active days as bitset. 0 is Sunday
44  */
45 Week::Week(const std::bitset<7> &days)
46     : Days(days)
47     , IsValid(true)
48 {
49 }
50
51 /**
52  * Default constructor
53  */
54 Week::Week()
55     : IsValid(true)
56 {
57 }
58
59 /**
60  * Check if this Week object is valid, f.e. after set().
61  * @return True if valid, false otherwise
62  */
63 bool Week::is_valid() const
64 {
65     return IsValid;
66 }
67
68 /**
69  * Clear all active days
70  */
71 void Week::clear()
72 {
73     Days.reset();
74     IsValid = true;
75 }
76
77 /**
78  * Set active days as string. Clears unset values.
79  * Active days will remain untouched if there's an error
80  * which you can query via is_valid().
81  * @param daystring Active days as string
82  * @return True if all is fine, false otherwise
83  */
84 bool Week::set(const std::string& daystring)
85 {
86     bitset<7> new_days;
87     IsValid = true;
88
89     string::size_type len=daystring.length();
90     for (string::size_type p=0; p < len; p++)
91     {
92         char nr[2];
93         nr[0]=daystring[p];
94         nr[1]=0;
95         istringstream c(nr);
96         unsigned int wnr=7;
97         if (!(c >> wnr) || wnr > 6)
98         {
99             IsValid = false;
100             return IsValid;
101         }
102
103         new_days.set(wnr);
104     }
105     // Atomic switch-over
106     Days = new_days;
107
108     return IsValid;
109 }
110
111 /**
112  * Set or disable one weekday.
113  * @param day Day to modify
114  * @param value True for active, false to disable
115  * @return True if all is fine, false otherwise
116  */
117 bool Week::set(const WeekDay day, bool value)
118 {
119     if (day >= _WeekDay_END)
120     {
121         IsValid = false;
122         return IsValid;
123     }
124
125     Days.set(static_cast<size_t>(day), value);
126
127     return IsValid;
128 }
129
130 /**
131  * Check if a particular day is active
132  * @param day Day to check
133  * @return True if day is active, false otherwise
134  */
135 bool Week::get(WeekDay day) const
136 {
137     return Days.test(static_cast<size_t>(day));
138 }
139
140 /**
141  * Check if a particular day is active
142  * @param day Day to check
143  * @return True if day is active, false otherwise
144  */
145 bool Week::is_set(WeekDay day) const
146 {
147     return Days.test(static_cast<size_t>(day));
148 }
149
150 /**
151  * Check if all days are active
152  * @return True if all days are active, false otherwise
153  */
154 bool Week::all_set() const
155 {
156     return (Days.count()==7);
157 }
158
159 /**
160  * Check if no days is active
161  * @return True if no day is active, false otherwise
162  */
163 bool Week::none_set() const
164 {
165     return Days.none();
166 }
167
168 /**
169     @brief returns the number of days till the next weekday which is set
170     @param start weekday to start checking
171     @note returns 0 if the start-day is set
172 */
173 unsigned int Week::days_till_set(WeekDay start) const
174 {
175     if (none_set())
176         return 0;
177
178     for (unsigned int days=0; days < 8; days++)
179     {
180         unsigned int check=start+days;
181         if (check > 6)
182             check-=7;
183         if (is_set(static_cast<WeekDay>(check)))
184             return days;
185     }
186
187     throw logic_error("can't find next weekday");
188
189     // fake
190     return 0;                   //lint !e527
191 }
192
193 /**
194     @brief returns the number of days since the previous weekday which is set
195     @param start weekday to start checking
196     @note returns 0 if the start-day is set
197 */
198 unsigned int Week::days_since_set(WeekDay start) const
199 {
200     if (none_set())
201         return 0;
202
203     for (unsigned int days=0; days < 8; days++)
204     {
205         int check=start-static_cast<int>(days);
206         if (check < 0)
207             check+=7;
208         if (is_set(static_cast<WeekDay>(check)))
209             return days;
210     }
211
212     throw logic_error("can't find next weekday");
213
214     // fake
215     return 0;                      //lint !e527
216 }
217
218 /**
219  * Get days as number string compatible for set() like "345".
220  * @return Days as string
221  */
222 std::string Week::get_daystring() const
223 {
224     ostringstream out;
225
226     for (unsigned int i = 0; i < 7; i++)
227         if (Days[i])
228             out << i;
229
230     return out.str();
231 }
232
233 /**
234  * Get days as string presentable to the user
235  * @return Days as string
236  */
237 std::string Week::get_displaystring() const
238 {
239     string weekdays_str;
240
241     // We want to display from Monday to Sunday so reorder
242     // the bitset and make monday bit 0 and sunday bit 6.
243     bitset<7> reordered = Days >> 1;
244     reordered.set(6, Days[0]);
245
246     for (unsigned int i = 0; i < 7; ++i)
247     {
248         // day active?
249         if (!reordered.test(i))
250             continue;
251
252         if (!weekdays_str.empty())
253             weekdays_str += ", ";
254
255         // get day string (revert the reorder)
256         weekdays_str += get_day_display(i == 6 ? Su : static_cast<WeekDay>(i+1));
257
258         // check if we can group two or more days
259         unsigned int group_days = i;
260         while (group_days < 6 && reordered.test(group_days+1))
261             group_days++;
262
263         // Were we able to group two or more days?
264         if (group_days > i+1)
265         {
266             i = group_days;
267             weekdays_str += "-" + get_day_display(i == 6 ? Su : static_cast<WeekDay>(i+1));
268         }
269     } /*lint --e(850) */
270
271     return weekdays_str;
272 }
273
274 /**
275  * Get days as string suitable for netfilter rules
276  * @return 
277  */
278 std::string Week::get_netfilterstring() const
279 {
280     string out;
281
282     for (unsigned int i = 0; i < 7; i++)
283         if (Days[i])
284         {
285             if (!out.empty())
286                 out+=","; 
287             out+=get_english_display(static_cast<WeekDay>(i));;
288         }
289
290     return out;
291 }
292
293 /**
294  * Convert WeekDay to readable string
295  * @param day WeekDay to convert
296  * @return Weekday as string
297  */
298 std::string Week::get_day_display(WeekDay day)
299 {
300     string weekday_str;
301
302     switch (day)
303     {
304         case Mo:
305             weekday_str = i18n("Mon");
306             break;
307         case Tu:
308             weekday_str = i18n("Tue");
309             break;
310         case We:
311             weekday_str = i18n("Wed");
312             break;
313         case Th:
314             weekday_str = i18n("Thu");
315             break;
316         case Fr:
317             weekday_str = i18n("Fri");
318             break;
319         case Sa:
320             weekday_str = i18n("Sat");
321             break;
322         case Su:
323             weekday_str = i18n("Sun");
324             break;
325         default:
326             break;
327     }                                           //lint !e788: Don't complain about unused _WeekDay_END
328
329     return weekday_str;
330 }
331
332 /**
333  * Convert WeekDay to readable string without i18n
334  * @param day WeekDay to convert
335  * @return WeekDay as string in English
336  */
337 std::string Week::get_english_display(WeekDay day)
338 {
339     string weekday_str;
340
341     switch (day)
342     {
343         case Mo:
344             weekday_str = "Mon";
345             break;
346         case Tu:
347             weekday_str = "Tue";
348             break;
349         case We:
350             weekday_str = "Wed";
351             break;
352         case Th:
353             weekday_str = "Thu";
354             break;
355         case Fr:
356             weekday_str = "Fri";
357             break;
358         case Sa:
359             weekday_str = "Sat";
360             break;
361         case Su:
362             weekday_str = "Sun";
363             break;
364         default:
365             break;
366     }                                                           //lint !e788: Don't complain about unused _WeekDay_END
367
368     return weekday_str;
369 }
370
371 }
372 }