Qore Programming Language  1.12.0
QoreTimeZoneManager.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  QoreTimeZoneManager.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2022 Qore Technologies, s.r.o.
8 
9  Permission is hereby granted, free of charge, to any person obtaining a
10  copy of this software and associated documentation files (the "Software"),
11  to deal in the Software without restriction, including without limitation
12  the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  and/or sell copies of the Software, and to permit persons to whom the
14  Software is furnished to do so, subject to the following conditions:
15 
16  The above copyright notice and this permission notice shall be included in
17  all copies or substantial portions of the Software.
18 
19  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  DEALINGS IN THE SOFTWARE.
26 
27  Note that the Qore library is released under a choice of three open-source
28  licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29  information.
30 */
31 
32 #ifndef QORE_TIMEZONEMANAGER_H
33 
34 #define QORE_TIMEZONEMANAGER_H
35 
36 #include <cinttypes>
37 #include <map>
38 #include <string>
39 #include <vector>
40 
41 #ifndef LOCALTIME_LOCATION
42 #define LOCALTIME_LOCATION "/etc/localtime"
43 #endif
44 
45 #ifndef ZONEINFO_LOCATION
46 #define ZONEINFO_LOCATION "/usr/share/zoneinfo"
47 #endif
48 
49 DLLLOCAL extern const char *STATIC_UTC;
50 
51 // transition info structure
52 hashdecl QoreTransitionInfo {
53  int32_t utcoff; // UTC offset in seconds east (negative for west)
54  std::string abbr; // time zone abbreviation (i.e. "EST")
55  bool isdst; // is daylight standard time?
56  bool isstd; // transition is standard time (true) or wall clock time (false)
57  bool isutc; // transition is UTC (true) or local time (false)
58 };
59 
60 typedef std::vector<QoreTransitionInfo> trans_vec_t;
61 
62 hashdecl QoreLeapInfo {
63  int ttime; // transition time
64  int total; // total correction after transition time
65 };
66 
67 hashdecl QoreDSTTransition {
68  int time;
69  hashdecl QoreTransitionInfo *trans;
70 };
71 
72 class AbstractQoreZoneInfo {
73 protected:
74  // region or time zone locale name (i.e. "Europe/Prague" or "-06:00" for UTC - 06:00)
75  std::string name;
76  // UTC offset in seconds east; -1 = unknown
77  int utcoff;
78  // true if the zone ever has daylight savings time, false if not
79  bool has_dst;
80 
81  // returns the UTC offset and local time zone name for the given time given as seconds from the epoch (1970-01-01Z)
82  DLLLOCAL virtual int getUTCOffsetImpl(int64 epoch_offset, bool &is_dst, const char *&zone_name) const = 0;
83 
85  DLLLOCAL AbstractQoreZoneInfo(int n_utcoff, const std::string& n_name) : utcoff(n_utcoff), has_dst(false) {
86  }
87 
88 public:
89  DLLLOCAL AbstractQoreZoneInfo() : utcoff(-1), has_dst(false) {
90  }
91 
92  DLLLOCAL AbstractQoreZoneInfo(const std::string &n_name, int n_utcoff = -1) : name(n_name), utcoff(n_utcoff), has_dst(false) {
93  // issue #3736: do not return leading path
94  if (!name.compare(0, localtime_path_prefix.size(), localtime_path_prefix)) {
95  name = name.c_str() + localtime_path_prefix.size();
96  }
97  }
98 
99  virtual DLLLOCAL ~AbstractQoreZoneInfo() {
100  }
101 
102  // returns general UTC offset for the time zone's standard time in seconds east
103  DLLLOCAL int getUTCOffset() const {
104  return utcoff == -1 ? 0 : utcoff;
105  }
106 
107  // returns the UTC offset for the given time given as seconds from the epoch (1970-01-01Z)
108  DLLLOCAL int getUTCOffset(int64 epoch_offset) const {
109  const char *temp;
110  bool is_dst;
111  return getUTCOffsetImpl(epoch_offset, is_dst, temp);
112  }
113 
114  // returns the UTC offset and local time zone name for the given time given as seconds from the epoch (1970-01-01Z)
115  DLLLOCAL int getUTCOffset(int64 epoch_offset, bool &is_dst) const {
116  const char *temp;
117  return getUTCOffsetImpl(epoch_offset, is_dst, temp);
118  }
119 
120  // returns the UTC offset and local time zone name for the given time given as seconds from the epoch (1970-01-01Z)
121  DLLLOCAL int getUTCOffset(int64 epoch_offset, bool &is_dst, const char *&zone_name) const {
122  return getUTCOffsetImpl(epoch_offset, is_dst, zone_name);
123  }
124 
125  // returns true if the zone has daylight savings time ever
126  DLLLOCAL bool hasDST() const {
127  return has_dst;
128  }
129 
130  DLLLOCAL const char* getRegionName() const {
131  return name.c_str();
132  }
133 
134  // static versions of methods follow which perform null-pointer check on the passed zone
135 
136  // returns general UTC offset for the time zone's standard time in seconds east
137  DLLLOCAL static int getUTCOffset(const AbstractQoreZoneInfo* zone) {
138  return zone ? zone->getUTCOffset() : 0;
139  }
140 
141  // returns the UTC offset for the given time given as seconds from the epoch (1970-01-01Z)
142  DLLLOCAL static int getUTCOffset(const AbstractQoreZoneInfo* zone, int64 epoch_offset) {
143  return zone ? zone->getUTCOffset(epoch_offset) : 0;
144  }
145 
146  // returns the UTC offset and local time zone name for the given time given as seconds from the epoch (1970-01-01Z)
147  DLLLOCAL static int getUTCOffset(const AbstractQoreZoneInfo* zone, int64 epoch_offset, bool &is_dst) {
148  if (zone)
149  return zone->getUTCOffset(epoch_offset, is_dst);
150  is_dst = false;
151  return 0;
152  }
153 
154  // returns the UTC offset and local time zone name for the given time given as seconds from the epoch (1970-01-01Z)
155  DLLLOCAL static int getUTCOffset(const AbstractQoreZoneInfo* zone, int64 epoch_offset, bool &is_dst, const char *&zone_name) {
156  if (zone)
157  return zone->getUTCOffset(epoch_offset, is_dst, zone_name);
158  is_dst = false;
159  zone_name = "UTC";
160  return 0;
161  }
162 
163  // returns true if the zone has daylight savings time ever
164  DLLLOCAL static bool hasDST(const AbstractQoreZoneInfo* zone) {
165  return zone ? zone->has_dst : false;
166  }
167 
168  DLLLOCAL static const char *getRegionName(const AbstractQoreZoneInfo* zone) {
169  return zone ? zone->getRegionName() : STATIC_UTC;
170  }
171 
172  static std::string localtime_path_prefix;
173  static std::string localtime_location;
174 };
175 
176 // offsets are normally in the range of -12 to +14 UTC
177 // implements a simple offset from UTC
178 class QoreOffsetZoneInfo : public AbstractQoreZoneInfo {
179 protected:
180  DLLLOCAL virtual int getUTCOffsetImpl(int64 epoch_offset, bool &is_dst, const char *&zone_name) const {
181  zone_name = name.c_str();
182  is_dst = false;
183  return utcoff;
184  }
185 public:
186  DLLLOCAL QoreOffsetZoneInfo(std::string &n_name, int seconds_east) : AbstractQoreZoneInfo(seconds_east, n_name) {
187  }
188  DLLLOCAL QoreOffsetZoneInfo(const char *n_name, int seconds_east) : AbstractQoreZoneInfo(seconds_east, n_name) {
189  }
190 };
191 
192 class QoreZoneInfo : public AbstractQoreZoneInfo {
193 protected:
194  // first positive transition entry (after the epoch)
195  int first_pos;
196  bool valid;
197  const char *std_abbr; // standard time abbreviation
198 
199  // QoreDSTTransition times
200  typedef std::vector<QoreDSTTransition> dst_transition_vec_t;
201  dst_transition_vec_t QoreDSTTransitions;
202 
203  // QoreTransitionInfo array
204  trans_vec_t tti;
205 
206  // leap info vector
207  typedef std::vector<QoreLeapInfo> leap_vec_t;
208  leap_vec_t leapinfo;
209 
210  // returns the UTC offset and local time zone name for the given time given as seconds from the epoch (1970-01-01Z)
211  DLLLOCAL virtual int getUTCOffsetImpl(int64 epoch_offset, bool &is_dst, const char *&zone_name) const;
212 
213 public:
214  DLLLOCAL QoreZoneInfo(QoreString &root, std::string &n_name, ExceptionSink *xsink);
215 
216  DLLLOCAL virtual ~QoreZoneInfo() {
217  }
218 
219  DLLLOCAL operator bool() const {
220  return valid;
221  }
222 
223  DLLLOCAL const trans_vec_t &getTransitionList() const {
224  return tti;
225  }
226 };
227 
228 #ifdef _Q_WINDOWS
229 class QoreWindowsZoneInfo : public AbstractQoreZoneInfo {
230 protected:
231  QoreString display, // display name for the zone from the registry
232  daylight, // name for daylight savings time for the zone from the registry
233  standard; // standard name for the zone from the registry
234 
235  bool valid, // is the object valid
236  rule, // is it based on a rule
237  daylight_first; // is the first transition in the year a transition to daylight savings time?
238 
239  // UTC offset for daylight savings time in seconds east
240  int dst_off;
241 
242  // DST transition times
243  SYSTEMTIME daylight_date, standard_date;
244 
245  // returns the UTC offset and local time zone name for the given time given as seconds from the epoch (1970-01-01Z)
246  DLLLOCAL virtual int getUTCOffsetImpl(int64 epoch_offset, bool &is_dst, const char *&zone_name) const;
247 
248  // given a year, get the epoch offsets in seconds for DST and standard time
249  DLLLOCAL void getTransitions(int year, int64 &dst, int64 &std) const;
250 
251 public:
252  DLLLOCAL QoreWindowsZoneInfo(const char *name, ExceptionSink *xsink);
253 
254  DLLLOCAL virtual ~QoreWindowsZoneInfo() {
255  }
256 
257  DLLLOCAL operator bool() const {
258  return valid;
259  }
260 };
261 #endif
262 
263 class QoreTimeZoneManager {
264 protected:
265  // read-write lock to manage real (non-offset) zone info objects
266  mutable QoreRWLock rwl;
267 
268  // read-write lock to guard access to offset custom zone info objects
269  mutable QoreRWLock rwl_offset;
270 
271  // time zone info map (ex: "Europe/Prague" -> QoreZoneInfo*)
272  typedef std::map<std::string, AbstractQoreZoneInfo *> tzmap_t;
273 
274  // offset map type
275  typedef std::map<int, QoreOffsetZoneInfo *> tzomap_t;
276 
277  unsigned tzsize;
278 
279  // our utc offset in seconds east of UTC
280  int our_utcoffset;
281 
282  QoreString root;
283  tzmap_t tzmap;
284 
285  // standard (unlocked) zone offset map
286  tzomap_t tzo_std_map;
287 
288  // custom (locked) zone offset map
289  tzomap_t tzomap;
290 
291  // pointer to our regional time information
292  AbstractQoreZoneInfo* localtz;
293  std::string localtzname;
294 
295  DLLLOCAL int processIntern(const char *fn, ExceptionSink *xsink);
296  DLLLOCAL int process(const char *fn);
297 
298  DLLLOCAL const AbstractQoreZoneInfo *processFile(const char *fn, bool use_path, ExceptionSink *xsink);
299  DLLLOCAL int processDir(const char *d, ExceptionSink *xsink);
300 
301  // to set the local time zone information from a file
302  DLLLOCAL int setLocalTZ(std::string fname);
303 
304  DLLLOCAL int setLocalTZ(std::string fname, AbstractQoreZoneInfo *tzi);
305 
306  DLLLOCAL void setFromLocalTimeFile();
307 
308  DLLLOCAL void init_intern(QoreString &TZ);
309 
310 public:
311  DLLLOCAL QoreTimeZoneManager();
312 
313  DLLLOCAL ~QoreTimeZoneManager() {
314  for (tzmap_t::iterator i = tzmap.begin(), e = tzmap.end(); i != e; ++i) {
315  delete i->second;
316  }
317 
318  for (tzomap_t::iterator i = tzo_std_map.begin(), e = tzo_std_map.end(); i != e; ++i) {
319  //printd(0, "QoreTimeZoneManager::~QoreTimeZoneManager() deleting %d: %s\n", i->first, i->second->getRegionName());
320  delete i->second;
321  }
322 
323  for (tzomap_t::iterator i = tzomap.begin(), e = tzomap.end(); i != e; ++i) {
324  delete i->second;
325  }
326  }
327 
328  DLLLOCAL AbstractQoreZoneInfo* getZone(const char *name) {
329  QoreAutoRWReadLocker al(rwl);
330  tzmap_t::iterator i = tzmap.find(name);
331  return i == tzmap.end() ? 0 : i->second;
332  }
333 
334  DLLLOCAL const QoreOffsetZoneInfo* findCreateOffsetZone(int seconds_east);
335  DLLLOCAL const QoreOffsetZoneInfo* findCreateOffsetZone(const char *offset, ExceptionSink *xsink = 0);
336 
337  DLLLOCAL int readAll(ExceptionSink *xsink);
338 
339  DLLLOCAL void init();
340 
341  DLLLOCAL const AbstractQoreZoneInfo* getLocalZoneInfo() const {
342  return localtz;
343  }
344 
345  DLLLOCAL const char* getLocalRegion() const {
346  return localtzname.empty() ? nullptr : localtzname.c_str();
347  }
348 
349  DLLLOCAL const AbstractQoreZoneInfo* findLoadRegion(const char* name, ExceptionSink* xsink);
350  DLLLOCAL const AbstractQoreZoneInfo* findLoadRegionFromPath(const char* name, ExceptionSink* xsink);
351 };
352 
353 DLLLOCAL extern QoreTimeZoneManager QTZM;
354 
355 #endif
DLLEXPORT const AbstractQoreZoneInfo * findCreateOffsetZone(int seconds_east)
returns a time zone for the given time zone UTC offset
container for holding Qore-language exception information and also for registering a "thread_exit" ca...
Definition: ExceptionSink.h:48
provides a safe and exception-safe way to hold read locks in Qore, only to be used on the stack,...
Definition: QoreRWLock.h:105
provides a simple POSIX-threads-based read-write lock
Definition: QoreRWLock.h:47
Qore's string type supported by the QoreEncoding class.
Definition: QoreString.h:93
long long int64
64bit integer type, cannot use int64_t here since it breaks the API on some 64-bit systems due to equ...
Definition: common.h:260