From: Eduardo Ramos Testillano Date: Sun, 3 Aug 2014 00:30:56 +0000 (+0200) Subject: libanna.time refactoring X-Git-Tag: REFACTORING_TESTING_LIBRARY~242 X-Git-Url: https://git.teslayout.com/public/public/public/?p=anna.git;a=commitdiff_plain;h=b8c96c2917b9d29976a8771659d70bfb0881c133 libanna.time refactoring --- diff --git a/example/diameter/launcher/main.cpp b/example/diameter/launcher/main.cpp index 249a595..af3ceb0 100644 --- a/example/diameter/launcher/main.cpp +++ b/example/diameter/launcher/main.cpp @@ -1173,7 +1173,7 @@ void Launcher::writeLogFile(const anna::diameter::codec::Message & decodedMessag if (a_detailedLog) { anna::time::Date now; - now.setCurrent(); + now.setNow(); title += " "; title += now.asString(); log += anna::functions::highlight(title, anna::functions::TextHighlightMode::OverAndUnderline); @@ -1289,7 +1289,7 @@ throw(anna::RuntimeException) { LOGMETHOD(anna::TraceMethod tm("Launcher", "run", ANNA_FILE_LOCATION)); CommandLine& cl(anna::CommandLine::instantiate()); // Start time: - a_start_time.setCurrent(); + a_start_time.setNow(); // Statistics: anna::statistics::Engine::instantiate().enable(); /////////////////////////////// diff --git a/example/time/conversor/main.cpp b/example/time/conversor/main.cpp index 371d907..5548764 100644 --- a/example/time/conversor/main.cpp +++ b/example/time/conversor/main.cpp @@ -63,7 +63,7 @@ int main(int argc, char** argv) { if (!argv[1]) { std::string msg = "Use: "; msg += argv[0]; - msg += " [-unix ] [-ntp ] [-yyyymmddHHmmss ]"; + msg += " [-unix ] [-ntp ] [-yyyymmddHHmmss ]"; F_exit(msg.c_str()); } diff --git a/include/anna/time/Date.hpp b/include/anna/time/Date.hpp index 8d699ff..f0a46be 100644 --- a/include/anna/time/Date.hpp +++ b/include/anna/time/Date.hpp @@ -44,7 +44,7 @@ #include // Local -#include +#include namespace anna { @@ -56,27 +56,14 @@ class Node; //------------------------------------------------------------------------------ //---------------------------------------------------------------------- #define //------------------------------------------------------------------------------ -#define _2K38_EFFECT_LIMIT "20380119031407" // UTC - -// Era Unix: 01/01/1970 00:00:00 UTC -// Fecha NTP: 01/01/1900 00:00:00 UTC -// -> Segundos de 70 años ( de 1900 a 1970) : 2207520000 -// -> Entre 1900 y 1970 hay 17años bisiestos, por lo que hay que sumar 17 días: 1468800 Segundos -// TOTAL = 2207520000 + 1468800 : -// Significa que debemos restar el anterior valor para obtener un valor unix, y sumarlo para tener un timestamp NTP: -#define TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970 2208988800U - -// The NTP timestamp format represents seconds and fraction as a 64-bit unsigned fixed-point -// integer with decimal point to the left of bit 32 numbered from the left. The 32-bit seconds -// field spans about 136 years, while the 32-bit fraction field precision is about 232 picoseconds. - -// Para un timestamp NTP me bastan 32 bits, es decir 'unsigned int'. El tipo time_t es realmente un int. Cojo -// el mayor entre ambos: unsigned int. - - -#define STRING_FORMAT_yyyymmddHHmmss "%04d%02d%02d%02d%02d%02d" -#define STRPTIME_FORMAT_yyyy_mm_dd_HH_mm_ss "%Y-%m-%d-%H-%M-%S" +// Unix time reference: 01/01/1970 00:00:00 UTC +// NTP time reference: 01/01/1900 00:00:00 UTC +// -> 70 years have 2207520000 seconds. +// -> Between 1900 and 1970 there are 17 leap years: 1468800 seconds more (17 days) +// TOTAL DIFFERENCE = 2207520000 + 1468800 = 2208988800 +#define TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970 2208988800U +//#define _2K38_EFFECT_LIMIT "20380119031407" // UTC namespace anna { @@ -87,72 +74,64 @@ namespace time { /** * Absolute time representation structs class manager. * Allow any time assignment/extraction regarding any timezone. +* Uses shell TZ assignment or rely on system timezone configuration (in ubuntu, /etc/timezone +* stores the abbreviation and /etc/localtime keeps the timezone file from /usr/share/zoneinfo). * -* Certain methods could launch runtime exception when trying to get TZ from shell. +* The internal unix timestamp reamain constant and thedate representation depends on the +* configured timezone. Some store methods could specify a certain origin timezone, but the +* instance inner timezone won't be changed until the user want to do that. * *
 *
-* I.e., supose you have TZ="MET" on host (on Madrid):
+*  Sample code:
 *
-*   resources::time::Date edu_birth("CET");
-*   //resources::time::Date edu_birth; // if Context TZ = shell TZ = "CET"
-*   edu_birth.store ("19741219101500");
-*   std::cout << "'edu_birth.asString()' is: " << edu_birth.asString() << std::endl;
-*   std::cout << "'Timestamp' edu_birth (Madrid:CET): " << edu_birth.getUnixTimestamp() << std::endl;
-*   struct tm Tm = edu_birth.getTm();
-*   std::cout << "'Tm' edu_birth (madrid:CET): " << "tm_mday = " << Tm.tm_mday << ", tm_mon (0-11) = " << Tm.tm_mon
-*   << ", tm_year (since 1900) = " << Tm.tm_year << ", tm_hour = " << Tm.tm_hour
-*   << ", tm_min = " << Tm.tm_min << ", tm_sec = " << Tm.tm_sec << std::endl;
-*   resources::time::Date same_moment_canary_island("GMT");
-*   same_moment_canary_island.store (edu_birth.getUnixTimestamp());
-*   std::cout << "'yyyymmddHHmmss' for 'same_moment_canary_island': " << (same_moment_canary_island.yyyymmddHHmmss()).c_str() << std::endl;
-*   edu_birth.setTzContext ("GMT");
-*   std::cout << "'yyyymmddHHmmss' for edu_birth, Local time on Canary Island: " << (edu_birth.yyyymmddHHmmss()).c_str() << std::endl;
-*   resources::time::Date birthday("EET");
-*   birthday.store (same_moment_canary_island.getTm(), "GMT"); // TZ origin = "GMT"
-*   if (birthday == edu_birth)
-*   {
-*   std::cout << "'birthday' same as 'edu_birth', and 'yyyymmddHHmmss' is: " << (birthday.yyyymmddHHmmss()).c_str()
-*   << " in TZ = " << (birthday.getTzContext()).getValue().c_str() << std::endl;
-*   }
+*  anna::time::functions::initialize();
+*  std::cout << "SystemTimezone: " << anna::time::functions::getSystemTimezone().asString() << std::endl;
 *
-*   std::cout << "'birthday': " << birthday.asString() << std::endl;
-*   std::cout << "'edu_birth': " << edu_birth.asString() << std::endl;
+*  anna::time::Date myBirth ("CET");
+*  myBirth.store("19741219111500", "EET");
+*  std::cout << "myBirth: " << myBirth.asString() << std::endl;
 *
-*   edu_birth.setTzContext (resources::time::functions::getLocalTz().getValue().c_str());
-*   std::cout << "'edu_birth' on local TZ: " << edu_birth.asString() << std::endl;
+*  anna::time::Date same_moment_canary_island("GMT");
+*  same_moment_canary_island.store(myBirth.getUnixTimestamp());
+*  std::cout << "same_moment_canary_island: " << same_moment_canary_island.asString() << std::endl;
+*  myBirth.setTz("GMT");
+*  std::cout << "myBirth on GMT: " << myBirth.asString() << std::endl;
+*  std::cout << "EQUAL: same_moment_canary_island = " << same_moment_canary_island.yyyymmddHHmmss() << "; myBirth in context GMT = " << myBirth.yyyymmddHHmmss() << std::endl;
+*  same_moment_canary_island.setTz("CET");
+*  std::cout << "same_moment_canary_island on CET: " << same_moment_canary_island.asString() << std::endl;
+*  anna::time::Date birthday("EET");
+*  birthday.store(same_moment_canary_island.getTm(), "CET");
+*  std::cout << "EQUAL: birthday TS = " << birthday.getUnixTimestamp() << "; myBirth TS = " << myBirth.getUnixTimestamp() << std::endl;
+*  std::cout << "birthday EET = " << birthday.asString() << std::endl;
 *
-*   //birthday.setTzContext("CET");
-*   birthday.setTzContext (resources::time::functions::getLocalTz().getValue().c_str()); // Go from "EET" to "CET"
-*   std::cout << "'yyyymmddHHmmss' for 'birthday' on Local TZ: " << (birthday.yyyymmddHHmmss()).c_str() << std::endl;
-*   // Adding 18 years to 'edu_birth':
-*   Tm.tm_year += 18;
-*   resources::time::Date eighteen("CET");
-*   eighteen.store (Tm);
-*   if (eighteen >= same_moment_canary_island) // same as compare to 'edu_birth'
-*   {
-*   std::cout << "'eighteen' is 'yyyymmddHHmmss' = " << (eighteen.yyyymmddHHmmss()).c_str() << std::endl;
-*   std::cout << "'eighteen' is later than timestamp for 'same_moment_canary_island' = " << same_moment_canary_island.getUnixTimestamp() << std::endl;
-*   std::cout << "'eighteen' is later than timestamp for 'edu_birth' = " << edu_birth.getUnixTimestamp() << std::endl;
-*   }
+*  std::cout << "Setting Local timezone ..." << std::endl;
+*  myBirth.setSystemTimezone();
+*  birthday.setSystemTimezone();
+*  std::cout << "EQUAL: birthday " << birthday.asString() << "; myBirth " << myBirth.asString() << std::endl;
+*  std::cout << "EQUAL: birthday (Local Timezone)= " << birthday.yyyymmddHHmmss() << "; myBirth (Local Timezone)= " << myBirth.yyyymmddHHmmss() << std::endl;
+*  std::cout << "myBirth TZ  = " << myBirth.getTzAsString() << std::endl;
+*  std::cout << "birthday TZ  = " << myBirth.getTzAsString() << std::endl;
 *
+*  myBirth.setTz("GMT");
+*  std::cout << "myBirth in GMT = " << myBirth.yyyymmddHHmmss() << std::endl;
 *
-* -----------------------------------------------------------------------------------------------------------------------------
-* Program output:
+*  Program output:
 *
-*   'edu_birth.asString()' is: 19/12/1974 10:15:00 CET, isdst = 0 [Unix Timestamp: 156676500], Local TZ = EET
-*   'Timestamp' edu_birth (Madrid:CET): 156676500
-*   'Tm' edu_birth (madrid:CET): tm_mday = 19, tm_mon (0-11) = 11, tm_year (since 1900) = 74, tm_hour = 10, tm_min = 15, tm_sec = 0
-*   'yyyymmddHHmmss' for 'same_moment_canary_island': 19741219091500
-*   'yyyymmddHHmmss' for edu_birth, Local time on Canary Island: 19741219091500
-*   'birthday' same as 'edu_birth', and 'yyyymmddHHmmss' is: 19741219111500 in TZ = EET
-*   'birthday': 19/12/1974 11:15:00 EET, isdst = 0 [Unix Timestamp: 156676500], Local TZ = EET
-*   'edu_birth': 19/12/1974 09:15:00 GMT, isdst = 0 [Unix Timestamp: 156676500], Local TZ = EET
-*   'edu_birth' on local TZ: 19/12/1974 10:15:00 EET, isdst = 0 [Unix Timestamp: 156676500], Local TZ = EET
-*   'yyyymmddHHmmss' for 'birthday' on Local TZ: 19741219101500
-*   'eighteen' is 'yyyymmddHHmmss' = 19921219101500
-*   'eighteen' is later than timestamp for 'same_moment_canary_island' = 156676500
-*   'eighteen' is later than timestamp for 'edu_birth' = 156676500
+*  SystemTimezone: 
+*  myBirth: Thursday 19/12/1974 10:15:00 CET, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: 
+*  same_moment_canary_island: Thursday 19/12/1974 09:15:00 GMT, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: 
+*  myBirth on GMT: Thursday 19/12/1974 09:15:00 GMT, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: 
+*  EQUAL: same_moment_canary_island = 19741219091500; myBirth in context GMT = 19741219091500
+*  same_moment_canary_island on CET: Thursday 19/12/1974 10:15:00 CET, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: 
+*  EQUAL: birthday TS = 156676500; myBirth TS = 156676500
+*  birthday EET = Thursday 19/12/1974 11:15:00 EET, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: 
+*  Setting Local timezone ...
+*  EQUAL: birthday Thursday 19/12/1974 10:15:00 , isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: ; myBirth Thursday 19/12/1974 10:15:00 , isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: 
+*  EQUAL: birthday (Local Timezone)= 19741219101500; myBirth (Local Timezone)= 19741219101500
+*  myBirth TZ  = 
+*  birthday TZ  = 
+*  myBirth in GMT = 19741219091500
 *
 * 
*/ @@ -163,134 +142,113 @@ class Date { ///////////////////////////////////////////////// // Main data: - TZ a_TZ_context; // timezone for current representation - time_t a_unix_timestamp; // unix timestamp + Timezone a_tz; // timezone for this instance context + time_t a_timestamp; // unix timestamp // Secondary data: - struct tm a_tm_struct; - std::string _yyyymmddHHmmss; // string representation for 'tm' + struct tm a_tm; - // Auxiliar: - TZ a_local_tz; // local tz on host - TZ a_work_tz; // auxiliar tz - void _putenv(const char * Tz) throw(); - void set_tz_context(const char * TzContext) throw(); - const TZ & get_TZ_context(void) const throw(); - void _initialize(const char * TzContext) throw(); + // sets the current date/time (time(NULL)) and provided TimeZone as described in constructor + void initialize(const char *TZ = NULL) throw(); - void check_yyyymmddHHmmss(const std::string & yyyymmddHHmmss) throw(anna::RuntimeException); - void refresh_regarding_unix_timestamp(void) throw(); // main refresh method + // main refresh method regarding timestamp + void refresh(void) throw(); anna::Mutex a_mutex; + public: /** * Default Constructor + * @param TZ timezone for date/time representation. Default (NULL) assumes the system + * timezone configuration. Empty string will be interpreted as UTC timezone. */ - Date(const char * TzContext = NULL); + Date(const char *TZ = NULL); /** * Copy Constructor */ - Date(const Date & d); + Date(const Date & d) { *this = d; } /** * Destructor */ - ~Date(); - + ~Date() {;} - // sets + // setters /** - * Class initialization with current date/time (time(NULL)) and provided TimeZone (default is Local TimeZone) - * - * @param TzContext timezone for date/time representation. Default is Local timezone - * - * @see setTzContext() - * @see store() + * Sets the current date/time (time(NULL)) and provided TimeZone as described in constructor */ - void initialize(const char * TzContext = NULL) throw(); + void setNow(const char *TZ = NULL) throw() { initialize(TZ); } /** - * Same as initialize() + * Sets the provided timezone for date/time representation of the class instance. + * This method keeps invariant the unix timestamp, thus, the date representation + * will evolve depending on the configured timezone. + * @warning Be careful with incorrect TZ values because no error will be shown but + * dates could be misleading. The valid TZ could be seen with tzselect tool or + * directly looking at /usr/share/zoneinfo hierarchy. + * + * @param TZ timezone for date/time representation. + * Default (NULL) is the local host (user-supplied) timezone + * Empty string sets UTC0 (GMT, Greenwich) + * Use available timezones in /usr/share/zoneinfo; i.e.: Europe/Madrid, CET, etc. + * You can also use 'tzselect' helper, but that tool don't change the system + * timezone, only helps to know the valid TZ values and shows how to change + * the timezone settings if you would want to do that). */ - void setCurrent(const char * TzContext = NULL) throw() { initialize(TzContext); } + void setTz(const char *TZ = NULL) throw(); /** - * Sets the provided timezone for date/time representation. default is Local timezone. - * - * @param TzContext timezone for date/time representation. Default is Local timezone - * - * @see initialize() - * @see store() + Sets the local host timezone (even if it is based on TZ or in /usr/share/zoneinfo) */ - void setTzContext(const char * TzContext = NULL) throw(); + void setSystemTimezone() throw(); /** - * Sets date/time from string 'yyyymmddHHmmss' representation (HH is 24 hour format, and string must be 14 digits-length), - * and optional timezone (default is local) - * - * @param yyyymmddHHmmss 'yyyymmddHHmmss' date/time - * @param OriginTz timezone for date/time provided. Default is Local TZ - * - * @see initialize() - * @see setTzContext() - */ - void store(const std::string & yyyymmddHHmmss, const char * OriginTz = NULL) throw(anna::RuntimeException); - - - /** - * Sets date/time from string representation with certain provided format, and optional timezone (default is local) - * - * @param dateTimeAsStringFormat Date/time string format - * @param dateTimeAsString Date/time string - * @param OriginTz timezone for date/time provided. Default is Local TZ + * Sets date/time providing unix timestamp (seconds since 01-Jan-1970 GMT) * - * @see initialize() - * @see setTzContext() + * @param unixTimestamp Time since 01-Jan-1970 GMT */ - void store(const char* dateTimeAsStringFormat, const std::string & dateTimeAsString, const char * OriginTz = NULL) throw(anna::RuntimeException); + void store(const time_t & unixTimestamp) throw(); + void storeUnix(const time_t & unixTimestamp) throw() { store(unixTimestamp); } /** - * Sets date/time from standard (time.h) 'tm' struct and optional timezone (default is local) - * - * @param TmOrigen 'tm' struct date/time - * @param OriginTz timezone for date/time provided. Default is Local TZ + * Sets date/time providing ntp timestamp (seconds since 01-Jan-1900 GMT) * - * @see initialize() - * @see setTzContext() + * @param ntpTimestamp Time since 01-Jan-1900 GMT */ - void store(const struct tm & TmOrigen, const char * OriginTz = NULL) throw(anna::RuntimeException); + void storeNtp(const unsigned int &ntpTimestamp) throw(); /** - * Sets date/time providing unix timestamp (seconds since 01-Jan-1970 GMT) - * - * @param unixTimestamp Time since 01-Jan-1970 GMT + * Sets date/time from standard 'tm' struct and optional timezone * - * @see initialize() - * @see setTzContext() + * @param date 'tm' struct date/time + * @param TZ timezone for date/time provided. Default (NULL) assumes the system + * timezone configuration. Empty string will be interpreted as UTC timezone. */ - void store(const time_t & unixTimestamp) throw(); - void storeUnix(const time_t & unixTimestamp) throw() { store(unixTimestamp); } + void store(const struct tm &date, const char *TZ = NULL) throw(anna::RuntimeException); /** - * Sets date/time providing ntp timestamp (seconds since 01-Jan-1900 GMT) - * - * @param ntpTimestamp Time since 01-Jan-1900 GMT - * - * @see initialize() - * @see setTzContext() + * Sets date/time from string representation with certain provided format, + * and optional timezone + * + * @param stringDate Date/time string + * @param TZ timezone for date/time provided. Default (NULL) assumes the system + * timezone configuration. Empty string will be interpreted as UTC timezone. + * @param strptimeFormat Date/time string format for strptime primitive. + * See format syntax at http://man7.org/linux/man-pages/man3/strptime.3.html */ - void storeNtp(const unsigned int & ntpTimestamp) throw(); + void store(const std::string &stringDate, const char *TZ = NULL, const char *strptimeFormat = "%Y%m%d%H%M%S") throw(anna::RuntimeException); + //void store(const std::string &stringDate, const char *tz = NULL, const char *strptimeFormat = "%Y-%m-%d-%H-%M-%S") throw(anna::RuntimeException); /** @@ -298,7 +256,7 @@ public: * * @param d Source class instance * - * @return Retunrs copied reference + * @return Returns copied reference */ Date & operator = (const Date &d); @@ -356,76 +314,68 @@ public: /** - * Gets context representation TZ + * Gets context instance timezone information * - * @return context representation TZ - * - * @see yyyymmddHHmmss() - * @see getUnixTimestamp() - * @see getTm() - * @see asString() + * @return NULL if TZ is unset, or string for TZ environment variable + * (empty is allowed and use to be understood as UTC. */ - const std::string & getTzContext(void) const throw(); + const char *getTz(void) const throw() { return (a_tz.unsetTZ() ? NULL:a_tz.getValue().c_str()); } /** - * Gets 'yyyymmddHHmmss' stored data - * - * @return Time/date on format 'yyyymmddHHmmss' - * - * @see getTzContext() - * @see getUnixTimestamp() - * @see getTm() - * @see asString() + * Gets the context timezone as string */ - const std::string & yyyymmddHHmmss(void) const throw(); + std::string getTzAsString() const throw() { return a_tz.asString(); } /** - * Gets unix timestamp (01-Jan-1970 GMT) + * Gets 'tm' struct on the current timezone * - * @return Seconds since 01-Jan-1970 GMT + * @return 'tm' struct on the current timezone + */ + const struct tm & getTm(void) const throw() { return a_tm; } + + /** + * Gets the week day abbreviation: Sun, Mon, Tue, Wed, Thu, Fri, Sat * - * @see getTzContext() - * @see yyyymmddHHmmss() - * @see getTm() - * @see asString() + * @return Day of the week */ - const time_t & getUnixTimestamp(void) const throw(); + const char *getDay(void) const throw(); /** - * Gets ntp timestamp (01-Jan-1900 GMT) + * Gets 'yyyymmddHHmmss' representation for the current timezone * - * @return Seconds since 01-Jan-1900 GMT + * @return Time/date on format 'yyyymmddHHmmss' * - * @see getTzContext() - * @see yyyymmddHHmmss() - * @see getTm() - * @see asString() + * @see getTz */ - unsigned int getNtpTimestamp(void) const throw(); + std::string yyyymmddHHmmss(void) const throw(); /** - * Gets 'tm' struct on context representation TZ + * Gets unix timestamp (01-Jan-1970 GMT) * - * @return 'tm' struct on context representation TZ + * @return Seconds since 01-Jan-1970 GMT + */ + const time_t & getUnixTimestamp(void) const throw() { return a_timestamp; } + + + /** + * Gets ntp timestamp (01-Jan-1900 GMT) * - * @see getTzContext() - * @see yyyymmddHHmmss() - * @see getUnixTimestamp() - * @see asString() + * @return Seconds since 01-Jan-1900 GMT */ - const struct tm & getTm(void) const throw(); + unsigned int getNtpTimestamp(void) const throw() { + unsigned int ntp_timestamp = a_timestamp + TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970; + return ntp_timestamp; + } /** * Class string representation * * @return String with class content - * - * @see asXML() */ std::string asString(void) const throw(); @@ -434,8 +384,6 @@ public: * Class XML representation * * @return XML with class content - * - * @see asString() */ anna::xml::Node* asXML(anna::xml::Node* parent) const throw(); diff --git a/include/anna/time/functions.hpp b/include/anna/time/functions.hpp index 67ab412..47a89bd 100644 --- a/include/anna/time/functions.hpp +++ b/include/anna/time/functions.hpp @@ -44,7 +44,7 @@ namespace anna { namespace time { -class TZ; +class Timezone; class functions { public: @@ -79,9 +79,9 @@ public: static bool initialized(void) throw(); /** - Gets the local TZ + Gets the host user-supplied timezone configuration */ - static const TZ & getLocalTz(void) throw(); + static const Timezone & getSystemTimezone(void) throw(); /** diff --git a/include/anna/time/internal/TZ.hpp b/include/anna/time/internal/TZ.hpp deleted file mode 100644 index 84638fe..0000000 --- a/include/anna/time/internal/TZ.hpp +++ /dev/null @@ -1,134 +0,0 @@ -// ANNA - Anna is Not Nothingness Anymore -// -// (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo -// -// https://bitbucket.org/testillano/anna -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// Authors: eduardo.ramos.testillano@gmail.com -// cisco.tierra@gmail.com - - -#ifndef anna_time_internal_TZ_hpp -#define anna_time_internal_TZ_hpp - - -// Standard -#include - - -//------------------------------------------------------------------------------ -//---------------------------------------------------------------------- #define -//------------------------------------------------------------------------------ - - -namespace anna { - -namespace time { - -class TZ { - - bool a_initialized; - std::string a_value; - bool a_isUnset; - -public: - - TZ() { a_initialized = false; }; - ~TZ() {}; - - - // sets - - void set(const char *input) { - a_initialized = true; - a_value = input ? input : ""; - a_isUnset = !input; - } - - TZ & operator = (const TZ &tz) { - if(this == &tz) return (*this); - - a_initialized = tz.isInitialized(); - a_value = tz.getValue(); - a_isUnset = tz.isUnset(); - return (*this); - } - - // gets - - // Operators - - friend bool operator == (const TZ & tz1, const TZ & tz2) { - if(tz1.isInitialized() != tz2.isInitialized()) - return (false); - - if(tz1.isUnset()) - return (tz2.isUnset()); - - return (tz1.getValue() == tz2.getValue()); - } - - friend bool operator != (const TZ & tz1, const TZ & tz2) { - return (!(tz1 == tz2)); - } - - const std::string & getValue(void) const throw() { - return (a_value); - } - - - bool isInitialized(void) const throw() { - return (a_initialized); - } - - - bool isUnset(void) const throw() { return (a_isUnset); } - bool isEmpty(void) const throw() { return (getValue() == ""); } - - std::string getShellAssignment(void) const throw() { - std::string assignment; - - if(isUnset()) - assignment = "unset TZ"; - else { - assignment = "TZ="; - assignment += getValue(); - } - - return (assignment); - } - -}; - - -} -} - -#endif - diff --git a/include/anna/time/internal/Timezone.hpp b/include/anna/time/internal/Timezone.hpp new file mode 100644 index 0000000..ed12614 --- /dev/null +++ b/include/anna/time/internal/Timezone.hpp @@ -0,0 +1,164 @@ +// ANNA - Anna is Not Nothingness Anymore +// +// (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo +// +// https://bitbucket.org/testillano/anna +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: eduardo.ramos.testillano@gmail.com +// cisco.tierra@gmail.com + + +#ifndef anna_time_internal_Timezone_hpp +#define anna_time_internal_Timezone_hpp + + +// Standard +#include +#include +#include + +extern char *tzname[2]; + +//------------------------------------------------------------------------------ +//---------------------------------------------------------------------- #define +//------------------------------------------------------------------------------ + + +namespace anna { + +namespace time { + +class Timezone { + + bool a_initialized; + std::string a_value; + bool a_unsetTZ; + +public: + + Timezone() { a_initialized = false; }; + ~Timezone() {}; + + + // sets + // pass NULL to unset + void set(const char *input) { + a_initialized = true; + a_value = input ? input : ""; + + // /* + // In many C libraries, TZ='' is equivalent to TZ='GMT0' or TZ='UTC0', + // and that's the behavior you're observing. An unset TZ is equivalent + // to a system-supplied default, typically the local time where the host + // is physically located. + // */ + // if (input && (a_value == "")) a_value="UTC0"; + + a_unsetTZ = !input; + } + + Timezone & operator = (const Timezone &tz) { + if(this == &tz) return (*this); + + a_initialized = tz.isInitialized(); + a_value = tz.getValue(); + a_unsetTZ = tz.unsetTZ(); + return (*this); + } + + // gets + + // Operators + + friend bool operator == (const Timezone & tz1, const Timezone & tz2) { + if(tz1.isInitialized() != tz2.isInitialized()) + return (false); + + if(tz1.unsetTZ()) + return (tz2.unsetTZ()); + + return (tz1.getValue() == tz2.getValue()); + } + + friend bool operator != (const Timezone & tz1, const Timezone & tz2) { + return (!(tz1 == tz2)); + } + + // be careful: empty if unset + const std::string & getValue(void) const throw() { + return (a_value); + } + + + bool isInitialized(void) const throw() { + return (a_initialized); + } + + //Unset is when getenv("TZ") returns NULL. Then, /etc/localtime used to be a copy or symlink to + // any of the /usr/share/zoneinfo available timezones. + bool unsetTZ(void) const throw() { return (a_unsetTZ); } + + bool apply() const throw() { + // http://stackoverflow.com/questions/5873029/questions-about-putenv-and-setenv + if(unsetTZ()) { + unsetenv("TZ"); + } + else { + setenv("TZ", a_value.c_str(), 1 /* overwrite */); + + /* + // If TZ has an invalid setting, tzset() uses GMT0: + // Check it: + tzset(); + //std::cout << "tzname[0]: " << tzname[0] << std::endl; + //std::cout << "tzname[1]: " << tzname[1] << std::endl; + std::string tzname_1 = tzname[1]; + bool success = (tzname_1 != ""); + if (!success) std::cerr << "Invalid TZ '" << a_value << "'. Probably GMT0 is going to be used instead !" << std::endl; + return success; + */ + } + + return true; + } + + std::string asString() const throw() { + std::string result = getValue(); + if (unsetTZ()) return ""; + if (result == "") return ""; + return result; + } +}; + + +} +} + +#endif + diff --git a/source/time/Date.cpp b/source/time/Date.cpp index fa6c8c3..e09a589 100644 --- a/source/time/Date.cpp +++ b/source/time/Date.cpp @@ -60,312 +60,146 @@ using namespace anna::time; //------------------------------------------------------------------------- Date //****************************************************************************** -//------------------------------------------------------------------------------ -//----------------------------------------------------------------- Date::Date() -//------------------------------------------------------------------------------ -Date::Date(const char * TzContext) { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "Default Constructor", ANNA_FILE_LOCATION)); - time::sccs::activate(); - - if(!functions::initialized()) { - //anna::Logger::error("You should firstly invoke anna::time::functions::initialize() before using this module", ANNA_FILE_LOCATION); - //Perhaps former couldn't be written (no Logger initialize): - std::cout << std::endl << "Develop ERROR: you should firstly invoke anna::time::functions::initialize() before using time module" << std::endl; - } - - a_local_tz = functions::getLocalTz(); - a_work_tz = a_local_tz; - initialize(TzContext); -}; - - -//------------------------------------------------------------------------------ -//----------------------------------------------------------------- Date::Date() -//------------------------------------------------------------------------------ -Date::Date(const Date & d) { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "Copy constructor", ANNA_FILE_LOCATION)); - time::sccs::activate(); - *this = d; - a_local_tz = functions::getLocalTz(); - a_work_tz = d.a_work_tz; -}; - - -//------------------------------------------------------------------------------ -//---------------------------------------------------------------- Date::~Date() -//------------------------------------------------------------------------------ -Date::~Date() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "~Date", ANNA_FILE_LOCATION)); -} - // private: - //------------------------------------------------------------------------------ -//-------------------------------------------------------------- Date::_putenv() +//----------------------------------------------------------- Date::initialize() //------------------------------------------------------------------------------ -void Date::_putenv(const char * Tz) throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "_putenv", ANNA_FILE_LOCATION)); - TZ providedTz; - - if(Tz) - providedTz.set(Tz); - else - providedTz = a_local_tz; - - if(providedTz != a_work_tz) { - a_work_tz = providedTz; -// Solaris no tiene el unsetenv, actualizamos directamente con putenv: "unset TZ", que es totalmente valido (*) -// if (a_work_tz.isUnset()) -// unsetenv ("TZ"); -// else - putenv((char *)a_work_tz.getShellAssignment().c_str()); -// (*) -// if (a_work_tz.isUnset()) -// std::cout << "Unset" << std::endl; -// else -// { -// if (a_work_tz.isEmpty()) -// std::cout << "Empty" << std::endl; -// else -// std::cout << a_work_tz.getValue() << std::endl; -// } -// std::cout << "Salida putenv poniendo " << a_work_tz.getAssignment().c_str() << ": " << putenv((char *)a_work_tz.getAssignment().c_str()) << std::endl; -// const char *kk = getenv("TZ"); -// if (kk) std::cout << "getenv: " << kk << std::endl; -// else std::cout << "getenv: NULL" << std::endl; - } +void Date::initialize(const char * TZ) throw() { + a_timestamp = ::time(NULL); + setTz(TZ); } //------------------------------------------------------------------------------ -//------------------------------------------------------- Date::set_tz_context() +//-------------------------------------------------------------- Date::refresh() //------------------------------------------------------------------------------ -void Date::set_tz_context(const char * TzContext) throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "set_tz_context", ANNA_FILE_LOCATION)); - if(TzContext) - a_TZ_context.set(TzContext); - else - a_TZ_context = a_local_tz; - - refresh_regarding_unix_timestamp(); // borns with timestamp and is based on new TZ value -} +void Date::refresh(void) throw() { + anna::Guard guard(a_mutex); + // Set current/programmed timezone + a_tz.apply(); -//------------------------------------------------------------------------------ -//------------------------------------------------------- Date::get_TZ_context() -//------------------------------------------------------------------------------ -const TZ & Date::get_TZ_context(void) const throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "get_TZ_context", ANNA_FILE_LOCATION)); - return (a_TZ_context); + // Rescue info: + struct tm *ptrTm = localtime(&a_timestamp); + a_tm = *ptrTm; } -//------------------------------------------------------------------------------ -//---------------------------------------------------------- Date::_initialize() -//------------------------------------------------------------------------------ -void Date::_initialize(const char * TzContext) throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "_initialize", ANNA_FILE_LOCATION)); - a_unix_timestamp = ::time(NULL); - set_tz_context(TzContext); // This refresh all data with the asigned TZ -} +// public: //------------------------------------------------------------------------------ -//------------------------------------------------- Date::check_yyyymmddHHmmss() +//----------------------------------------------------------------- Date::Date() //------------------------------------------------------------------------------ -void Date::check_yyyymmddHHmmss(const std::string & yyyymmddHHmmss) - -throw(anna::RuntimeException) +Date::Date(const char *TZ) { + time::sccs::activate(); -{ - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "check_yyyymmddHHmmss", ANNA_FILE_LOCATION)); - // check 2038 effect: restrictive limit - if(yyyymmddHHmmss > _2K38_EFFECT_LIMIT) { // ESTO NO LO COMPRUEBA mktime !! - throw anna::RuntimeException("Provided time/date is over 2K38 effect (19 January 2038, 03:14:07) !!", ANNA_FILE_LOCATION); + if(!functions::initialized()) { + std::cerr << std::endl << "Develop ERROR: you should firstly invoke anna::time::functions::initialize() before using time module" << std::endl; } - // check 14-digit length (i.e. "19741219100000") - int longitud = yyyymmddHHmmss.length(); - - if(longitud != 14) { - throw anna::RuntimeException("Provided time/date has not 14-digit length !!. Remember: 'yyyymmddHHmmss'", ANNA_FILE_LOCATION); - } + initialize(TZ); +}; - // All digits: - const char * ptr_cad = yyyymmddHHmmss.c_str(); - for(register int k = 0; k < longitud; k++) { - if(!isdigit(ptr_cad[k])) { - throw anna::RuntimeException("Provided time/date only can contain digits !!. Remember: 'yyyymmddHHmmss'", ANNA_FILE_LOCATION); - } - } +//------------------------------------------------------------------------------ +//---------------------------------------------------------------- Date::setTz() +//------------------------------------------------------------------------------ +void Date::setTz(const char * TZ) throw() { + if (TZ) a_tz.set(TZ); + else a_tz = functions::getSystemTimezone(); -// // Data range: -// int year, mon, mday, hour, min, sec; -// sscanf(ptr_cad, STRING_FORMAT_yyyymmddHHmmss, &year, &mon, &mday, &hour, &min, &sec); -// //year 2038 already checked -// if (mon < 1 || mon > 12) -// throw anna::RuntimeException("Month out of range (1-12) !!", ANNA_FILE_LOCATION); -// top_day = 31; -// if (mon == 2) top_day = 29/*28*/; -// if (mon == 4 || mon == 6 || mon == 9 || mon == 11) top_day = 30; -// -// if (mday < 1 || mday > top_day) -// throw anna::RuntimeException(anna::functions::asString("Day out of range (1-%d) !!", top_day), ANNA_FILE_LOCATION); -// if (hour > 23) -// throw anna::RuntimeException("Hour out of range (0-23) !!", ANNA_FILE_LOCATION); -// if (min < 0 || min > 59) -// throw anna::RuntimeException("Minute out of range (0-59) !!", ANNA_FILE_LOCATION); -// if (sec < 0 || sec > 59) -// throw anna::RuntimeException("Second out of range (0-59) !!", ANNA_FILE_LOCATION); + refresh(); } //------------------------------------------------------------------------------ -//------------------------------------- Date::refresh_regarding_unix_timestamp() +//---------------------------------------------------- Date::setSystemTimezone() //------------------------------------------------------------------------------ -void Date::refresh_regarding_unix_timestamp(void) throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "refresh_regarding_unix_timestamp", ANNA_FILE_LOCATION)); - anna::Guard guard(a_mutex); - //anna::Guard guard(a_mutex); - _putenv(get_TZ_context().getValue().c_str()); - struct tm *ptrTm; - struct tm resTm; - //ptrTm = localtime_r(&a_unix_timestamp, &resTm); NO FUNCIONA BIEN EN LINUX!! - ptrTm = localtime(&a_unix_timestamp); - // OJO: - // ctime() -> localtime() -> __tz_convert() -> __libc_lock_lock() - // So, glibc does not guarantee the sane behavior when one uses ctime() in - // signal handler. - // - // Este problema puede ser de algún bug de la librería libc.so.6 de linux. - // Recomiendan poner LD_ASSUME_KERNEL=2.4.1 - // Otra solucion sera hacerlo MT-Safe con guardas - a_tm_struct = *ptrTm; - // Returns begining - _putenv(a_local_tz.getValue().c_str()); - static char cad_aux[32]; - sprintf(cad_aux, STRING_FORMAT_yyyymmddHHmmss, 1900 + a_tm_struct.tm_year, 1 + (a_tm_struct.tm_mon), a_tm_struct.tm_mday, - a_tm_struct.tm_hour, a_tm_struct.tm_min, a_tm_struct.tm_sec); - // Assignment: - _yyyymmddHHmmss = cad_aux; +void Date::setSystemTimezone() throw() { + a_tz = functions::getSystemTimezone(); + // Refresh the other data: + refresh(); } -// public - -// sets - //------------------------------------------------------------------------------ -//----------------------------------------------------------- Date::initialize() +//---------------------------------------------------------------- Date::store() //------------------------------------------------------------------------------ -void Date::initialize(const char * TzContext) throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "initialize", ANNA_FILE_LOCATION)); - _initialize(TzContext); +void Date::store(const time_t & unixTimestamp) throw() { + a_timestamp = unixTimestamp; + // Refresh the other data: + refresh(); } //------------------------------------------------------------------------------ -//--------------------------------------------------------- Date::setTzContext() +//------------------------------------------------------------- Date::storeNtp() //------------------------------------------------------------------------------ -void Date::setTzContext(const char * TzContext) throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "setTzContext", ANNA_FILE_LOCATION)); - set_tz_context(TzContext); +void Date::storeNtp(const unsigned int & ntpTimestamp) throw() { + a_timestamp = ntpTimestamp - TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970; + // Refresh the other data: + refresh(); } - //------------------------------------------------------------------------------ //---------------------------------------------------------------- Date::store() //------------------------------------------------------------------------------ -void Date::store(const char* dateTimeAsStringFormat, const std::string & dateTimeAsString, const char * OriginTz) +void Date::store(const struct tm &date, const char *TZ) throw(anna::RuntimeException) { - // Get equivalent 'tm': - struct tm TmOrigin; - memset(&TmOrigin, 0, sizeof(TmOrigin)); - - if(strptime(dateTimeAsString.c_str(), dateTimeAsStringFormat, &TmOrigin) == NULL) { - std::string msg("Error during strptime() conversion: '"); - msg += dateTimeAsString; - msg += "' can't be interpreted as '"; - msg += dateTimeAsStringFormat; - msg += "'"; - throw anna::RuntimeException(msg, ANNA_FILE_LOCATION); + if (TZ) { + Timezone aux; + aux.set(TZ); + aux.apply(); + } + else { + functions::getSystemTimezone().apply(); } - store(TmOrigin, OriginTz); -} - - -void Date::store(const std::string & yyyymmddHHmmss, const char * OriginTz) - -throw(anna::RuntimeException) - -{ - check_yyyymmddHHmmss(yyyymmddHHmmss); // launch exception when format error (14 digits) - // Could be good, but not assing (perhaps 'strptime' could fail) -// Limitaciones del 'strptime' usado por 'store(format, cadena)': -// El formato directamente sacado de este interfaz ("%Y%m%d%H%M%S") no valida las fechas (meses 13, dias 32, etc), -// para ello hay que poner separadores en el formato (mucho más robusto). Aun asi, 'strptime' sigue "tragandose cosas" -// como por ejemplo segundos=60 (07:54:60 lo convierte a 07:55:00), y no comprueba los años bisiestos (de febrero traga -// 29 dias siempre, aunque no traga 30) -// Solucion, convertimos la cadena a otra con separadores para dar mayor robustez (aunque no es perfecto como se ha dicho): - int tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec; - sscanf(yyyymmddHHmmss.c_str(), STRING_FORMAT_yyyymmddHHmmss, &tm_year, &tm_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec); - static char cad_aux[64]; - sprintf(cad_aux, "%04d-%02d-%02d-%02d-%02d-%02d", tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec); - store(STRPTIME_FORMAT_yyyy_mm_dd_HH_mm_ss, cad_aux, OriginTz); -} - - -//------------------------------------------------------------------------------ -//---------------------------------------------------------------- Date::store() -//------------------------------------------------------------------------------ -void Date::store(const struct tm & TmOrigen, const char * OriginTz) - -throw(anna::RuntimeException) - -{ - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "store", ANNA_FILE_LOCATION)); - _putenv(OriginTz); // Set '-1' on 'tm_isdst', or it could be an ambiguity on tranformation // returning inexact timestamp. This setting force system to check the correct flag: - struct tm TmOrigen_isdst_menos1 = TmOrigen; - TmOrigen_isdst_menos1.tm_isdst = -1; - time_t unixTimestamp = mktime((struct tm*) & TmOrigen_isdst_menos1); + struct tm dateIsdst_1 = date; + dateIsdst_1.tm_isdst = -1; + time_t unixTimestamp = mktime((struct tm*) & dateIsdst_1); if(unixTimestamp == (time_t) - 1) throw anna::RuntimeException("Error during mktime() conversion !!", ANNA_FILE_LOCATION); - a_unix_timestamp = unixTimestamp; + a_timestamp = unixTimestamp; + // Refresh the othr data: - refresh_regarding_unix_timestamp(); + refresh(); } //------------------------------------------------------------------------------ //---------------------------------------------------------------- Date::store() //------------------------------------------------------------------------------ -void Date::store(const time_t & unixTimestamp) throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "store unix timestamp", ANNA_FILE_LOCATION)); - a_unix_timestamp = unixTimestamp; - // Refresh the other data: - refresh_regarding_unix_timestamp(); -} +void Date::store(const std::string &stringDate, const char *TZ, const char *strptimeFormat) +throw(anna::RuntimeException) +{ + if(strptimeFormat == NULL) + throw anna::RuntimeException("Invalid NULL strptimeFormat !!", ANNA_FILE_LOCATION); + // Get equivalent 'tm': + struct tm tmOrigin; + memset(&tmOrigin, 0, sizeof(tmOrigin)); -//------------------------------------------------------------------------------ -//------------------------------------------------------------- Date::storeNtp() -//------------------------------------------------------------------------------ -void Date::storeNtp(const unsigned int & ntpTimestamp) throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "store ntp timestamp", ANNA_FILE_LOCATION)); - a_unix_timestamp = ntpTimestamp - TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970; - // Refresh the other data: - refresh_regarding_unix_timestamp(); + if(strptime(stringDate.c_str(), strptimeFormat, &tmOrigin) == NULL) { + std::string msg("Error during strptime() conversion: '"); + msg += stringDate; + msg += "' can't be interpreted as '"; + msg += strptimeFormat; + msg += "'"; + throw anna::RuntimeException(msg, ANNA_FILE_LOCATION); + } + + store(tmOrigin, TZ); } @@ -373,65 +207,38 @@ void Date::storeNtp(const unsigned int & ntpTimestamp) throw() { //-------------------------------------------------------------- Date::operator= //------------------------------------------------------------------------------ Date & Date::operator = (const Date & d) { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "operator=", ANNA_FILE_LOCATION)); // avoid itself copy: i.e., Date a,b; a=&b; a=b; b=a; if(this == &d) return (*this); - a_TZ_context = d.get_TZ_context(); - a_unix_timestamp = d.getUnixTimestamp(); - a_tm_struct = d.getTm(); - _yyyymmddHHmmss = d.yyyymmddHHmmss(); + a_timestamp = d.getUnixTimestamp(); + a_tz.set(d.getTz()); + a_tm = d.getTm(); return (*this); } // gets -//------------------------------------------------------------------------------ -//--------------------------------------------------------- Date::getTzContext() -//------------------------------------------------------------------------------ -const std::string & Date::getTzContext(void) const throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "getTzContext", ANNA_FILE_LOCATION)); - return (a_TZ_context.getValue()); -} - - -//------------------------------------------------------------------------------ -//------------------------------------------------------- Date::yyyymmddHHmmss() -//------------------------------------------------------------------------------ -const std::string & Date::yyyymmddHHmmss(void) const throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "yyyymmddHHmmss", ANNA_FILE_LOCATION)); - return (_yyyymmddHHmmss); -} - //------------------------------------------------------------------------------ -//----------------------------------------------------- Date::getUnixTimestamp() +//--------------------------------------------------------------- Date::getDay() //------------------------------------------------------------------------------ -const time_t & Date::getUnixTimestamp(void) const throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "getUnixTimestamp", ANNA_FILE_LOCATION)); - return (a_unix_timestamp); -} +const char *Date::getDay(void) const throw() { + static const char *weekdayname[] = {"Sunday", "Monday", "Tuesday", + "Wednesday", "Thursday", "Friday", "Saturday"}; -//------------------------------------------------------------------------------ -//------------------------------------------------------ Date::getNtpTimestamp() -//------------------------------------------------------------------------------ -unsigned int Date::getNtpTimestamp(void) const throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "getNtpTimestamp", ANNA_FILE_LOCATION)); - unsigned int ntp_timestamp = a_unix_timestamp + TIMESTAMP_OFFSET_NTP1900_OVER_UNIX1970; - return (ntp_timestamp); + return weekdayname[a_tm.tm_wday]; } //------------------------------------------------------------------------------ -//---------------------------------------------------------------- Date::getTm() +//------------------------------------------------------- Date::yyyymmddHHmmss() //------------------------------------------------------------------------------ -const struct tm & Date::getTm(void) const throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "getTm", ANNA_FILE_LOCATION)); - - - return (a_tm_struct); +std::string Date::yyyymmddHHmmss(void) const throw() { + return anna::functions::asString("%04d%02d%02d%02d%02d%02d", + 1900 + a_tm.tm_year, 1 + (a_tm.tm_mon), a_tm.tm_mday, + a_tm.tm_hour, a_tm.tm_min, a_tm.tm_sec); } @@ -439,34 +246,16 @@ const struct tm & Date::getTm(void) const throw() { //------------------------------------------------------------- Date::asString() //------------------------------------------------------------------------------ std::string Date::asString(void) const throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "asString", ANNA_FILE_LOCATION)); - std::string trace; - static char cad_aux[256]; - //bool differentContextAndLocalTZ = getTzContext() != a_local_tz.getValue(); - sprintf(cad_aux, "%02d/%02d/%04d %02d:%02d:%02d", - // yyyymmddHHmmss().c_str(), - getTm().tm_mday, 1 + (getTm().tm_mon), 1900 + getTm().tm_year, - getTm().tm_hour, getTm().tm_min, getTm().tm_sec); - trace = cad_aux; - -// if (!(get_TZ_context().isUnset())) -// if (!(get_TZ_context().isEmpty())) -// { trace += " "; trace += get_TZ_context().getValue(); } - if(getTzContext() != ""/* && differentContextAndLocalTZ*/) { trace += " "; trace += getTzContext(); } - - sprintf(cad_aux, ", isdst = %d [Unix Timestamp: %ld, Ntp Timestamp: %u]", getTm().tm_isdst, getUnixTimestamp(), getNtpTimestamp()); - trace += cad_aux; - -// if (!(a_local_tz.isUnset())) -// if (!(a_local_tz.isEmpty())) -// { trace += ", Local TZ = "; trace += a_local_tz.getValue(); } - if(a_local_tz.getValue() != "") { - //trace += differentContextAndLocalTZ ? ", Local TZ = ":", TZ Context = Local TZ = "; - trace += ", Local TZ = "; - trace += a_local_tz.getValue(); - } - return (trace); + std::string result = anna::functions::asString("%s %02d/%02d/%04d %02d:%02d:%02d ", getDay(), + getTm().tm_mday, 1 + (getTm().tm_mon), 1900 + getTm().tm_year, + getTm().tm_hour, getTm().tm_min, getTm().tm_sec); + result += getTzAsString(); + result += anna::functions::asString(", isdst = %d [Unix Timestamp: %ld, Ntp Timestamp: %u]", getTm().tm_isdst, getUnixTimestamp(), getNtpTimestamp()); + result += ", System Timezone: "; + result += functions::getSystemTimezone().asString(); + + return result; } @@ -474,21 +263,15 @@ std::string Date::asString(void) const throw() { //---------------------------------------------------------------- Date::asXML() //------------------------------------------------------------------------------ anna::xml::Node* Date::asXML(anna::xml::Node* parent) const throw() { - //LOGMETHOD (TraceMethod tm ("anna::time::Date", "asXML", ANNA_FILE_LOCATION)); //anna::xml::Node* result = parent->createChild("anna.time.Date"); - //bool differentContextAndLocalTZ = getTzContext() != a_local_tz.getValue(); parent->createAttribute("Date", anna::functions::asString("%02d/%02d/%04d", getTm().tm_mday, 1 + (getTm().tm_mon), 1900 + getTm().tm_year)); + parent->createAttribute("Day", getDay()); parent->createAttribute("Time", anna::functions::asString("%02d:%02d:%02d", getTm().tm_hour, getTm().tm_min, getTm().tm_sec)); - - if(getTzContext() != ""/* && differentContextAndLocalTZ*/) parent->createAttribute("TZContext", getTzContext()); - + parent->createAttribute("TZ", getTzAsString()); parent->createAttribute("Isdst", (getTm().tm_isdst) ? "yes" : "no"); -// parent->createAttribute("UnixTimestamp", anna::functions::asString("%lu seconds", getUnixTimestamp())); // unsigned long -> %lu -// parent->createAttribute("NtpTimestamp", anna::functions::asString("%u seconds", getNtpTimestamp())); // unsigned int -> %u parent->createAttribute("UnixTimestamp", anna::functions::asString((const int)getUnixTimestamp())); parent->createAttribute("NtpTimestamp", anna::functions::asString(getNtpTimestamp())); - - if(a_local_tz.getValue() != "") parent->createAttribute(/*differentContextAndLocalTZ ? "LocalTZ":"TZContext=LocalTZ"*/"LocalTZ", a_local_tz.getValue()); + parent->createAttribute("SystemTimezone", functions::getSystemTimezone().asString()); return parent; } diff --git a/source/time/functions.cpp b/source/time/functions.cpp index b044fd2..e3ef33d 100644 --- a/source/time/functions.cpp +++ b/source/time/functions.cpp @@ -36,7 +36,7 @@ // Local #include -#include +#include // Standard #include // getenv @@ -49,27 +49,16 @@ namespace anna { namespace time { -TZ shell_local_tz; +Timezone SystemTimezone; unsigned long long int SecondsReference; } } void anna::time::functions::initialize() throw() { -//throw(anna::RuntimeException) { -// If the system is configured properly, the default time zone will be correct. You might set TZ if you are -// using a computer over a network from a different time zone, and would like times reported to you in the -// time zone local to you, rather than what is local to the computer. static bool cached = false; - if(!cached) { - // The getenv() function need not be reentrant. A function that is not required to be reentrant is not - // required to be thread-safe. - const char *local_tz = getenv("TZ"); - shell_local_tz.set(local_tz); -// if (shell_local_tz.isUnset()) -// std::cout << "'TZ' enviroment variable is not defined on shell (it shouldn't be necesary)" << std::endl; -// setControlPoint(); + SystemTimezone.set(getenv("TZ")); SecondsReference = 0; cached = true; } @@ -83,12 +72,12 @@ unsigned long long int anna::time::functions::getSecondsReference() throw() { return SecondsReference; } -const anna::time::TZ & anna::time::functions::getLocalTz(void) throw() { - return (shell_local_tz); +const anna::time::Timezone & anna::time::functions::getSystemTimezone(void) throw() { + return (SystemTimezone); } bool anna::time::functions::initialized(void) throw() { - return (shell_local_tz.isInitialized()); + return (SystemTimezone.isInitialized()); } unsigned long long int anna::time::functions::unixSeconds(void) throw() { diff --git a/test/time/main.cpp b/test/time/main.cpp index 3820199..b8ddcc0 100644 --- a/test/time/main.cpp +++ b/test/time/main.cpp @@ -51,10 +51,9 @@ using namespace anna; // http://www.boost.org/doc/libs/1_44_0/libs/test/doc/html/utf/testing-tools/reference.html BOOST_AUTO_TEST_CASE(date) { - const char * tzarg = NULL; anna::time::functions::initialize(); anna::time::Date current, current2; - current.setCurrent(); + current.setNow(); unsigned int ntptime = current.getNtpTimestamp(); time_t unixtime = current.getUnixTimestamp(); current2.storeNtp(ntptime); @@ -62,26 +61,33 @@ BOOST_AUTO_TEST_CASE(date) { current2.storeUnix(unixtime); std::string unixStr = current2.asString(); BOOST_REQUIRE_EQUAL(ntpStr, unixStr); + anna::time::Date myBirth("CET"); // 19 December 1974, 10:00 (GMT+1 = UTC + 1 = CET) - //anna::time::Date myBirth; // if Context TZ = shell TZ = "CET" myBirth.store("19741219101500"); // yyyymmddHHmmss anna::time::Date same_moment_canary_island("GMT"); same_moment_canary_island.store(myBirth.getUnixTimestamp()); - myBirth.setTzContext("GMT"); + myBirth.setTz("GMT"); BOOST_REQUIRE_EQUAL(same_moment_canary_island.yyyymmddHHmmss(), myBirth.yyyymmddHHmmss()); + anna::time::Date birthday("EET"); birthday.store(same_moment_canary_island.getTm(), "GMT"); // TZ origin = "GMT" - //BOOST_REQUIRE_EQUAL(birthday, myBirth); BOOST_REQUIRE_EQUAL(birthday.getUnixTimestamp(), myBirth.getUnixTimestamp()); - myBirth.setTzContext(anna::time::functions::getLocalTz().getValue().c_str()); - birthday.setTzContext(anna::time::functions::getLocalTz().getValue().c_str()); // Go from "EET" to "CET" - // TODO: review the following test case: - //BOOST_REQUIRE_EQUAL(myBirth.yyyymmddHHmmss(), birthday.yyyymmddHHmmss()); + + myBirth.setTz(); + birthday.setTz(); + BOOST_REQUIRE_EQUAL(myBirth.asString(), birthday.asString()); + BOOST_REQUIRE_EQUAL(myBirth.yyyymmddHHmmss(), birthday.yyyymmddHHmmss()); + + myBirth.setTz("GMT"); + BOOST_REQUIRE_EQUAL(myBirth.yyyymmddHHmmss(), "19741219091500"); + // Adding 18 years to 'myBirth': + same_moment_canary_island.setTz("CET"); + anna::time::Date eighteen("CET"); struct tm Tm = myBirth.getTm(); Tm.tm_year += 18; - anna::time::Date eighteen("CET"); - eighteen.store(Tm); + eighteen.store(Tm, "GMT"); BOOST_REQUIRE(eighteen >= same_moment_canary_island); + BOOST_REQUIRE_EQUAL(eighteen.yyyymmddHHmmss(), "19921219101500"); }