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