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