[MERGE] libi2ncommon: (reinhard) added contains_exact method to interval classes.
[libi2ncommon] / src / timefunc.cpp
CommitLineData
1b5dfd98
TJ
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
e93545dd
GE
10
11#include <string>
12#include <sstream>
13#include <iostream>
14#include <iomanip>
f1499910
GE
15#include <bitset>
16#include <stdexcept>
1b5dfd98
TJ
17#include <iterator>
18#include <algorithm>
e93545dd
GE
19
20#include <time.h>
21#include <sys/timeb.h>
22
23#include <timefunc.hxx>
24#include <i18n.h>
25
26using namespace std;
27
28double 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
41int 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
80string 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
104string 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}
f1499910 113
87869870
GE
114void 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
1344894d 133std::string output_hour_minute(int hour, int minute, bool h_for_00)
2c66f490
GE
134{
135 ostringstream out;
136
137 if (hour >= 0 && hour < 10)
138 out << '0';
139 out << hour;
140
1344894d 141 if (!h_for_00 || minute != 0)
2c66f490
GE
142 {
143 out << ':';
fe223928 144 if (minute >= 0 && minute < 10)
2c66f490
GE
145 out << '0';
146 out << minute;
147 }
148 else
149 out << 'h';
150
151 return out.str();
152}
153
f1499910
GE
154WEEK::WEEK(const std::string& daystring)
155{
156 int len=daystring.length();
157 for (int p=0; p < len; p++)
158 {
865bdeef
GE
159 char nr[2];
160 nr[0]=daystring[p];
161 nr[1]=0;
162 istringstream c(nr);
f1499910
GE
163 int wnr=-1;
164 if (!(c >> wnr) || wnr<0 || wnr >6)
865bdeef 165 throw range_error("illegal weekday >"+string(nr)+"< in "+daystring);
f1499910
GE
166
167 days.set(wnr);
168 }
169}
170
171std::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
181std::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
230std::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
244std::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
277std::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}
4e157d1d
TJ
309
310string 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}
1b5dfd98
TJ
360
361
362/*
363** implementaion of Interval
364*/
365
d181c3bc
TJ
366
367/**
368 * @brief clears the interval (make it empty).
369 */
370void Interval::clear()
371{
372 m_lower_bound = m_upper_bound = 0;
373} // eo Interval::clear()
374
375
1b5dfd98
TJ
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 */
381bool 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 */
399bool 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
412Intervals::Intervals()
413{
414} // eo Intervals::Intervals
415
416
d181c3bc
TJ
417void Intervals::clear()
418{
419 m_intervals.clear();
420} // eo Intervals::clear()
421
1b5dfd98
TJ
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 */
427bool 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 */
447bool 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.
ebc3b584
TJ
467 *
468 * @internal complexity O(n).
1b5dfd98
TJ
469 */
470void 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;
80f30818 505 current_frame.m_changed = true;
1b5dfd98
TJ
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;
80f30818 522 current_frame.m_changed= true;
1b5dfd98
TJ
523 }
524
525 // now let's extend the end:
526 current_frame.m_upper_bound = new_frame.m_upper_bound;
80f30818 527 current_frame.m_changed = true;
1b5dfd98
TJ
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.
ebc3b584
TJ
560 *
561 * @internal complexity O(n).
1b5dfd98
TJ
562 */
563void 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:
80f30818
TJ
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 }
1b5dfd98
TJ
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;
80f30818 610 current_frame.m_changed= true;
1b5dfd98
TJ
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 */
635bool 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/**
e156de7c
TJ
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 */
658bool 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/**
1b5dfd98
TJ
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.
ebc3b584
TJ
680 *
681 * So this method has a complexity of O(n).
1b5dfd98
TJ
682 */
683bool 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());
ebc3b584 710} // eo Intervals::contains(const Intervals&) const
1b5dfd98
TJ
711
712
ebc3b584
TJ
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 */
1b5dfd98
TJ
721bool 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
728Intervals& Intervals::operator+=(const Interval& other)
729{
730 add(other);
731 return *this;
732} // eo operator+=(const Interval&)
733
734
735Intervals& Intervals::operator-=(const Interval& other)
736{
737 sub(other);
738 return *this;
739} // eo operator-=(const Interval&)
740
741
ebc3b584
TJ
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 */
1b5dfd98
TJ
752Intervals& 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
ebc3b584
TJ
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 */
1b5dfd98
TJ
774Intervals& 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&)