[MERGE] libi2ncommon: (reinhard) added contains_exact method to interval classes.
[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  * @internal complexity O(n).
469  */
470 void Intervals::add(const Interval& new_frame)
471 {
472     if (not new_frame.is_valid() or new_frame.empty())
473     {
474         // well... we will not insert invalid or empty frames!
475         return;
476     }
477     for (IntervalList::iterator it= m_intervals.begin();
478          it != m_intervals.end();
479          ++it)
480     {
481         Interval& current_frame = *it;
482         if ( new_frame.m_lower_bound > current_frame.m_upper_bound )
483         {
484             // new_frame begins later than current end; go on:
485             continue;
486         }
487         // at this point: the begin of the new frame is less then the current end.
488         // now let's determine how we can insert the new frame:
489
490         if ( new_frame.m_upper_bound < current_frame.m_lower_bound )
491         {
492             // new disjoint frame; insert it before the current frame:
493             m_intervals.insert( it, new_frame );
494             // and we are done.
495             return;
496         }
497         // at this point: the end of the new frame is >= current begin. 
498         if ( new_frame.m_upper_bound <= current_frame.m_upper_bound )
499         {
500             // the end of the new frame is within our current frame; we need to combine
501             if (new_frame.m_lower_bound < current_frame.m_lower_bound)
502             {
503                 // the new interval starts earlier; we need to adjust our current frame:
504                 current_frame.m_lower_bound = new_frame.m_lower_bound;
505                 current_frame.m_changed = true;
506             }
507             // NOTE no "else" part needed since in that case our current frame already
508             // contains the new one!
509
510             // we are done:
511             return;
512         }
513         // at this point: end of new frame > end of current frame
514         // so we need to extend the current frame; at least the end.
515         // But we need to deal with intersects of following frames... *sigh*
516
517         // first the simple part: let's see if we need to move the start:
518         if ( new_frame.m_lower_bound < current_frame.m_lower_bound)
519         {
520             // yes, we need to move the start:
521             current_frame.m_lower_bound = new_frame.m_lower_bound;
522             current_frame.m_changed= true;
523         }
524
525         // now let's extend the end:
526         current_frame.m_upper_bound = new_frame.m_upper_bound;
527         current_frame.m_changed = true;
528
529         // well... let's walk through the following frames; looking for more joins...:
530         IntervalList::iterator it2 = it;
531         while(      ++(it2=it) != m_intervals.end()
532                 and current_frame.m_upper_bound >= it2->m_lower_bound
533            )
534         {
535             Interval next_frame= *it2;
536             if ( current_frame.m_upper_bound < next_frame.m_upper_bound )
537             {
538                 // in this case our end is within the next frame.
539                 // adjust our end.
540                 current_frame.m_upper_bound = next_frame.m_upper_bound;
541             }
542             // and remove the next frame since the current frame contains it (now):
543             m_intervals.erase(it2);
544         }
545         // we are done!
546         return;
547     }
548     // at this point: new frame starts later than the last frame ends
549     // append the new frame:
550     m_intervals.push_back( new_frame );
551 } // eo Intervals::add(const Interval&)
552
553
554 /**
555  * @brief subtracts a time interval from the list.
556  * @param del_frame the time interval to subtract.
557  *
558  * removes the time interval from the list; cut off parts from or remove existing
559  * intervals if they overlap.
560  *
561  * @internal complexity O(n).
562  */
563 void Intervals::sub(const Interval& del_frame)
564 {
565     if (not del_frame.is_valid() or del_frame.empty() )
566     {
567         return;
568     }
569     for (IntervalList::iterator it= m_intervals.begin();
570          it != m_intervals.end();
571          )
572     {
573         Interval& current_frame = *it;
574         if ( del_frame.m_lower_bound >= current_frame.m_upper_bound )
575         {
576             // del_frame begins later than current end; go on:
577             ++it;
578             continue;
579         }
580         // at this point: the begin of the del frame is less then the current end.
581         if ( del_frame.m_upper_bound < current_frame.m_lower_bound )
582         {
583             // end is before our start; nothing to do.
584             return;
585         }
586         // at this point: the end of the del frame is >= current begin.
587         if ( del_frame.m_upper_bound < current_frame.m_upper_bound )
588         {
589             // del frame end point is within our interval.
590             if ( del_frame.m_lower_bound > current_frame.m_lower_bound)
591             {
592                 // the del frame is within our interval... we need to split:
593                 m_intervals.insert(it, Interval( current_frame.m_lower_bound, del_frame.m_lower_bound ) );
594             }
595             // adjust start of current frame:
596             if (current_frame.m_lower_bound < del_frame.m_upper_bound)
597             {
598                 current_frame.m_lower_bound= del_frame.m_upper_bound;
599                 current_frame.m_changed= true;
600             }
601             // and we are done!
602             return;
603         }
604         // at this point the end of the del frame is >= current end
605         if ( del_frame.m_lower_bound > current_frame.m_lower_bound )
606         {
607             // a part of the current interval needs to be preserved..
608             // move the end.
609             current_frame.m_upper_bound= del_frame.m_lower_bound;
610             current_frame.m_changed= true;
611             // and continue with the next interval:
612             ++it;
613             continue;
614         }
615         // at this point; the whole frame needs to be deleted..
616         if ( it == m_intervals.begin())
617         {
618             m_intervals.erase(it);
619             it= m_intervals.begin();
620         }
621         else
622         {
623             IntervalList::iterator it2= it++;
624             m_intervals.erase(it2);
625         }
626     }
627 } // eo Intervals::sub(const Interval&)
628
629
630 /**
631  * @brief returns if we contain an interval.
632  * @param other the interval to check.
633  * @return @a true if we cover the given interval, too.
634  */
635 bool Intervals::contains(const Interval& other) const
636 {
637     for(const_iterator it= begin();
638         it != end();
639         ++it)
640     {
641         if ( it->contains( other ))
642         {
643             return true;
644         }
645     }
646     return false;
647 } // eo Intervals::contains(const Interval&) const
648
649
650 /**
651  * @brief returns if we contain an exact interval.
652  * @param other the interval to check.
653  * @return @a true if we axactly contains the given interval.
654  *
655  * @note thsi differs from contain in the way, that we return only @a true
656  * iff we have the given interval in our list; not only cover it.
657  */
658 bool Intervals::contains_exact(const Interval& other) const
659 {
660     for(const_iterator it= begin();
661         it != end();
662         ++it)
663     {
664         if ( *it == other)
665         {
666             return true;
667         }
668     }
669     return false;
670 } // eo Intervals::contains_exact(const Interval&)const
671
672
673 /**
674  * @brief returns if we contain another interval combination.
675  * @param other the intervals to check.
676  * @return @a true if we cover the given intervals, too.
677  *
678  * @internal we rely on the fact that the lists are sorted and contain
679  * disjoint intervals.
680  *
681  * So this method has a complexity of O(n).
682  */
683 bool Intervals::contains(const Intervals& other) const
684 {
685     const_iterator my_it= begin();
686     const_iterator other_it= other.begin();
687     while( my_it != end() and other_it!= other.end() )
688     {
689         // seek the first interval which contains the lower bound of the current other interval
690         while (my_it != end()
691                and my_it->m_lower_bound > other_it->m_lower_bound
692                and other_it->m_lower_bound >= my_it->m_upper_bound
693               )
694         {
695             ++my_it;
696         }
697         if (my_it == end())
698         {
699             break;
700         }
701         if (not my_it->contains( *other_it ))
702         {
703             // if we don't contain the current other; we're done:
704             return false;
705         }
706         //else check the next other interval:
707         ++other_it;
708     }
709     return (other_it == other.end());
710 } // eo Intervals::contains(const Intervals&) const
711
712
713 /**
714  * @brief combines to interval combinates for equality
715  * @param other the other instance.
716  * @return @a true if the other is equal to the current.
717  *
718  * @internal since the lists are sorted, we compare the interval lists.
719  * Thus we have a complexity of O(n).
720  */
721 bool Intervals::operator==(const Intervals& other) const
722 {
723     // since we keep sorted lists: just compare the lists :-)
724     return m_intervals == other.m_intervals;
725 } // eo Intervals::operator==(const Intervals&)
726
727
728 Intervals& Intervals::operator+=(const Interval& other)
729 {
730     add(other);
731     return *this;
732 } // eo operator+=(const Interval&)
733
734
735 Intervals& Intervals::operator-=(const Interval& other)
736 {
737     sub(other);
738     return *this;
739 } // eo operator-=(const Interval&)
740
741
742 /**
743  * @brief adds the intervals of a second instance to us.
744  * @param other the other instance.
745  * @return self reference (allow chaining).
746  *
747  * @internal since we do simple loops over the other and our intervals
748  * we have a complexity of O(n^2).
749  *
750  * @todo optimize if complexity becomes a problem.
751  */
752 Intervals& Intervals::operator+=(const Intervals& other)
753 {
754     for(const_iterator it= other.begin();
755         it != other.end();
756         ++it)
757     {
758         add( *it );
759     }
760     return *this;
761 } // eo operator+=(const Intervals&)
762
763
764 /**
765  * @brief subtracts the intervals of a second instance from us.
766  * @param other the other instance.
767  * @return self reference (allow chaining).
768  *
769  * @internal since we do simple loops over the other and our intervals
770  * we have a complexity of O(n^2).
771  *
772  * @todo optimize if complexity becomes a problem.
773  */
774 Intervals& Intervals::operator-=(const Intervals& other)
775 {
776     if (&other == this)
777     {
778         m_intervals.clear();
779     }
780     else
781     {
782         for(const_iterator it= other.begin();
783             it != other.end();
784             ++it)
785         {
786             sub( *it );
787         }
788     }
789     return *this;
790 } // eo operator-=(const Intervals&)