libanna.time refactoring
[anna.git] / include / anna / time / Date.hpp
index 8d699ff..f0a46be 100644 (file)
@@ -44,7 +44,7 @@
 #include <time.h>
 
 // Local
-#include <anna/time/internal/TZ.hpp>
+#include <anna/time/internal/Timezone.hpp>
 
 
 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.
 *
 * <pre>
 *
-* 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: <TZ unset\>
+*  myBirth: Thursday 19/12/1974 10:15:00 CET, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
+*  same_moment_canary_island: Thursday 19/12/1974 09:15:00 GMT, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
+*  myBirth on GMT: Thursday 19/12/1974 09:15:00 GMT, isdst = 0 [Unix Timestamp: 156676500, Ntp Timestamp: 2365665300], System Timezone: <TZ unset\>
+*  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: <TZ unset\>
+*  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: <TZ unset\>
+*  Setting Local timezone ...
+*  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\>
+*  EQUAL: birthday (Local Timezone)= 19741219101500; myBirth (Local Timezone)= 19741219101500
+*  myBirth TZ  = <TZ unset\>
+*  birthday TZ  = <TZ unset\>
+*  myBirth in GMT = 19741219091500
 *
 * </pre>
 */
@@ -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();