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