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