libanna.time refactoring
[anna.git] / include / anna / time / Date.hpp
1 // ANNA - Anna is Not Nothingness Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 #ifndef anna_time_Date_hpp
38 #define anna_time_Date_hpp
39
40 #include <anna/core/RuntimeException.hpp>
41 #include <anna/core/mt/Mutex.hpp>
42
43 // Standard
44 #include <time.h>
45
46 // Local
47 #include <anna/time/internal/Timezone.hpp>
48
49
50 namespace anna {
51 namespace xml {
52 class Node;
53 }
54 }
55
56 //------------------------------------------------------------------------------
57 //---------------------------------------------------------------------- #define
58 //------------------------------------------------------------------------------
59
60 // Unix time reference:  01/01/1970 00:00:00 UTC
61 // NTP time reference:   01/01/1900 00:00:00 UTC
62 // -> 70 years have 2207520000 seconds.
63 // -> Between 1900 and 1970 there are 17 leap years: 1468800 seconds more (17 days)
64 // TOTAL DIFFERENCE = 2207520000 + 1468800 = 2208988800
65 #define TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970   2208988800U
66 //#define _2K38_EFFECT_LIMIT                       "20380119031407" // UTC
67
68
69 namespace anna {
70
71 namespace time {
72
73
74 /**
75 * Absolute time representation structs class manager.
76 * Allow any time assignment/extraction regarding any timezone.
77 * Uses shell TZ assignment or rely on system timezone configuration (in ubuntu, /etc/timezone
78 * stores the abbreviation and /etc/localtime keeps the timezone file from /usr/share/zoneinfo).
79 *
80 * The internal unix timestamp reamain constant and thedate representation depends on the
81 * configured timezone. Some store methods could specify a certain origin timezone, but the
82 * instance inner timezone won't be changed until the user want to do that.
83 *
84 * <pre>
85 *
86 *  Sample code:
87 *
88 *  anna::time::functions::initialize();
89 *  std::cout << "SystemTimezone: " << anna::time::functions::getSystemTimezone().asString() << std::endl;
90 *
91 *  anna::time::Date myBirth ("CET");
92 *  myBirth.store("19741219111500", "EET");
93 *  std::cout << "myBirth: " << myBirth.asString() << std::endl;
94 *
95 *  anna::time::Date same_moment_canary_island("GMT");
96 *  same_moment_canary_island.store(myBirth.getUnixTimestamp());
97 *  std::cout << "same_moment_canary_island: " << same_moment_canary_island.asString() << std::endl;
98 *  myBirth.setTz("GMT");
99 *  std::cout << "myBirth on GMT: " << myBirth.asString() << std::endl;
100 *  std::cout << "EQUAL: same_moment_canary_island = " << same_moment_canary_island.yyyymmddHHmmss() << "; myBirth in context GMT = " << myBirth.yyyymmddHHmmss() << std::endl;
101 *  same_moment_canary_island.setTz("CET");
102 *  std::cout << "same_moment_canary_island on CET: " << same_moment_canary_island.asString() << std::endl;
103 *  anna::time::Date birthday("EET");
104 *  birthday.store(same_moment_canary_island.getTm(), "CET");
105 *  std::cout << "EQUAL: birthday TS = " << birthday.getUnixTimestamp() << "; myBirth TS = " << myBirth.getUnixTimestamp() << std::endl;
106 *  std::cout << "birthday EET = " << birthday.asString() << std::endl;
107 *
108 *  std::cout << "Setting Local timezone ..." << std::endl;
109 *  myBirth.setSystemTimezone();
110 *  birthday.setSystemTimezone();
111 *  std::cout << "EQUAL: birthday " << birthday.asString() << "; myBirth " << myBirth.asString() << std::endl;
112 *  std::cout << "EQUAL: birthday (Local Timezone)= " << birthday.yyyymmddHHmmss() << "; myBirth (Local Timezone)= " << myBirth.yyyymmddHHmmss() << std::endl;
113 *  std::cout << "myBirth TZ  = " << myBirth.getTzAsString() << std::endl;
114 *  std::cout << "birthday TZ  = " << myBirth.getTzAsString() << std::endl;
115 *
116 *  myBirth.setTz("GMT");
117 *  std::cout << "myBirth in GMT = " << myBirth.yyyymmddHHmmss() << std::endl;
118 *
119 *  Program output:
120 *
121 *  SystemTimezone: <TZ unset\>
122 *  myBirth: Thursday 19/12/1974 10:15:00 CET, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
123 *  same_moment_canary_island: Thursday 19/12/1974 09:15:00 GMT, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
124 *  myBirth on GMT: Thursday 19/12/1974 09:15:00 GMT, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
125 *  EQUAL: same_moment_canary_island = 19741219091500; myBirth in context GMT = 19741219091500
126 *  same_moment_canary_island on CET: Thursday 19/12/1974 10:15:00 CET, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
127 *  EQUAL: birthday TS = 156676500; myBirth TS = 156676500
128 *  birthday EET = Thursday 19/12/1974 11:15:00 EET, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
129 *  Setting Local timezone ...
130 *  EQUAL: birthday Thursday 19/12/1974 10:15:00 <TZ unset\>, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>; myBirth Thursday 19/12/1974 10:15:00 <TZ unset\>, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
131 *  EQUAL: birthday (Local Timezone)= 19741219101500; myBirth (Local Timezone)= 19741219101500
132 *  myBirth TZ  = <TZ unset\>
133 *  birthday TZ  = <TZ unset\>
134 *  myBirth in GMT = 19741219091500
135 *
136 * </pre>
137 */
138
139 class Date {
140   /////////////////////////////////////////////////
141   // Key: Keep coherent all time representations //
142   /////////////////////////////////////////////////
143
144   // Main data:
145   Timezone a_tz;            // timezone for this instance context
146   time_t a_timestamp; // unix timestamp
147
148   // Secondary data:
149   struct tm a_tm;
150
151
152   // sets the current date/time (time(NULL)) and provided TimeZone as described in constructor
153   void initialize(const char *TZ = NULL) throw();
154
155   // main refresh method regarding timestamp
156   void refresh(void) throw();
157
158   anna::Mutex a_mutex;
159
160 public:
161
162   /**
163   * Default Constructor
164   * @param TZ timezone for date/time representation. Default (NULL) assumes the system
165   * timezone configuration. Empty string will be interpreted as UTC timezone.
166   */
167   Date(const char *TZ = NULL);
168
169   /**
170   * Copy Constructor
171   */
172   Date(const Date & d) { *this = d; }
173
174   /**
175   * Destructor
176   */
177   ~Date() {;}
178
179
180   // setters
181
182   /**
183   * Sets the current date/time (time(NULL)) and provided TimeZone as described in constructor
184   */
185   void setNow(const char *TZ = NULL) throw() { initialize(TZ); }
186
187
188   /**
189   * Sets the provided timezone for date/time representation of the class instance.
190   * This method keeps invariant the unix timestamp, thus, the date representation
191   *  will evolve depending on the configured timezone.
192   * @warning Be careful with incorrect TZ values because no error will be shown but
193   * dates could be misleading. The valid TZ could be seen with tzselect tool or
194   * directly looking at /usr/share/zoneinfo hierarchy.
195   *
196   * @param TZ timezone for date/time representation.
197   * Default (NULL) is the local host (user-supplied) timezone
198   * Empty string sets UTC0 (GMT, Greenwich)
199   * Use available timezones in /usr/share/zoneinfo; i.e.: Europe/Madrid, CET, etc.
200   * You can also use 'tzselect' helper, but that tool don't change the system
201   * timezone, only helps to know the valid TZ values and shows how to change
202   * the timezone settings if you would want to do that).
203   */
204   void setTz(const char *TZ = NULL) throw();
205
206
207   /**
208    Sets the local host timezone (even if it is based on TZ or in /usr/share/zoneinfo)
209   */
210   void setSystemTimezone() throw();
211
212
213   /**
214   * Sets date/time providing unix timestamp (seconds since 01-Jan-1970 GMT)
215   *
216   * @param unixTimestamp Time since 01-Jan-1970 GMT
217   */
218   void store(const time_t & unixTimestamp) throw();
219   void storeUnix(const time_t & unixTimestamp) throw() { store(unixTimestamp); }
220
221
222   /**
223   * Sets date/time providing ntp timestamp (seconds since 01-Jan-1900 GMT)
224   *
225   * @param ntpTimestamp Time since 01-Jan-1900 GMT
226   */
227   void storeNtp(const unsigned int &ntpTimestamp) throw();
228
229
230   /**
231   * Sets date/time from standard 'tm' struct and optional timezone
232   *
233   * @param date 'tm' struct date/time
234   * @param TZ timezone for date/time provided. Default (NULL) assumes the system
235   * timezone configuration. Empty string will be interpreted as UTC timezone.
236   */
237   void store(const struct tm &date, const char *TZ = NULL) throw(anna::RuntimeException);
238
239
240   /**
241   * Sets date/time from string representation with certain provided format,
242   * and optional timezone
243   *
244   * @param stringDate Date/time string
245   * @param TZ timezone for date/time provided. Default (NULL) assumes the system
246   * timezone configuration. Empty string will be interpreted as UTC timezone.
247   * @param strptimeFormat Date/time string format for strptime primitive.
248   * See format syntax at http://man7.org/linux/man-pages/man3/strptime.3.html
249   */
250   void store(const std::string &stringDate, const char *TZ = NULL, const char *strptimeFormat = "%Y%m%d%H%M%S") throw(anna::RuntimeException);
251   //void store(const std::string &stringDate, const char *tz = NULL, const char *strptimeFormat = "%Y-%m-%d-%H-%M-%S") throw(anna::RuntimeException);
252
253
254   /**
255   * Copy Operator
256   *
257   * @param d Source class instance
258   *
259   * @return Returns copied reference
260   */
261   Date & operator = (const Date &d);
262
263   // gets
264
265   // Operators
266
267   /**
268   * Operator ==
269   *
270   * @param d1 First instance from Date class
271   * @param d2 Second instance from Date class
272   *
273   * @return Returns d1 == d2 comparison
274   */
275   friend bool operator == (const Date & d1, const Date & d2) {
276     return (d1.getUnixTimestamp() == d2.getUnixTimestamp());
277   }
278
279   /**
280   * Operator !=
281   *
282   * @param d1 First instance from Date class
283   * @param d2 Second instance from Date class
284   *
285   * @return Returns d1 != d2 comparison
286   */
287   friend bool operator != (const Date & d1, const Date & d2) {
288     return (d1.getUnixTimestamp() != d2.getUnixTimestamp());
289   }
290
291   /**
292   * Operator <=
293   *
294   * @param d1 First instance from Date class
295   * @param d2 Second instance from Date class
296   *
297   * @return Returns d1 <= d2 comparison (compare unix timestamp unix, ignore TZ)
298   */
299   friend bool operator <= (const Date & d1, const Date & d2) {
300     return (d1.getUnixTimestamp() <= d2.getUnixTimestamp());
301   }
302
303   /**
304   * Operator >=
305   *
306   * @param d1 First instance from Date class
307   * @param d2 Second instance from Date class
308   *
309   * @return Returns d1 >= d2 comparison (compare unix timestamp unix, ignore TZ)
310   */
311   friend bool operator >= (const Date & d1, const Date & d2) {
312     return (d1.getUnixTimestamp() >= d2.getUnixTimestamp());
313   }
314
315
316   /**
317   * Gets context instance timezone information
318   *
319   * @return  NULL if TZ is unset, or string for TZ environment variable
320   * (empty is allowed and use to be understood as UTC.
321   */
322   const char *getTz(void) const throw() { return (a_tz.unsetTZ() ? NULL:a_tz.getValue().c_str()); }
323
324
325   /**
326   * Gets the context timezone as string
327   */
328   std::string getTzAsString() const throw() { return a_tz.asString(); }
329
330
331   /**
332   * Gets 'tm' struct on the current timezone
333   *
334   * @return 'tm' struct on the current timezone
335   */
336   const struct tm & getTm(void) const throw() { return a_tm; }
337
338   /**
339   * Gets the week day abbreviation: Sun, Mon, Tue, Wed, Thu, Fri, Sat
340   *
341   * @return Day of the week
342   */
343   const char *getDay(void) const throw();
344
345
346   /**
347   * Gets 'yyyymmddHHmmss' representation for the current timezone
348   *
349   * @return Time/date on format 'yyyymmddHHmmss'
350   *
351   * @see getTz
352   */
353   std::string yyyymmddHHmmss(void) const throw();
354
355
356   /**
357   * Gets unix timestamp (01-Jan-1970 GMT)
358   *
359   * @return Seconds since 01-Jan-1970 GMT
360   */
361   const time_t & getUnixTimestamp(void) const throw() { return a_timestamp; }
362
363
364   /**
365   * Gets ntp timestamp (01-Jan-1900 GMT)
366   *
367   * @return Seconds since 01-Jan-1900 GMT
368   */
369   unsigned int getNtpTimestamp(void) const throw() {
370     unsigned int ntp_timestamp = a_timestamp + TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970;
371     return ntp_timestamp;
372   }
373
374
375   /**
376   * Class string representation
377   *
378   * @return String with class content
379   */
380   std::string asString(void) const throw();
381
382
383   /**
384   * Class XML representation
385   *
386   * @return XML with class content
387   */
388   anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
389
390 };
391
392 }
393 }
394
395 #endif
396