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-2011, 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