1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // https://bitbucket.org/testillano/anna
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
37 #include <anna/core/tracing/Logger.hpp>
38 #include <anna/core/tracing/TraceWriter.hpp>
39 #include <anna/config/defines.hpp>
40 #include <anna/core/functions.hpp>
41 #include <anna/core/mt/Guard.hpp>
42 #include <anna/xml/xml.hpp>
45 #include <anna/time/Date.hpp>
46 #include <anna/time/functions.hpp>
47 #include <anna/time/internal/sccs.hpp>
50 #include <stdlib.h> // putenv
55 using namespace anna::time;
59 //******************************************************************************
60 //------------------------------------------------------------------------- Date
61 //******************************************************************************
63 //------------------------------------------------------------------------------
64 //----------------------------------------------------------------- Date::Date()
65 //------------------------------------------------------------------------------
66 Date::Date(const char * TzContext) {
67 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "Default Constructor", ANNA_FILE_LOCATION));
68 time::sccs::activate();
70 if(!functions::initialized()) {
71 //anna::Logger::error("You should firstly invoke anna::time::functions::initialize() before using this module", ANNA_FILE_LOCATION);
72 //Perhaps former couldn't be written (no Logger initialize):
73 std::cout << std::endl << "Develop ERROR: you should firstly invoke anna::time::functions::initialize() before using time module" << std::endl;
76 a_local_tz = functions::getLocalTz();
77 a_work_tz = a_local_tz;
78 initialize(TzContext);
82 //------------------------------------------------------------------------------
83 //----------------------------------------------------------------- Date::Date()
84 //------------------------------------------------------------------------------
85 Date::Date(const Date & d) {
86 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "Copy constructor", ANNA_FILE_LOCATION));
87 time::sccs::activate();
89 a_local_tz = functions::getLocalTz();
90 a_work_tz = d.a_work_tz;
94 //------------------------------------------------------------------------------
95 //---------------------------------------------------------------- Date::~Date()
96 //------------------------------------------------------------------------------
98 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "~Date", ANNA_FILE_LOCATION));
104 //------------------------------------------------------------------------------
105 //-------------------------------------------------------------- Date::_putenv()
106 //------------------------------------------------------------------------------
107 void Date::_putenv(const char * Tz) throw() {
108 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "_putenv", ANNA_FILE_LOCATION));
114 providedTz = a_local_tz;
116 if(providedTz != a_work_tz) {
117 a_work_tz = providedTz;
118 // Solaris no tiene el unsetenv, actualizamos directamente con putenv: "unset TZ", que es totalmente valido (*)
119 // if (a_work_tz.isUnset())
122 putenv((char *)a_work_tz.getShellAssignment().c_str());
124 // if (a_work_tz.isUnset())
125 // std::cout << "Unset" << std::endl;
128 // if (a_work_tz.isEmpty())
129 // std::cout << "Empty" << std::endl;
131 // std::cout << a_work_tz.getValue() << std::endl;
133 // std::cout << "Salida putenv poniendo " << a_work_tz.getAssignment().c_str() << ": " << putenv((char *)a_work_tz.getAssignment().c_str()) << std::endl;
134 // const char *kk = getenv("TZ");
135 // if (kk) std::cout << "getenv: " << kk << std::endl;
136 // else std::cout << "getenv: NULL" << std::endl;
141 //------------------------------------------------------------------------------
142 //------------------------------------------------------- Date::set_tz_context()
143 //------------------------------------------------------------------------------
144 void Date::set_tz_context(const char * TzContext) throw() {
145 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "set_tz_context", ANNA_FILE_LOCATION));
147 a_TZ_context.set(TzContext);
149 a_TZ_context = a_local_tz;
151 refresh_regarding_unix_timestamp(); // borns with timestamp and is based on new TZ value
155 //------------------------------------------------------------------------------
156 //------------------------------------------------------- Date::get_TZ_context()
157 //------------------------------------------------------------------------------
158 const TZ & Date::get_TZ_context(void) const throw() {
159 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "get_TZ_context", ANNA_FILE_LOCATION));
160 return (a_TZ_context);
164 //------------------------------------------------------------------------------
165 //---------------------------------------------------------- Date::_initialize()
166 //------------------------------------------------------------------------------
167 void Date::_initialize(const char * TzContext) throw() {
168 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "_initialize", ANNA_FILE_LOCATION));
169 a_unix_timestamp = ::time(NULL);
170 set_tz_context(TzContext); // This refresh all data with the asigned TZ
174 //------------------------------------------------------------------------------
175 //------------------------------------------------- Date::check_yyyymmddHHmmss()
176 //------------------------------------------------------------------------------
177 void Date::check_yyyymmddHHmmss(const std::string & yyyymmddHHmmss)
179 throw(anna::RuntimeException)
182 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "check_yyyymmddHHmmss", ANNA_FILE_LOCATION));
183 // check 2038 effect: restrictive limit
184 if(yyyymmddHHmmss > _2K38_EFFECT_LIMIT) { // ESTO NO LO COMPRUEBA mktime !!
185 throw anna::RuntimeException("Provided time/date is over 2K38 effect (19 January 2038, 03:14:07) !!", ANNA_FILE_LOCATION);
188 // check 14-digit length (i.e. "19741219100000")
189 int longitud = yyyymmddHHmmss.length();
192 throw anna::RuntimeException("Provided time/date has not 14-digit length !!. Remember: 'yyyymmddHHmmss'", ANNA_FILE_LOCATION);
196 const char * ptr_cad = yyyymmddHHmmss.c_str();
198 for(register int k = 0; k < longitud; k++) {
199 if(!isdigit(ptr_cad[k])) {
200 throw anna::RuntimeException("Provided time/date only can contain digits !!. Remember: 'yyyymmddHHmmss'", ANNA_FILE_LOCATION);
205 // int year, mon, mday, hour, min, sec;
206 // sscanf(ptr_cad, STRING_FORMAT_yyyymmddHHmmss, &year, &mon, &mday, &hour, &min, &sec);
207 // //year 2038 already checked
208 // if (mon < 1 || mon > 12)
209 // throw anna::RuntimeException("Month out of range (1-12) !!", ANNA_FILE_LOCATION);
211 // if (mon == 2) top_day = 29/*28*/;
212 // if (mon == 4 || mon == 6 || mon == 9 || mon == 11) top_day = 30;
214 // if (mday < 1 || mday > top_day)
215 // throw anna::RuntimeException(anna::functions::asString("Day out of range (1-%d) !!", top_day), ANNA_FILE_LOCATION);
217 // throw anna::RuntimeException("Hour out of range (0-23) !!", ANNA_FILE_LOCATION);
218 // if (min < 0 || min > 59)
219 // throw anna::RuntimeException("Minute out of range (0-59) !!", ANNA_FILE_LOCATION);
220 // if (sec < 0 || sec > 59)
221 // throw anna::RuntimeException("Second out of range (0-59) !!", ANNA_FILE_LOCATION);
225 //------------------------------------------------------------------------------
226 //------------------------------------- Date::refresh_regarding_unix_timestamp()
227 //------------------------------------------------------------------------------
228 void Date::refresh_regarding_unix_timestamp(void) throw() {
229 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "refresh_regarding_unix_timestamp", ANNA_FILE_LOCATION));
230 anna::Guard guard(a_mutex);
231 //anna::Guard<anna::Mutex> guard(a_mutex);
232 _putenv(get_TZ_context().getValue().c_str());
235 //ptrTm = localtime_r(&a_unix_timestamp, &resTm); NO FUNCIONA BIEN EN LINUX!!
236 ptrTm = localtime(&a_unix_timestamp);
238 // ctime() -> localtime() -> __tz_convert() -> __libc_lock_lock()
239 // So, glibc does not guarantee the sane behavior when one uses ctime() in
242 // Este problema puede ser de algún bug de la librería libc.so.6 de linux.
243 // Recomiendan poner LD_ASSUME_KERNEL=2.4.1
244 // Otra solucion sera hacerlo MT-Safe con guardas
245 a_tm_struct = *ptrTm;
247 _putenv(a_local_tz.getValue().c_str());
248 static char cad_aux[32];
249 sprintf(cad_aux, STRING_FORMAT_yyyymmddHHmmss, 1900 + a_tm_struct.tm_year, 1 + (a_tm_struct.tm_mon), a_tm_struct.tm_mday,
250 a_tm_struct.tm_hour, a_tm_struct.tm_min, a_tm_struct.tm_sec);
252 _yyyymmddHHmmss = cad_aux;
260 //------------------------------------------------------------------------------
261 //----------------------------------------------------------- Date::initialize()
262 //------------------------------------------------------------------------------
263 void Date::initialize(const char * TzContext) throw() {
264 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "initialize", ANNA_FILE_LOCATION));
265 _initialize(TzContext);
269 //------------------------------------------------------------------------------
270 //--------------------------------------------------------- Date::setTzContext()
271 //------------------------------------------------------------------------------
272 void Date::setTzContext(const char * TzContext) throw() {
273 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "setTzContext", ANNA_FILE_LOCATION));
274 set_tz_context(TzContext);
279 //------------------------------------------------------------------------------
280 //---------------------------------------------------------------- Date::store()
281 //------------------------------------------------------------------------------
282 void Date::store(const char* dateTimeAsStringFormat, const std::string & dateTimeAsString, const char * OriginTz)
284 throw(anna::RuntimeException)
287 // Get equivalent 'tm':
289 memset(&TmOrigin, 0, sizeof(TmOrigin));
291 if(strptime(dateTimeAsString.c_str(), dateTimeAsStringFormat, &TmOrigin) == NULL) {
292 std::string msg("Error during strptime() conversion: '");
293 msg += dateTimeAsString;
294 msg += "' can't be interpreted as '";
295 msg += dateTimeAsStringFormat;
297 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
300 store(TmOrigin, OriginTz);
304 void Date::store(const std::string & yyyymmddHHmmss, const char * OriginTz)
306 throw(anna::RuntimeException)
309 check_yyyymmddHHmmss(yyyymmddHHmmss); // launch exception when format error (14 digits)
310 // Could be good, but not assing (perhaps 'strptime' could fail)
311 // Limitaciones del 'strptime' usado por 'store(format, cadena)':
312 // El formato directamente sacado de este interfaz ("%Y%m%d%H%M%S") no valida las fechas (meses 13, dias 32, etc),
313 // para ello hay que poner separadores en el formato (mucho más robusto). Aun asi, 'strptime' sigue "tragandose cosas"
314 // como por ejemplo segundos=60 (07:54:60 lo convierte a 07:55:00), y no comprueba los años bisiestos (de febrero traga
315 // 29 dias siempre, aunque no traga 30)
316 // Solucion, convertimos la cadena a otra con separadores para dar mayor robustez (aunque no es perfecto como se ha dicho):
317 int tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec;
318 sscanf(yyyymmddHHmmss.c_str(), STRING_FORMAT_yyyymmddHHmmss, &tm_year, &tm_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec);
319 static char cad_aux[64];
320 sprintf(cad_aux, "%04d-%02d-%02d-%02d-%02d-%02d", tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec);
321 store(STRPTIME_FORMAT_yyyy_mm_dd_HH_mm_ss, cad_aux, OriginTz);
325 //------------------------------------------------------------------------------
326 //---------------------------------------------------------------- Date::store()
327 //------------------------------------------------------------------------------
328 void Date::store(const struct tm & TmOrigen, const char * OriginTz)
330 throw(anna::RuntimeException)
333 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "store", ANNA_FILE_LOCATION));
335 // Set '-1' on 'tm_isdst', or it could be an ambiguity on tranformation
336 // returning inexact timestamp. This setting force system to check the correct flag:
337 struct tm TmOrigen_isdst_menos1 = TmOrigen;
338 TmOrigen_isdst_menos1.tm_isdst = -1;
339 time_t unixTimestamp = mktime((struct tm*) & TmOrigen_isdst_menos1);
341 if(unixTimestamp == (time_t) - 1)
342 throw anna::RuntimeException("Error during mktime() conversion !!", ANNA_FILE_LOCATION);
344 a_unix_timestamp = unixTimestamp;
345 // Refresh the othr data:
346 refresh_regarding_unix_timestamp();
350 //------------------------------------------------------------------------------
351 //---------------------------------------------------------------- Date::store()
352 //------------------------------------------------------------------------------
353 void Date::store(const time_t & unixTimestamp) throw() {
354 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "store unix timestamp", ANNA_FILE_LOCATION));
355 a_unix_timestamp = unixTimestamp;
356 // Refresh the other data:
357 refresh_regarding_unix_timestamp();
361 //------------------------------------------------------------------------------
362 //------------------------------------------------------------- Date::storeNtp()
363 //------------------------------------------------------------------------------
364 void Date::storeNtp(const unsigned int & ntpTimestamp) throw() {
365 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "store ntp timestamp", ANNA_FILE_LOCATION));
366 a_unix_timestamp = ntpTimestamp - TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970;
367 // Refresh the other data:
368 refresh_regarding_unix_timestamp();
372 //------------------------------------------------------------------------------
373 //-------------------------------------------------------------- Date::operator=
374 //------------------------------------------------------------------------------
375 Date & Date::operator = (const Date & d) {
376 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "operator=", ANNA_FILE_LOCATION));
377 // avoid itself copy: i.e., Date a,b; a=&b; a=b; b=a;
378 if(this == &d) return (*this);
380 a_TZ_context = d.get_TZ_context();
381 a_unix_timestamp = d.getUnixTimestamp();
382 a_tm_struct = d.getTm();
383 _yyyymmddHHmmss = d.yyyymmddHHmmss();
390 //------------------------------------------------------------------------------
391 //--------------------------------------------------------- Date::getTzContext()
392 //------------------------------------------------------------------------------
393 const std::string & Date::getTzContext(void) const throw() {
394 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "getTzContext", ANNA_FILE_LOCATION));
395 return (a_TZ_context.getValue());
399 //------------------------------------------------------------------------------
400 //------------------------------------------------------- Date::yyyymmddHHmmss()
401 //------------------------------------------------------------------------------
402 const std::string & Date::yyyymmddHHmmss(void) const throw() {
403 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "yyyymmddHHmmss", ANNA_FILE_LOCATION));
404 return (_yyyymmddHHmmss);
408 //------------------------------------------------------------------------------
409 //----------------------------------------------------- Date::getUnixTimestamp()
410 //------------------------------------------------------------------------------
411 const time_t & Date::getUnixTimestamp(void) const throw() {
412 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "getUnixTimestamp", ANNA_FILE_LOCATION));
413 return (a_unix_timestamp);
417 //------------------------------------------------------------------------------
418 //------------------------------------------------------ Date::getNtpTimestamp()
419 //------------------------------------------------------------------------------
420 unsigned int Date::getNtpTimestamp(void) const throw() {
421 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "getNtpTimestamp", ANNA_FILE_LOCATION));
422 unsigned int ntp_timestamp = a_unix_timestamp + TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970;
423 return (ntp_timestamp);
427 //------------------------------------------------------------------------------
428 //---------------------------------------------------------------- Date::getTm()
429 //------------------------------------------------------------------------------
430 const struct tm & Date::getTm(void) const throw() {
431 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "getTm", ANNA_FILE_LOCATION));
434 return (a_tm_struct);
438 //------------------------------------------------------------------------------
439 //------------------------------------------------------------- Date::asString()
440 //------------------------------------------------------------------------------
441 std::string Date::asString(void) const throw() {
442 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "asString", ANNA_FILE_LOCATION));
444 static char cad_aux[256];
445 //bool differentContextAndLocalTZ = getTzContext() != a_local_tz.getValue();
446 sprintf(cad_aux, "%02d/%02d/%04d %02d:%02d:%02d",
447 // yyyymmddHHmmss().c_str(),
448 getTm().tm_mday, 1 + (getTm().tm_mon), 1900 + getTm().tm_year,
449 getTm().tm_hour, getTm().tm_min, getTm().tm_sec);
452 // if (!(get_TZ_context().isUnset()))
453 // if (!(get_TZ_context().isEmpty()))
454 // { trace += " "; trace += get_TZ_context().getValue(); }
455 if(getTzContext() != ""/* && differentContextAndLocalTZ*/) { trace += " "; trace += getTzContext(); }
457 sprintf(cad_aux, ", isdst = %d [Unix Timestamp: %ld, Ntp Timestamp: %u]", getTm().tm_isdst, getUnixTimestamp(), getNtpTimestamp());
460 // if (!(a_local_tz.isUnset()))
461 // if (!(a_local_tz.isEmpty()))
462 // { trace += ", Local TZ = "; trace += a_local_tz.getValue(); }
463 if(a_local_tz.getValue() != "") {
464 //trace += differentContextAndLocalTZ ? ", Local TZ = ":", TZ Context = Local TZ = ";
465 trace += ", Local TZ = ";
466 trace += a_local_tz.getValue();
473 //------------------------------------------------------------------------------
474 //---------------------------------------------------------------- Date::asXML()
475 //------------------------------------------------------------------------------
476 anna::xml::Node* Date::asXML(anna::xml::Node* parent) const throw() {
477 //LOGMETHOD (TraceMethod tm ("anna::time::Date", "asXML", ANNA_FILE_LOCATION));
478 //anna::xml::Node* result = parent->createChild("anna.time.Date");
479 //bool differentContextAndLocalTZ = getTzContext() != a_local_tz.getValue();
480 parent->createAttribute("Date", anna::functions::asString("%02d/%02d/%04d", getTm().tm_mday, 1 + (getTm().tm_mon), 1900 + getTm().tm_year));
481 parent->createAttribute("Time", anna::functions::asString("%02d:%02d:%02d", getTm().tm_hour, getTm().tm_min, getTm().tm_sec));
483 if(getTzContext() != ""/* && differentContextAndLocalTZ*/) parent->createAttribute("TZContext", getTzContext());
485 parent->createAttribute("Isdst", (getTm().tm_isdst) ? "yes" : "no");
486 // parent->createAttribute("UnixTimestamp", anna::functions::asString("%lu seconds", getUnixTimestamp())); // unsigned long -> %lu
487 // parent->createAttribute("NtpTimestamp", anna::functions::asString("%u seconds", getNtpTimestamp())); // unsigned int -> %u
488 parent->createAttribute("UnixTimestamp", anna::functions::asString((const int)getUnixTimestamp()));
489 parent->createAttribute("NtpTimestamp", anna::functions::asString(getNtpTimestamp()));
491 if(a_local_tz.getValue() != "") parent->createAttribute(/*differentContextAndLocalTZ ? "LocalTZ":"TZContext=LocalTZ"*/"LocalTZ", a_local_tz.getValue());