tzwrapper.h

Go to the documentation of this file.
00001 ///
00002 /// \file       tzwrapper.h
00003 ///             Timezone adjustment class, wrapping the TZ environment
00004 ///             variable to make struct tm -> time_t conversions easier.
00005 ///
00006 
00007 /*
00008     Copyright (C) 2010-2012, Chris Frey <cdfrey@foursquare.net>, To God be the glory
00009     Released to the public domain.
00010     Included in Barry and Barrified the namespace July 2010
00011 */
00012 
00013 #ifndef __TZWRAPPER_H__
00014 #define __TZWRAPPER_H__
00015 
00016 #include "dll.h"
00017 #include <string>
00018 #include <time.h>
00019 #include <stdlib.h>
00020 
00021 namespace Barry { namespace Sync {
00022 
00023 /// Parses ISO timestamp in the format of YYYYMMDDTHHMMSS[Z]
00024 /// or YYYY-MM-DDTHH:MM:SS.uuu-HH:MM
00025 /// and places broken down time in result.
00026 /// The trailing Z is optional in the format.
00027 /// If the Z exists, utc will be set to true, otherwise false.
00028 /// If zoneminutes is not null, and if a timezone offset is
00029 /// specified, it will be filled in and *zone set to true.
00030 /// Otherwise, *zone will be set to false.
00031 /// Returns NULL on error.
00032 /// Thread-safe.
00033 BXEXPORT struct tm* iso_to_tm(const char *timestamp,
00034                                 struct tm *result,
00035                                 bool &utc,
00036                                 bool *zone = 0,
00037                                 int *zoneminutes = 0);
00038 
00039 /// Turns the struct tm into an ISO timestamp in the format
00040 /// of YYYYMMDDTHHMMSS[Z].  The Z is appended if utc is true.
00041 /// This function assumes that t contains sane values, and will
00042 /// create the target string directly from its content.
00043 /// Returns the ISO timestamp, or empty string on error.
00044 /// If t contains sane values, this function should never fail.
00045 /// Thread-safe.
00046 BXEXPORT std::string tm_to_iso(const struct tm *t, bool utc);
00047 
00048 /// utc_mktime() converts a struct tm that contains
00049 /// broken down time in utc to a time_t.  This function uses
00050 /// a brute-force method of conversion that does not require
00051 /// the environment variable TZ to be changed at all, and is
00052 /// therefore slightly more thread-safe in that regard.
00053 ///
00054 /// The difference between mktime() and utc_mktime() is that
00055 /// standard mktime() expects the struct tm to be in localtime,
00056 /// according to the current TZ and system setting, while utc_mktime()
00057 /// always assumes that the struct tm is in UTC, and converts it
00058 /// to time_t regardless of what TZ is currently set.
00059 ///
00060 /// The difference between utc_mktime() and TzWrapper::iso_mktime()
00061 /// is that iso_mktime() will parse straight from an ISO string,
00062 /// and if the ISO timestamp ends in a 'Z', it will behave like
00063 /// utc_mktime() except it will alter the TZ environment variable
00064 /// to do it.  If the ISO timestamp has no 'Z', then iso_mktime()
00065 /// behaves like mktime().
00066 ///
00067 BXEXPORT time_t utc_mktime(struct tm *utctime);
00068 
00069 //
00070 // class TzWrapper
00071 //
00072 /// Wrapper class for the TZ environment variable.  This class allows
00073 /// setting TZ to any number of variables, and will restore the original
00074 /// setting on destruction.
00075 ///
00076 /// By default, TzWrapper does not change the environment at all, but
00077 /// only saves it.  Alternately, you can use the timezone constructor
00078 /// to save and set a new timezone on the fly.
00079 ///
00080 /// Each Set() and Unset() function returns a reference to TzWrapper,
00081 /// so that you can chain function calls like this:
00082 ///
00083 ///     time_t utc = TzWrapper("Canada/Pacific").mktime(&pacific_tm);
00084 ///
00085 /// In addition, there are two static utility functions used to
00086 /// convert ISO timestamps to struct tm* and time_t values.
00087 ///
00088 /// Note: This class is not thread-safe, since it modifies the TZ
00089 ///       environment variable without locking.  If other threads
00090 ///       use time functions, this may interfere with their behaviour.
00091 ///
00092 class BXEXPORT TzWrapper
00093 {
00094         std::string m_orig_tz;
00095         bool m_tz_exists;
00096         bool m_dirty;
00097 
00098 protected:
00099         void SaveTz()
00100         {
00101                 char *ptz = getenv("TZ");
00102                 if( ptz )
00103                         m_orig_tz = ptz;
00104                 m_tz_exists = ptz;
00105         }
00106 
00107         void RestoreTz()
00108         {
00109                 if( m_dirty ) {
00110                         if( m_tz_exists )
00111                                 Set(m_orig_tz.c_str());
00112                         else
00113                                 Unset();
00114 
00115                         m_dirty = false;
00116                 }
00117         }
00118 
00119 public:
00120         /// Does not change TZ, only saves current setting
00121         TzWrapper()
00122                 : m_dirty(false)
00123         {
00124                 SaveTz();
00125         }
00126 
00127         /// Saves current setting and sets TZ to new timezone value.
00128         /// If timezone is null, it is the same as calling Unset().
00129         explicit TzWrapper(const char *timezone)
00130                 : m_dirty(false)
00131         {
00132                 SaveTz();
00133                 Set(timezone);
00134         }
00135 
00136         ~TzWrapper()
00137         {
00138                 RestoreTz();
00139         }
00140 
00141         /// Set TZ to a new value.  If timezone is null, it is the
00142         /// same as calling Unset().
00143         ///
00144         /// If timezone is an empty or invalid timezone string, it
00145         /// is the same as calling SetUTC().
00146         TzWrapper& Set(const char *timezone)
00147         {
00148                 if( timezone )
00149                         setenv("TZ", timezone, 1);
00150                 else
00151                         unsetenv("TZ");
00152                 tzset();
00153                 m_dirty = true;
00154                 return *this;
00155         }
00156 
00157         /// Deletes TZ from the environment, which has the same effect
00158         /// as calling SetSysLocal().  This is not a permanent
00159         /// condition, since TZ will be restored to original state
00160         /// upon destruction.
00161         TzWrapper& Unset()
00162         {
00163                 unsetenv("TZ");
00164                 tzset();
00165                 m_dirty = true;
00166                 return *this;
00167         }
00168 
00169         /// Set timezone to UTC
00170         TzWrapper& SetUTC()
00171         {
00172                 return Set("");
00173         }
00174 
00175         /// Set timezone via offset in minutes
00176         /// Negative minutes goes west, positive goes east
00177         /// i.e. -05:00 is EST
00178         TzWrapper& SetOffset(int zoneminutes);
00179 
00180         /// Use system localtime.  This overrides any TZ value that the
00181         /// user may have set before running your program.
00182         TzWrapper& SetSysLocal()
00183         {
00184                 return Unset();
00185         }
00186 
00187         /// Use the default TZ value that the user set before running
00188         /// this program.  In most cases, this will be the user's
00189         /// preferred local timezone.
00190         TzWrapper& SetDefault()
00191         {
00192                 RestoreTz();
00193                 return *this;
00194         }
00195         /// Same as SetDefault()
00196         TzWrapper& SetOrig()
00197         {
00198                 return SetDefault();
00199         }
00200 
00201         //
00202         // C library wrappers, for calls like:
00203         //      time_t t = TzWrapper("Canada/Pacific").mktime(tm);
00204         //
00205         char* asctime(const struct tm *t) const { return ::asctime(t); }
00206         char* asctime_r(const struct tm *t, char *buf) const
00207                 { return ::asctime_r(t, buf); }
00208         char* ctime(const time_t *t) const { return ::ctime(t); }
00209         char* ctime_r(const time_t *t, char *buf) const
00210                 { return ::ctime_r(t, buf); }
00211         struct tm* gmtime(const time_t *t) const { return ::gmtime(t); }
00212         struct tm* gmtime_r(const time_t *t, struct tm *result) const
00213                 { return ::gmtime_r(t, result); }
00214         struct tm* localtime(const time_t *t) const { return ::localtime(t); }
00215         struct tm* localtime_r(const time_t *t, struct tm *result) const
00216                 { return ::localtime_r(t, result); }
00217         time_t mktime(struct tm *t) { return ::mktime(t); }
00218 
00219         //
00220         // Additional utility functions
00221         //
00222 
00223         /// Converts an ISO timestamp (YYYYMMDDTHHMMWW[Z]) into a
00224         /// unix time_t.  If the 'Z' UTC flag is not specified, then
00225         /// the timestamp will be assumed to be in the current
00226         /// default timezone.  Otherwise, SetUTC() will be used for the
00227         /// conversion.
00228         ///
00229         /// This function uses an internal TzWrapper to adjust TZ
00230         /// if necessary, which is why it is a static function
00231         /// of TzWrapper, instead of a standalone function.
00232         ///
00233         static time_t iso_mktime(const char *timestamp);
00234 };
00235 
00236 }} // namespace Barry::Sync
00237 
00238 #endif
00239