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