[MERGE] libi2ncommon: (reinhard) added changed flag and a weak mark to Interval.
[libi2ncommon] / src / timefunc.cpp
1 /** @file
2  * @brief time related functions.
3  *
4  * @copyright Copyright © 2001-2008 by Intra2net AG
5  * @license commercial
6  * @contact info@intra2net.com
7  *
8  */
9
10
11 #include <string>
12 #include <sstream>
13 #include <iostream>
14 #include <iomanip>
15 #include <bitset>
16 #include <stdexcept>
17 #include <iterator>
18 #include <algorithm>
19
20 #include <time.h>
21 #include <sys/timeb.h>
22
23 #include <timefunc.hxx>
24 #include <i18n.h>
25
26 using namespace std;
27
28 double prec_time(void)
29 {
30     struct timeb tb;
31     double ret;
32
33     ftime(&tb);
34
35     ret=tb.time+(static_cast<float>(tb.millitm)/1000);
36
37     return ret;
38 }
39
40 // converts ISO-DATE: 2003-06-13
41 int date_to_seconds(const std::string &date)
42 {
43     int rtn = -1, year = -1, month = -1, day = -1;
44     
45     string::size_type pos = date.find("-");
46     if (pos == string::npos)
47         return rtn;
48     
49     istringstream in(string(date,0,pos));
50     in >> year;
51     year -= 1900;
52         
53     string dstr(date, pos+1);
54     if ((pos = dstr.find("-")) == string::npos)
55         return rtn;
56     
57     in.clear();
58     in.str(string(dstr, 0, pos));
59     in >> month;
60     month -= 1;
61         
62     in.clear();
63     in.str(string(dstr, pos+1));
64     in >> day;
65     
66     if (year < 0 || month == -1 || day == -1)
67         return rtn;
68     
69     struct tm tm_struct;
70     bzero (&tm_struct, sizeof(struct tm));
71     tm_struct.tm_year = year;
72     tm_struct.tm_mon = month;
73     tm_struct.tm_mday = day;
74     tm_struct.tm_isdst = -1;
75     
76     rtn = mktime (&tm_struct);
77     return rtn;
78 }
79
80 string make_nice_time(int seconds)
81 {
82     ostringstream out;
83
84     int days=seconds/86400;
85     seconds%=86400;
86
87     int hours=seconds/3600;
88     seconds%=3600;
89
90     int minutes=seconds/60;
91     seconds%=60;
92
93     if (days==1)
94         out << i18n("1 day") << ", ";
95     else if (days>1)
96         out << days << ' ' << i18n("days") << ", ";
97
98     out << setfill('0');
99     out << setw(2) << hours << ':' << setw(2) << minutes << ':' << setw(2) << seconds;
100
101     return out.str();
102 }
103
104 string format_full_time(int seconds)
105 {
106     char buf[50];
107     memset (buf, 0, 50);
108     struct tm *ta = localtime ((time_t *)&seconds);
109
110     strftime (buf, 49, "%d.%m.%Y %H:%M", ta);
111     return string(buf);
112 }
113
114 void seconds_to_hour_minute(int seconds, int *hour, int *minute)
115 {
116     if (hour != NULL) {
117         *hour = 0;
118         while (seconds >= 3600) {
119             seconds-=3600;
120             (*hour)++;
121         }
122     }
123
124     if (minute != NULL) {
125         *minute = 0;
126         while (seconds >= 60) {
127             seconds-=60;
128             (*minute)++;
129         }
130     }
131 }
132
133 std::string output_hour_minute(int hour, int minute, bool h_for_00)
134 {
135     ostringstream out;
136     
137     if (hour >= 0 && hour < 10)
138         out << '0';
139     out << hour;
140     
141     if (!h_for_00 || minute != 0)
142     {
143         out << ':';
144         if (minute >= 0 && minute < 10)
145             out << '0';
146         out << minute;
147     }
148     else
149         out << 'h';
150
151     return out.str();
152 }
153
154 WEEK::WEEK(const std::string& daystring)
155 {
156     int len=daystring.length();
157     for (int p=0; p < len; p++)
158     {
159         char nr[2];
160         nr[0]=daystring[p];
161         nr[1]=0;
162         istringstream c(nr);
163         int wnr=-1;
164         if (!(c >> wnr) || wnr<0 || wnr >6)
165             throw range_error("illegal weekday >"+string(nr)+"< in "+daystring);
166             
167         days.set(wnr);
168     }
169 }
170
171 std::string WEEK::get_daystring() const
172 {
173     ostringstream out;
174     for (int i = 0; i < 7; i++)
175         if (days[i])
176             out << i;
177
178     return out.str();
179 }
180
181 std::string WEEK::get_displaystring() const
182 {
183     string weekdays_str;
184
185     // From Monday to Saturday
186     int j;
187     for (int i = 1; i < 7; i++)
188     {
189         if (days[i])
190         {
191             if (!weekdays_str.empty())
192                 weekdays_str += ", ";
193
194             weekdays_str += get_day_display(static_cast<WEEKDAY>(i));
195
196             // check if we can group two or more days
197             j = i;
198             while (days[j] && j < 7)
199                 j++;
200             j--;
201
202             // Sunday end of week? j -> 7
203             if (j-i > 0 && j == 6 && days[0])
204                 j++;
205
206             if (j-i > 1) 
207             {
208                 if (j == 7)
209                     weekdays_str += "-" + get_day_display(SU);
210                 else
211                     weekdays_str += "-" + get_day_display(static_cast<WEEKDAY>(j));
212
213                 i = j;
214             }
215         }
216     }
217
218     // special: sunday
219     if (days[0] && j != 7) 
220     {
221         if (!weekdays_str.empty())
222             weekdays_str += ", ";
223
224         weekdays_str += get_day_display(SU);
225     }
226
227     return weekdays_str;
228 }
229
230 std::string WEEK::get_netfilterstring() const
231 {
232     string out;
233     for (int i = 0; i < 7; i++)
234         if (days[i])
235         {
236             if (!out.empty())
237                 out+=","; 
238             out+=get_english_display(static_cast<WEEKDAY>(i));;
239         }
240             
241     return out;
242 }
243
244 std::string WEEK::get_day_display(WEEKDAY day)
245 {
246     string weekday_str;
247
248     switch (day) {
249         case MO:
250             weekday_str = i18n("Mon");
251             break;
252         case TU:
253             weekday_str = i18n("Tue");
254             break;
255         case WE:
256             weekday_str = i18n("Wed");
257             break;
258         case TH:
259             weekday_str = i18n("Thu");
260             break;
261         case FR:
262             weekday_str = i18n("Fri");
263             break;
264         case SA:
265             weekday_str = i18n("Sat");
266             break;
267         case SU:
268             weekday_str = i18n("Sun");
269             break;
270         default:
271             break;
272     }
273
274     return weekday_str;
275 }
276
277 std::string WEEK::get_english_display(WEEKDAY day)
278 {
279     string weekday_str;
280
281     switch (day) {
282         case MO:
283             weekday_str = "Mon";
284             break;
285         case TU:
286             weekday_str = "Tue";
287             break;
288         case WE:
289             weekday_str = "Wed";
290             break;
291         case TH:
292             weekday_str = "Thu";
293             break;
294         case FR:
295             weekday_str = "Fri";
296             break;
297         case SA:
298             weekday_str = "Sat";
299             break;
300         case SU:
301             weekday_str = "Sun";
302             break;
303         default:
304             break;
305     }
306
307     return weekday_str;
308 }
309
310 string get_month_name(unsigned char month)
311 {
312     string rtn;
313     switch(month) {
314         case 1:
315             rtn = i18n("January");
316             break;
317         case 2:
318             rtn = i18n("February");
319             break;
320         case 3:
321             rtn = i18n("March");
322             break;
323         case 4:
324             rtn = i18n("April");
325             break;
326         case 5:
327             rtn = i18n("May");
328             break;
329         case 6:
330             rtn = i18n("June");
331             break;
332         case 7:
333             rtn = i18n("July");
334             break;
335         case 8:
336             rtn = i18n("August");
337             break;
338         case 9:
339             rtn = i18n("September");
340             break;
341         case 10:
342             rtn = i18n("October");
343             break;
344         case 11:
345             rtn = i18n("November");
346             break;
347         case 12:
348             rtn = i18n("December");
349             break;
350         default:
351             {
352                 ostringstream out;
353                 out << i18n("Illegal month:") << " " << month;
354                 rtn = out.str();
355             }
356     }
357
358     return rtn;
359 }
360
361
362 /*
363 ** implementaion of Interval
364 */
365
366
367 /**
368  * @brief clears the interval (make it empty).
369  */
370 void Interval::clear()
371 {
372     m_lower_bound = m_upper_bound = 0;
373 } // eo Interval::clear()
374
375
376 /**
377  * @brief tests if there is some overlapping with another interval
378  * @param other the other interval
379  * @return @a true if the two intervals have a non empty intersection.
380  */
381 bool Interval::intersects(const Interval& other) const
382 {
383     return
384         //  // other start within this:
385         (other.m_lower_bound >= m_lower_bound and other.m_lower_bound < m_upper_bound )
386         // // other end within this:
387         or (other.m_upper_bound > m_lower_bound and other.m_upper_bound <= m_upper_bound )
388         //  // other contains this
389         or (other.m_lower_bound <= m_lower_bound and other.m_upper_bound >= m_upper_bound )
390     ;
391 } // eo Interval::intersects(const Interval&)
392
393
394 /**
395  * @brief tests if the current interval (fully) contains another one.
396  * @param other the other interval.
397  * @return @a true if the current interval fully contains the other interval.
398  */
399 bool Interval::contains(const Interval& other) const
400 {
401     return  (other.m_lower_bound >= m_lower_bound)
402         and (other.m_upper_bound <= m_upper_bound)
403     ;
404 } // eo Interval::contains(const Interval& other) const
405
406
407 /*
408 ** implementation of Intervals:
409 */
410
411
412 Intervals::Intervals()
413 {
414 } // eo Intervals::Intervals
415
416
417 void Intervals::clear()
418 {
419     m_intervals.clear();
420 } // eo Intervals::clear()
421
422 /**
423  * @brief tests if one of the intervals of the list intersects with the given interval.
424  * @param other the interval to check for intersection.
425  * @return @a true if there is an intersection.
426  */
427 bool Intervals::intersects(const Interval& other) const
428 {
429     for(const_iterator it= begin();
430         it != end();
431         ++it)
432     {
433         if ( it->intersects(other) )
434         {
435             return true;
436         }
437     }
438     return false;
439 } // eo Intervals::intersects(const Interval&) const
440
441
442 /**
443  * @brief tests if we have at least one intersection with another Intervals instance.
444  * @param other the other instance.
445  * @return @a true if there is an intersection.
446  */
447 bool Intervals::intersects(const Intervals& other) const
448 {
449     for(const_iterator it= begin();
450         it != end();
451         ++it)
452     {
453         if ( other.intersects( *it ) )
454         {
455             return true;
456         }
457     }
458     return false;
459 } // eo Intervals::intersects(const Intervals&) const
460
461
462 /**
463  * @brief adds a new interval to the list.
464  * @param new_frame the new interval.
465  *
466  * Adds the interval to the list and joins overlapping intervals.
467  */
468 void Intervals::add(const Interval& new_frame)
469 {
470     if (not new_frame.is_valid() or new_frame.empty())
471     {
472         // well... we will not insert invalid or empty frames!
473         return;
474     }
475     for (IntervalList::iterator it= m_intervals.begin();
476          it != m_intervals.end();
477          ++it)
478     {
479         Interval& current_frame = *it;
480         if ( new_frame.m_lower_bound > current_frame.m_upper_bound )
481         {
482             // new_frame begins later than current end; go on:
483             continue;
484         }
485         // at this point: the begin of the new frame is less then the current end.
486         // now let's determine how we can insert the new frame:
487
488         if ( new_frame.m_upper_bound < current_frame.m_lower_bound )
489         {
490             // new disjoint frame; insert it before the current frame:
491             m_intervals.insert( it, new_frame );
492             // and we are done.
493             return;
494         }
495         // at this point: the end of the new frame is >= current begin. 
496         if ( new_frame.m_upper_bound <= current_frame.m_upper_bound )
497         {
498             // the end of the new frame is within our current frame; we need to combine
499             if (new_frame.m_lower_bound < current_frame.m_lower_bound)
500             {
501                 // the new interval starts earlier; we need to adjust our current frame:
502                 current_frame.m_lower_bound = new_frame.m_lower_bound;
503                 current_frame.m_changed = true;
504             }
505             // NOTE no "else" part needed since in that case our current frame already
506             // contains the new one!
507
508             // we are done:
509             return;
510         }
511         // at this point: end of new frame > end of current frame
512         // so we need to extend the current frame; at least the end.
513         // But we need to deal with intersects of following frames... *sigh*
514
515         // first the simple part: let's see if we need to move the start:
516         if ( new_frame.m_lower_bound < current_frame.m_lower_bound)
517         {
518             // yes, we need to move the start:
519             current_frame.m_lower_bound = new_frame.m_lower_bound;
520             current_frame.m_changed= true;
521         }
522
523         // now let's extend the end:
524         current_frame.m_upper_bound = new_frame.m_upper_bound;
525         current_frame.m_changed = true;
526
527         // well... let's walk through the following frames; looking for more joins...:
528         IntervalList::iterator it2 = it;
529         while(      ++(it2=it) != m_intervals.end()
530                 and current_frame.m_upper_bound >= it2->m_lower_bound
531            )
532         {
533             Interval next_frame= *it2;
534             if ( current_frame.m_upper_bound < next_frame.m_upper_bound )
535             {
536                 // in this case our end is within the next frame.
537                 // adjust our end.
538                 current_frame.m_upper_bound = next_frame.m_upper_bound;
539             }
540             // and remove the next frame since the current frame contains it (now):
541             m_intervals.erase(it2);
542         }
543         // we are done!
544         return;
545     }
546     // at this point: new frame starts later than the last frame ends
547     // append the new frame:
548     m_intervals.push_back( new_frame );
549 } // eo Intervals::add(const Interval&)
550
551
552 /**
553  * @brief subtracts a time interval from the list.
554  * @param del_frame the time interval to subtract.
555  *
556  * removes the time interval from the list; cut off parts from or remove existing
557  * intervals if they overlap.
558  */
559 void Intervals::sub(const Interval& del_frame)
560 {
561     if (not del_frame.is_valid() or del_frame.empty() )
562     {
563         return;
564     }
565     for (IntervalList::iterator it= m_intervals.begin();
566          it != m_intervals.end();
567          )
568     {
569         Interval& current_frame = *it;
570         if ( del_frame.m_lower_bound >= current_frame.m_upper_bound )
571         {
572             // del_frame begins later than current end; go on:
573             ++it;
574             continue;
575         }
576         // at this point: the begin of the del frame is less then the current end.
577         if ( del_frame.m_upper_bound < current_frame.m_lower_bound )
578         {
579             // end is before our start; nothing to do.
580             return;
581         }
582         // at this point: the end of the del frame is >= current begin.
583         if ( del_frame.m_upper_bound < current_frame.m_upper_bound )
584         {
585             // del frame end point is within our interval.
586             if ( del_frame.m_lower_bound > current_frame.m_lower_bound)
587             {
588                 // the del frame is within our interval... we need to split:
589                 m_intervals.insert(it, Interval( current_frame.m_lower_bound, del_frame.m_lower_bound ) );
590             }
591             // adjust start of current frame:
592             if (current_frame.m_lower_bound < del_frame.m_upper_bound)
593             {
594                 current_frame.m_lower_bound= del_frame.m_upper_bound;
595                 current_frame.m_changed= true;
596             }
597             // and we are done!
598             return;
599         }
600         // at this point the end of the del frame is >= current end
601         if ( del_frame.m_lower_bound > current_frame.m_lower_bound )
602         {
603             // a part of the current interval needs to be preserved..
604             // move the end.
605             current_frame.m_upper_bound= del_frame.m_lower_bound;
606             current_frame.m_changed= true;
607             // and continue with the next interval:
608             ++it;
609             continue;
610         }
611         // at this point; the whole frame needs to be deleted..
612         if ( it == m_intervals.begin())
613         {
614             m_intervals.erase(it);
615             it= m_intervals.begin();
616         }
617         else
618         {
619             IntervalList::iterator it2= it++;
620             m_intervals.erase(it2);
621         }
622     }
623 } // eo Intervals::sub(const Interval&)
624
625
626 /**
627  * @brief returns if we contain an interval.
628  * @param other the interval to check.
629  * @return @a true if we cover the given interval, too.
630  */
631 bool Intervals::contains(const Interval& other) const
632 {
633     for(const_iterator it= begin();
634         it != end();
635         ++it)
636     {
637         if ( it->contains( other ))
638         {
639             return true;
640         }
641     }
642     return false;
643 } // eo Intervals::contains(const Interval&) const
644
645
646 /**
647  * @brief returns if we contain another interval combination.
648  * @param other the intervals to check.
649  * @return @a true if we cover the given intervals, too.
650  *
651  * @internal we rely on the fact that the lists are sorted and contain
652  * disjoint intervals.
653  */
654 bool Intervals::contains(const Intervals& other) const
655 {
656     const_iterator my_it= begin();
657     const_iterator other_it= other.begin();
658     while( my_it != end() and other_it!= other.end() )
659     {
660         // seek the first interval which contains the lower bound of the current other interval
661         while (my_it != end()
662                and my_it->m_lower_bound > other_it->m_lower_bound
663                and other_it->m_lower_bound >= my_it->m_upper_bound
664               )
665         {
666             ++my_it;
667         }
668         if (my_it == end())
669         {
670             break;
671         }
672         if (not my_it->contains( *other_it ))
673         {
674             // if we don't contain the current other; we're done:
675             return false;
676         }
677         //else check the next other interval:
678         ++other_it;
679     }
680     return (other_it == other.end());
681 } // eo Intervals::comtains(const Intervals&) const
682
683
684 bool Intervals::operator==(const Intervals& other) const
685 {
686     // since we keep sorted lists: just compare the lists :-)
687     return m_intervals == other.m_intervals;
688 } // eo Intervals::operator==(const Intervals&)
689
690
691 Intervals& Intervals::operator+=(const Interval& other)
692 {
693     add(other);
694     return *this;
695 } // eo operator+=(const Interval&)
696
697
698 Intervals& Intervals::operator-=(const Interval& other)
699 {
700     sub(other);
701     return *this;
702 } // eo operator-=(const Interval&)
703
704
705 Intervals& Intervals::operator+=(const Intervals& other)
706 {
707     for(const_iterator it= other.begin();
708         it != other.end();
709         ++it)
710     {
711         add( *it );
712     }
713     return *this;
714 } // eo operator+=(const Intervals&)
715
716
717 Intervals& Intervals::operator-=(const Intervals& other)
718 {
719     if (&other == this)
720     {
721         m_intervals.clear();
722     }
723     else
724     {
725         for(const_iterator it= other.begin();
726             it != other.end();
727             ++it)
728         {
729             sub( *it );
730         }
731     }
732     return *this;
733 } // eo operator-=(const Intervals&)