root/trunk/whisperlib/common/base/date.cc

Revision 7, 10.1 kB (checked in by whispercastorg, 2 years ago)

version 0.2.0

Line 
1 // Copyright (c) 2009, Whispersoft s.r.l.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Whispersoft s.r.l. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Cosmin Tudorache
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/time.h>
36
37 #include "common/base/log.h"
38 #include "common/base/errno.h"
39 #include "common/base/strutil.h"
40
41 #include "common/base/date.h"
42
43 namespace timer {
44
45 const char* Date::MonthName(int month_number) {
46   switch ( month_number ) {
47   case 0:  return "January";
48   case 1:  return "February";
49   case 2:  return "March";
50   case 3:  return "April";
51   case 4:  return "May";
52   case 5:  return "June";
53   case 6:  return "July";
54   case 7:  return "August";
55   case 8:  return "September";
56   case 9:  return "October";
57   case 10: return "November";
58   case 11: return "December";
59   default:
60     return NULL;
61   };
62 }
63
64 const char* Date::DayOfTheWeekName(int day_of_the_week_number) {
65   switch ( day_of_the_week_number ) {
66   case 0: return "Sunday";
67   case 1: return "Monday";
68   case 2: return "Tuesday";
69   case 3: return "Wednesday";
70   case 4: return "Thursday";
71   case 5: return "Friday";
72   case 6: return "Saturday";
73   default:
74     return NULL;
75   };
76 }
77
78 int64 Date::Now() {
79   struct timeval tv;
80   struct timezone tz;
81
82   int result = ::gettimeofday(&tv, &tz);
83   if ( result != 0 ) {
84     LOG_ERROR << "gettimeofday failed: " << GetLastSystemErrorDescription();
85     return 0;
86   }
87
88   return ((static_cast<int64>(tv.tv_sec)) * static_cast<int64>(1000) +
89           tv.tv_usec / static_cast<int64>(1000));
90 }
91 //static
92 int64 Date::ShiftDay(int64 time, int32 days, bool is_utc) {
93   int64 shift_time = time + 24LL * 3600 * 1000 * days;
94   if ( is_utc ) {
95     return shift_time;
96   }
97   Date d(time, is_utc);
98   Date s(shift_time, is_utc);
99   if ( s.GetHour() == d.GetHour() ) {
100     // no daylight saving change in the 'days' interval
101     return shift_time;
102   }
103   // daylight saving change, we need to restore the hour
104   s.Set(s.GetYear(),
105         s.GetMonth(),
106         s.GetDay(),
107         d.GetHour(),
108         s.GetMinute(),
109         s.GetSecond(),
110         s.GetMilisecond());
111   return s.GetTime();
112 }
113
114 Date::Date(bool use_utc)
115   : time_(0),
116     broken_down_milisecond_(0),
117     is_utc_(use_utc),
118     has_errors_(false) {
119   memset(&broken_down_time_, 0, sizeof(broken_down_time_));
120   SetNow();
121 }
122
123 Date::Date(int64 time, bool use_utc)
124   : time_(0),
125     broken_down_milisecond_(0),
126     is_utc_(use_utc),
127     has_errors_(false) {
128   SetTime(time);
129 }
130 #if __WORDSIZE != 64
131 Date::Date(time_t time, bool use_utc)
132   : time_(0),
133     broken_down_milisecond_(0),
134     is_utc_(use_utc),
135     has_errors_(false) {
136   SetTime(1000LL * time);
137 }
138 #endif
139 Date::Date(int year, int month, int day, int hour,
140            int minute, int second, int milisecond, bool use_utc)
141   : time_(0),
142     broken_down_milisecond_(0),
143     is_utc_(false),
144     has_errors_(false) {
145   has_errors_ = !Set(year, month, day,
146                      hour, minute, second, milisecond,
147                      use_utc);
148 }
149
150 Date::Date(const Date& date)
151   : time_(date.GetTime()),
152     is_utc_(date.IsUTC()),
153     has_errors_(date.HasErrors()) {
154   SetTime(date.GetTime());
155 }
156
157 ///////////////////////////////////////////////////////////////////////
158
159 bool Date::Set(int year, int month, int day,
160                int hour, int minute, int second, int milisecond,
161                bool is_utc) {
162   if (year < 1900) {
163     LOG_ERROR << "invalid year " << year << ". Should be greater than 1900.";
164     return false;
165   }
166   if ( month > 11 || month < 0 ) {
167     LOG_ERROR << "invalid month " << month << ". Should be in range 0...11.";
168     return false;
169   }
170   if ( day > 31 || day < 1 ) {
171     LOG_ERROR << "invalid day " << day << ". Should be in range 1...31.";
172     return false;
173   }
174   if ( hour > 23 ||  hour < 0 ) {
175     LOG_ERROR << "invalid hour " << hour << ". Should be in range 0...23.";
176     return false;
177   }
178   if ( minute > 59 || minute < 0 ) {
179     LOG_ERROR << "invalid minute " << minute << ". Should be in range 0...59.";
180     return false;
181   }
182   if ( second > 59 || second < 0 ) {
183     LOG_ERROR << "invalid second " << second << ". Should be in range 0...60.";
184     return false;
185   }
186   if ( milisecond > 999 || milisecond < 0 ) {
187     LOG_ERROR << "invalid milisecond " << milisecond
188               << ". Should be in range 0...999.";
189     return false;
190   }
191
192   struct tm t = {0, };
193   t.tm_year = year - 1900;
194   t.tm_mon = month;
195   t.tm_mday = day;
196   t.tm_hour = hour;
197   t.tm_min = minute;
198   t.tm_sec = second;
199   t.tm_isdst = -1;
200   time_t tt = ::mktime(&t);  // mktime always treats t as localtime
201   if ( tt == -1 ) {
202     LOG_ERROR << "mktime() failed: " << GetLastSystemErrorDescription();
203     return false;
204   }
205   // tt = seconds since Epoch until t as localtime
206
207   // If we need seconds until t as UTC we need to shift tt by gmt offset.
208   if ( is_utc ) {
209     struct tm asLocalTime;
210     struct tm * result = ::localtime_r(&tt, &asLocalTime);
211     if ( result == NULL ) {
212       LOG_ERROR << "localtime_r() failed: "
213                 << GetLastSystemErrorDescription();
214       return false;
215     }
216
217     tt += asLocalTime.tm_gmtoff;
218   }
219   const int64 milis_since_epoch =
220       (static_cast<int64>(tt)) * static_cast<int64>(1000) + milisecond;
221
222   SetUTC(is_utc);
223   SetTime(milis_since_epoch);
224
225   // now check that we obtained the same year/month/day/.. values
226   DCHECK_EQ(year, GetYear());
227   DCHECK_EQ(month, GetMonth());
228   DCHECK_EQ(day, GetDay());
229   DCHECK_EQ(hour, GetHour());
230   DCHECK_EQ(minute, GetMinute());
231   DCHECK_EQ(second, GetSecond());
232   DCHECK_EQ(milisecond, GetMilisecond());
233
234   return true;
235 }
236
237 // date: the milliseconds since January 1, 1970, 00:00:00 GMT.
238 void Date::SetTime(int64 time) {
239   time_ = time;
240   broken_down_milisecond_ = static_cast<int>((time % static_cast<int64>(1000)));
241   time_t tt = time_t(time / static_cast<int64>(1000));
242   struct tm * result = IsUTC() ? ::gmtime_r(&tt, &broken_down_time_) :
243                                  ::localtime_r(&tt, &broken_down_time_);
244   if ( result == NULL ) {
245     has_errors_ = true;
246     LOG_ERROR << (IsUTC() ? "gmtime_r()" : "localtime_r()") << " failed: "
247               << GetLastSystemErrorDescription();
248   } else {
249     has_errors_ = false;
250   }
251 }
252
253 void Date::SetNow() {
254   const int64 milis_since_epoch = Now();
255   SetTime(milis_since_epoch);
256
257   // check time_
258   CHECK(has_errors_ || GetTime() == milis_since_epoch)
259     << " has_errors_: " << has_errors_
260     << " GetTime(): " << GetTime()
261     << " milis_since_epoch: " << milis_since_epoch;
262 }
263
264 void Date::SetUTC(bool use_utc) {
265   is_utc_ = use_utc;
266   SetTime(time_);  // updates broken down time
267 }
268
269 Date& Date::operator=(const Date& dt) {
270   time_ = dt.time_;
271   broken_down_time_ = dt.broken_down_time_;
272   broken_down_milisecond_ = dt.broken_down_milisecond_;
273   is_utc_ = dt.is_utc_;
274   return *this;
275 }
276
277 bool Date::operator==(const Date& dt) const {
278   return time_ == dt.time_;
279 }
280
281 bool Date::SetFromShortString(const string& s, bool is_utc) {
282   if ( s.size() != 19 ) {
283     return false;
284   }
285   int num_dash = 0;
286   for ( int i = 0; i < s.size(); ++i ) {
287     const char c = s[i];
288     if ( c < '0' || c > '9' ) {
289       if ( c == '-' ) {
290         ++num_dash;
291       } else {
292         return false;
293       }
294     }
295   }
296   if ( num_dash != 2 ) {
297     return false;
298   }
299   if ( s[8] != '-' || s[15] != '-' ) {
300     return false;
301   }
302   return Set(atoi(s.substr(0, 4).c_str()),  // year
303              atoi(s.substr(4, 2).c_str()) - 1,  // month,
304              atoi(s.substr(6, 2).c_str()),  // day,
305              atoi(s.substr(9, 2).c_str()),  // hour
306              atoi(s.substr(11, 2).c_str()),  // min
307              atoi(s.substr(13, 2).c_str()),  // sec
308              atoi(s.substr(16, 3).c_str()),  // milisecond
309              is_utc);
310 }
311
312 string Date::ToShortString() const {
313   return strutil::StringPrintf("%04d%02d%02d-%02d%02d%02d-%03d",
314                                static_cast<int32>(GetYear()),
315                                static_cast<int32>(GetMonth() + 1),
316                                static_cast<int32>(GetDay()),
317                                static_cast<int32>(GetHour()),
318                                static_cast<int32>(GetMinute()),
319                                static_cast<int32>(GetSecond()),
320                                static_cast<int32>(GetMilisecond()));
321 }
322
323 string Date::ToString() const {
324   return strutil::StringPrintf("%s %d.%s.%d %02d:%02d:%02d.%03d %s",
325                                GetDayOfTheWeekName(),
326                                static_cast<int32>(GetDay()),
327                                GetMonthName(),
328                                static_cast<int32>(GetYear()),
329                                static_cast<int32>(GetHour()),
330                                static_cast<int32>(GetMinute()),
331                                static_cast<int32>(GetSecond()),
332                                static_cast<int32>(GetMilisecond()),
333                                GetTimezoneName());
334 }
335
336 ostream& operator<<(ostream& os, const Date& date) {
337   return os << date.ToString();
338 }
339 }
Note: See TracBrowser for help on using the browser.