Merge branch 'release-2.140.101'
[feisty_meow.git] / nucleus / library / timely / time_stamp.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : time_stamp                                                        *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 1995-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "earth_time.h"
16 #include "time_stamp.h"
17
18 #include <basis/environment.h>
19 #include <basis/mutex.h>
20 #include <loggers/program_wide_logger.h>
21
22 #include <stdlib.h>
23 #ifdef __WIN32__
24   #define _WINSOCKAPI_  // make windows.h happy about winsock.
25   #include <winsock2.h>  // timeval.
26 #endif
27
28 //#define DEBUG_TIME_STAMP
29
30 #ifdef DEBUG_TIME_STAMP
31   #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
32   using namespace loggers;
33 #endif
34
35 using namespace basis;
36
37 namespace timely {
38
39 static mutex &__uptime_synchronizer() {
40   static mutex uptiming_syncher;
41   return uptiming_syncher;
42 }
43
44 basis::astring time_stamp::notarize(bool add_space)
45 {
46   const time_locus the_time = now();
47   astring to_return;
48   the_time.text_form_long(to_return, clock_time::MILITARY | clock_time::MILLISECONDS);
49   if (add_space) to_return += " ";
50   return to_return;
51 }
52
53 time_stamp::time_stamp() : c_stamp(0) { fill_in_time(); }
54
55 time_stamp::time_stamp(time_representation offset)
56 : c_stamp(0) { reset(offset); }
57
58 void time_stamp::reset() { fill_in_time(); }
59
60 astring time_stamp::text_form(stamp_display_style style) const
61 {
62   time_representation stump = c_stamp;
63   bool past = false;
64   if (style == STAMP_RELATIVE) {
65     // adjust the returned time by subtracting the current time.
66     stump -= get_time_now();
67     if (negative(stump)) {
68       // if we're negative, just note that the stamp is in the past.
69       past = true;
70       stump = absolute_value(stump);
71     }
72   }
73   time_representation divisor = 3600 * SECOND_ms;
74   basis::un_int hours = basis::un_int(stump / divisor);
75   stump -= divisor * time_representation(hours);
76   divisor /= 60;
77   basis::un_int minutes = basis::un_int(stump / divisor);
78   stump -= divisor * time_representation(minutes);
79   divisor /= 60;
80   basis::un_int seconds = basis::un_int(stump / divisor);
81   stump -= divisor * time_representation(seconds);
82   basis::un_int milliseconds = basis::un_int(stump);
83   // make absolutely sure we are between 0 and 999.
84   milliseconds %= 1000;
85
86   astring to_return;
87   bool did_hours = false;
88   if (hours) {
89     to_return += astring(astring::SPRINTF, "%uh:", hours);
90     did_hours = true;
91   }
92   if (minutes || did_hours)
93     to_return += astring(astring::SPRINTF, "%02um:", minutes);
94   to_return += astring(astring::SPRINTF, "%02us.%03u", seconds, milliseconds);
95   if (style == STAMP_RELATIVE) {
96     if (past) to_return += " ago";
97     else to_return += " from now";
98   }
99   return to_return;
100 }
101
102 void time_stamp::fill_in_time()
103 {
104   time_representation current = get_time_now();
105   c_stamp = current;  // reset our own time now.
106 }
107
108 void time_stamp::reset(time_representation offset)
109 {
110   fill_in_time();
111   c_stamp += offset;
112 }
113
114 time_stamp::time_representation time_stamp::get_time_now()
115 { return rolling_uptime(); }
116
117 const double __rollover_point = 2.0 * MAXINT32;
118   // this number is our rollover point for 32 bit integers.
119
120 double time_stamp::rolling_uptime()
121 {
122   auto_synchronizer l(__uptime_synchronizer());
123     // protect our rollover records.
124
125   static basis::un_int __last_ticks = 0;
126   static int __rollovers = 0;
127
128   basis::un_int ticks_up = environment::system_uptime();
129     // acquire the current uptime as a 32 bit unsigned int.
130
131   if (ticks_up < __last_ticks) {
132     // rollover happened.  increment our tracker.
133     __rollovers++;
134   }
135   __last_ticks = ticks_up;
136
137   return double(__rollovers) * __rollover_point + double(ticks_up);
138 }
139
140 void time_stamp::fill_timeval_ms(struct timeval &time_out, int duration)
141 {
142   FUNCDEF("fill_timeval_ms");
143   // timeval has tv_sec=seconds, tv_usec=microseconds.
144   if (!duration) {
145     // duration is immediate for the check; just a quick poll.
146     time_out.tv_sec = 0;
147     time_out.tv_usec = 0;
148 #ifdef DEBUG_TIME_STAMP
149     LOG("no duration specified");
150 #endif
151   } else {
152     // a non-zero duration means we need to compute secs and usecs.
153     time_out.tv_sec = duration / 1000;
154     // set the number of seconds from the input in milliseconds.
155     duration -= time_out.tv_sec * 1000;
156     // now take out the chunk we've already recorded as seconds.
157     time_out.tv_usec = duration * 1000;
158     // set the number of microseconds from the remaining milliseconds.
159 #ifdef DEBUG_TIME_STAMP
160     LOG(a_sprintf("duration of %d ms went to %d sec and %d usec.", duration,
161         time_out.tv_sec, time_out.tv_usec));
162 #endif
163   }
164 }
165
166 } //namespace.
167