Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Timezone.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <cstdlib>
7#include <cstring>
8#include <ctime>
9
10#if qStroika_Foundation_Common_Platform_POSIX
11#include <time.h>
12#endif
13
16#include "Stroika/Foundation/Common/Common.h"
17#include "Stroika/Foundation/Containers/Mapping.h"
21#include "Stroika/Foundation/Execution/ProcessRunner.h"
24
25#include "DateTime.h"
26
27#include "Timezone.h"
28
29using namespace Stroika::Foundation;
31using namespace Stroika::Foundation::Time;
32
33// Comment this in to turn on aggressive noisy DbgTrace in this module
34//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
35
36namespace {
37 tm Date2TM_ (const Date& d, const optional<TimeOfDay>& tod)
38 {
39 struct tm tm{};
40 tm.tm_year = static_cast<int> (d.GetYear ()) - 1900;
41 tm.tm_mon = static_cast<unsigned int> (d.GetMonth ()) - 1;
42 tm.tm_mday = static_cast<unsigned int> (d.GetDayOfMonth ());
43 unsigned int totalSecondsRemaining = tod.has_value () ? tod->GetAsSecondsCount () : 0;
44 tm.tm_hour = totalSecondsRemaining / (60 * 60);
45 totalSecondsRemaining -= tm.tm_hour * 60 * 60;
46 Assert (0 <= totalSecondsRemaining and totalSecondsRemaining < 60 * 60); // cuz would have gone into hours
47 tm.tm_min = totalSecondsRemaining / 60;
48 totalSecondsRemaining -= tm.tm_min * 60;
49 Assert (0 <= totalSecondsRemaining and totalSecondsRemaining < 60); // cuz would have gone into minutes
50 tm.tm_sec = totalSecondsRemaining;
51 tm.tm_isdst = -1;
52 Ensure (0 <= tm.tm_hour and tm.tm_hour <= 23);
53 Ensure (0 <= tm.tm_min and tm.tm_min <= 59);
54 Ensure (0 <= tm.tm_sec and tm.tm_sec <= 59);
55 return tm;
56 }
57}
58
59namespace {
60 bool IsDaylightSavingsTime_ (const Date& date, const optional<TimeOfDay>& tod);
61 bool IsDaylightSavingsTime_ (const DateTime& d);
62
63 /**
64 * Return the number of seconds which must be added to a LocalTime value to get GMT.
65 */
66 time_t GetLocaltimeToGMTOffset_ (bool applyDST);
67 time_t GetLocaltimeToGMTOffset_ (const DateTime& forTime);
68}
69
70/*
71 ********************************************************************************
72 ********************************* Time::Timezone *******************************
73 ********************************************************************************
74 */
75optional<Timezone> Timezone::ParseTimezoneOffsetString (const char* tzStr)
76{
77 RequireNotNull (tzStr);
78 if (*tzStr == '\0') {
79 return nullopt;
80 }
81 else {
82 int tzHr = 0;
83 int tzMn = 0;
84 const char* tStrPtr = tzStr;
85 bool isNeg = (*tStrPtr == '-');
86 if (*tStrPtr == '+' or *tStrPtr == '-') {
87 ++tStrPtr;
88 }
89 DISABLE_COMPILER_MSC_WARNING_START (4996) // MSVC SILLY WARNING ABOUT USING sscanf_s
90 int nTZItems = ::sscanf (tStrPtr, "%2d%2d", &tzHr, &tzMn);
91 if (nTZItems == 1) {
92 nTZItems = ::sscanf (tStrPtr, "%2d:%2d", &tzHr, &tzMn);
93 }
94 DISABLE_COMPILER_MSC_WARNING_END (4996)
95 if (nTZItems == 2) {
96 int16_t n = (isNeg ? -1 : 1) * static_cast<int16_t> (tzHr * 60 + tzMn);
98 }
99 static const Execution::RuntimeErrorException kException_{"invalid timezone offset"sv};
100 Execution::Throw (kException_);
101 }
102}
103
104optional<Timezone> Timezone::ParseTimezoneOffsetString (const wchar_t* tzStr)
105{
106 RequireNotNull (tzStr);
107 if (*tzStr == '\0') {
108 return nullopt;
109 }
110 else {
111 int tzHr = 0;
112 int tzMn = 0;
113 const wchar_t* tStrPtr = tzStr;
114 bool isNeg = (*tStrPtr == '-');
115 if (*tStrPtr == '+' or *tStrPtr == '-') {
116 ++tStrPtr;
117 }
118 DISABLE_COMPILER_MSC_WARNING_START (4996) // MSVC SILLY WARNING ABOUT USING swscanf_s
119 int nTZItems = ::swscanf (tStrPtr, L"%2d%2d", &tzHr, &tzMn);
120 if (nTZItems == 1) {
121 nTZItems = ::swscanf (tStrPtr, L"%2d:%2d", &tzHr, &tzMn);
122 }
123 DISABLE_COMPILER_MSC_WARNING_END (4996)
124 if (nTZItems == 2) {
125 int16_t n = (isNeg ? -1 : 1) * static_cast<int16_t> (tzHr * 60 + tzMn);
127 }
128 static const Execution::RuntimeErrorException kException_{"invalid timezone offset"sv};
129 Execution::Throw (kException_);
130 }
131}
132
133optional<Timezone> Timezone::ParseTimezoneOffsetString (const Characters::String& tzStr)
134{
135 return ParseTimezoneOffsetString (tzStr.As<wstring> ().c_str ());
136}
137
138String Timezone::AsHHMM (const Date& date, const TimeOfDay& tod, bool insertColon) const
139{
140 int minutes = GetBiasInMinutesFromUTC (date, tod);
141 bool isNeg = minutes < 0;
142 unsigned int nMinOffset = abs (fBiasInMinutesFromUTC_);
143 return Characters::Format (insertColon ? "{}{:02}:{:02}"_f : "{}{:02}{:02}"_f, isNeg ? L"-" : L"+", nMinOffset / 60, nMinOffset % 60);
144}
145
146String Timezone::AsRFC1123 (const Date& date, const TimeOfDay& tod) const
147{
148 int minutes = GetBiasInMinutesFromUTC (date, tod);
149 if (minutes == 0) {
150 static const String kUTC_ = "GMT"sv; // UT or GMT for UTC in https://tools.ietf.org/html/rfc822#section-5
151 return kUTC_;
152 }
153 return AsHHMM (date, tod, false);
154}
155
156Timezone::BiasInMinutesFromUTCType Timezone::GetBiasInMinutesFromUTC (const Date& date, const TimeOfDay& tod) const
157{
158 switch (fTZ_) {
159 case TZ_::eUTC:
160 return 0;
161 case TZ_::eFixedOffsetBias:
162 Ensure (kBiasInMinutesFromUTCTypeValidRange.Contains (fBiasInMinutesFromUTC_));
163 return fBiasInMinutesFromUTC_;
164 case TZ_::eLocalTime: {
165 auto result = static_cast<BiasInMinutesFromUTCType> (-GetLocaltimeToGMTOffset_ (IsDaylightSavingsTime_ (date, tod)) / 60);
167 return result;
168 }
169 default:
171 return 0;
172 }
173}
174
175optional<bool> Timezone::IsDaylightSavingsTime (const Date& date, const optional<TimeOfDay>& tod)
176{
177 // @todo - fix for other (not fixed) timezones - like America/NewYork
178 if (fTZ_ == TZ_::eLocalTime) {
179 return IsDaylightSavingsTime_ (date, tod);
180 }
181 else {
182 return {};
183 }
184}
185
187{
188 // This format is just comsetic and for information (debugging) purposes, and follows no standards
189 static const String kUTC_{"UTC"sv};
190 static const String kLocaltime_{"localtime"sv};
191 switch (fTZ_) {
192 case TZ_::eUTC:
193 return kUTC_;
194 case TZ_::eFixedOffsetBias: {
195 Ensure (kBiasInMinutesFromUTCTypeValidRange.Contains (fBiasInMinutesFromUTC_));
196 bool isNeg = fBiasInMinutesFromUTC_ < 0;
197 unsigned int nMinOffset = abs (fBiasInMinutesFromUTC_);
198 return "{}{:02}:{:02}"_f(isNeg ? L"-" : L"+", nMinOffset / 60, nMinOffset % 60);
199 } break;
200 case TZ_::eLocalTime: {
201 return kLocaltime_;
202 } break;
203 default:
205 return String{};
206 }
207}
208
209/*
210 ********************************************************************************
211 ********************** Time::GetCurrentLocaleTimezoneInfo **********************
212 ********************************************************************************
213 */
214TimeZoneInformationType Time::GetCurrentLocaleTimezoneInfo ()
215{
217#if qStroika_Foundation_Common_Platform_POSIX
218 try {
219 result.fID = Streams::BinaryToText::Reader::New (IO::FileSystem::FileInputStream::New ("/etc/timezone"sv)).ReadAll ().Trim ();
220 }
221 catch (...) {
222 DbgTrace ("Ignoring missing ID from /etc/timezone"_f);
223 }
224#if 0
225 //
226 // COULD look at /etc/localtime, but very hard to map this to olson db name
227 //
228 // One maybe close way is to see if /etc/localtime is a slink or exact copy of file in /usr/share/zoneinfo - that subdir/name
229 // is typically/often an Olson DB name.
230 //
231 if (not result.fID.has_value ()) {
232 // WEAK but maybe effective way
233 // http://www.linuxforums.org/forum/red-hat-fedora-linux/162483-changing-timezone-rhel-5-4-centos.html
234 try {
235 DataExchange::INI::Profile p = DataExchange::INI::Reader{}.ReadProfile (IO::FileSystem::FileInputStream::New ("/etc/sysconfig/clock"sv));
236 if (auto o = p.fUnnamedSection.fProperties.Lookup ("ZONE"sv)) {
237 result.fID = *o;
238 }
239 }
240 catch (...) {
241 DbgTrace ("Missing Zone ID from /etc/sysconfig/clock"_f);
242 }
243 }
244#endif
245#if 0
246 if (not result.fID.has_value ()) {
247 try {
248 // Not a good approach because this returns a zone abbreviation, which doesn't uniquely define a zone.
249 // For example, CDT could be Cocos Islands Time, or Central Daylight Time (North America) etc (see http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations)
250 String tzAbbrev = get<0> (Execution::ProcessRunner {"date +%Z"sv}.Run (String {})).Trim ();
253 // Table from Hand-coded for a few empirical cases around 2015-06-01
254 static const Mapping<String, String> kUNIXTZAbbrev2OlsonName_ = {
255 KeyValuePair<String, String> { "CST"sv, "America/Chicago"sv },
256 KeyValuePair<String, String> { "CDT"sv, "America/Chicago"sv },
257 KeyValuePair<String, String> { "CST6CDT"sv, "America/Chicago"sv },
258 KeyValuePair<String, String> { "EDT" sv, "America/New_York"sv },
259 KeyValuePair<String, String> { "EST"sv, "America/New_York"sv },
260 KeyValuePair<String, String> { "EST5EDT"sv, "America/New_York"sv },
261 KeyValuePair<String, String> { "PDT"sv, "America/Los_Angeles"sv },
262 KeyValuePair<String, String> { "PST"sv, "America/Los_Angeles"sv },
263 KeyValuePair<String, String> { "PDT8PST"sv, "America/Los_Angeles"sv },
264 };
265 result.fID = kUNIXTZAbbrev2OlsonName_.LookupValue (tzAbbrev, tzAbbrev);
266 }
267 catch (...) {
268 DbgTrace ("Ignoring missing ID from date +%Z");
269 }
270 }
271#endif
272 if (not result.fID.has_value ()) {
273 // We could look to see if /etc/localtime is a symlink or a copy of any named file from /usr/share/zoneinfo, but
274 // hope thats not needed!
275 }
276 // @see http://pubs.opengroup.org/onlinepubs/7908799/xsh/tzset.html
277 result.fStandardTime.fAbbreviation = String::FromNarrowSDKString (tzname[0]);
278 result.fStandardTime.fName = String::FromNarrowSDKString (tzname[0]);
279 result.fStandardTime.fBiasInMinutesFromUTC = -GetLocaltimeToGMTOffset_ (false) / 60;
280 result.fDaylightSavingsTime.fAbbreviation = String::FromNarrowSDKString (tzname[1]);
281 result.fDaylightSavingsTime.fName = String::FromNarrowSDKString (tzname[1]);
282 result.fDaylightSavingsTime.fBiasInMinutesFromUTC = -GetLocaltimeToGMTOffset_ (true) / 60;
283#elif qStroika_Foundation_Common_Platform_Windows
286 // Table from Stering around 2015-05-01
287 static const Mapping<String, String> kWinDoze2OlsonName_ = {
288 KeyValuePair<String, String>{"Afghanistan Standard Time"sv, "Asia/Kabul"sv},
289 KeyValuePair<String, String>{"Alaskan Standard Time"sv, "America/Juneau"sv},
290 KeyValuePair<String, String>{"Arab Standard Time"sv, "Asia/Riyadh"sv},
291 KeyValuePair<String, String>{"Arabian Standard Time"sv, "Asia/Muscat"sv},
292 KeyValuePair<String, String>{"Arabic Standard Time"sv, "Asia/Baghdad"sv},
293 KeyValuePair<String, String>{"Argentina Standard Time"sv, "America/Rosario"sv},
294 KeyValuePair<String, String>{"Atlantic Standard Time"sv, "America/Halifax"sv},
295 KeyValuePair<String, String>{"AUS Central Standard Time"sv, "Australia/Darwin"sv},
296 KeyValuePair<String, String>{"AUS Eastern Standard Time"sv, "Australia/Sydney"sv},
297 KeyValuePair<String, String>{"Azerbaijan Standard Time"sv, "Asia/Baku"sv},
298 KeyValuePair<String, String>{"Azores Standard Time"sv, "Atlantic/Azores"sv},
299 KeyValuePair<String, String>{"Bahia Standard Time"sv, "America/Maceio"sv},
300 KeyValuePair<String, String>{"Bangladesh Standard Time"sv, "Asia/Dhaka"sv},
301 KeyValuePair<String, String>{"Canada Central Standard Time"sv, "America/Swift_Current"sv},
302 KeyValuePair<String, String>{"Cape Verde Standard Time"sv, "Atlantic/Cape_Verde"sv},
303 KeyValuePair<String, String>{"Caucasus Standard Time"sv, "Asia/Yerevan"sv},
304 KeyValuePair<String, String>{"Cen. Australia Standard Time"sv, "Australia/Adelaide"sv},
305 KeyValuePair<String, String>{"Central America Standard Time"sv, "America/Tegucigalpa"sv},
306 KeyValuePair<String, String>{"Central Asia Standard Time"sv, "Asia/Almaty"sv},
307 KeyValuePair<String, String>{"Central Brazilian Standard Time"sv, "America/Cuiaba"sv},
308 KeyValuePair<String, String>{"Central Europe Standard Time"sv, "Europe/Prague"sv},
309 KeyValuePair<String, String>{"Central European Standard Time"sv, "Europe/Skopje"sv},
310 KeyValuePair<String, String>{"Central Pacific Standard Time"sv, "Pacific/Guadalcanal"sv},
311 KeyValuePair<String, String>{"Central Standard Time"sv, "America/Chicago"sv},
312 KeyValuePair<String, String>{"Central Standard Time (Mexico)"sv, "America/Monterrey"sv},
313 KeyValuePair<String, String>{"China Standard Time"sv, "Asia/Urumqi"sv},
314 KeyValuePair<String, String>{"E. Africa Standard Time"sv, "Africa/Nairobi"sv},
315 KeyValuePair<String, String>{"E. Australia Standard Time"sv, "Australia/Brisbane"sv},
316 KeyValuePair<String, String>{"E. Europe Standard Time"sv, "Europe/Bucharest"sv},
317 KeyValuePair<String, String>{"E. South America Standard Time"sv, "America/Sao_Paulo"sv},
318 KeyValuePair<String, String>{"Eastern Standard Time"sv, "America/New_York"sv},
319 KeyValuePair<String, String>{"Egypt Standard Time"sv, "Africa/Cairo"sv},
320 KeyValuePair<String, String>{"Ekaterinburg Standard Time"sv, "Asia/Yekaterinburg"sv},
321 KeyValuePair<String, String>{"Fiji Standard Time"sv, "Pacific/Fiji"sv},
322 KeyValuePair<String, String>{"FLE Standard Time"sv, "Europe/Helsinki"sv},
323 KeyValuePair<String, String>{"Georgian Standard Time"sv, "Asia/Tbilisi"sv},
324 KeyValuePair<String, String>{"GMT Standard Time"sv, "Europe/London"sv},
325 KeyValuePair<String, String>{"Greenland Standard Time"sv, "America/Godthab"sv},
326 KeyValuePair<String, String>{"Greenwich Standard Time"sv, "Atlantic/Reykjavik"sv},
327 KeyValuePair<String, String>{"GTB Standard Time"sv, "Europe/Bucharest"sv},
328 KeyValuePair<String, String>{"Hawaiian Standard Time"sv, "Pacific/Honolulu"sv},
329 KeyValuePair<String, String>{"India Standard Time"sv, "Asia/Calcutta"sv},
330 KeyValuePair<String, String>{"Iran Standard Time"sv, "Asia/Tehran"sv},
331 KeyValuePair<String, String>{"Jerusalem Standard Time"sv, "Asia/Jerusalem"sv},
332 KeyValuePair<String, String>{"Jordan Standard Time"sv, "Asia/Amman"sv},
333 KeyValuePair<String, String>{"Kaliningrad Standard Time"sv, "Europe/Kaliningrad"sv},
334 KeyValuePair<String, String>{"Korea Standard Time"sv, "Asia/Seoul"sv},
335 KeyValuePair<String, String>{"Libya Standard Time"sv, "Africa/Tripoli"sv},
336 KeyValuePair<String, String>{"Magadan Standard Time"sv, "Asia/Magadan"sv},
337 KeyValuePair<String, String>{"Mauritius Standard Time"sv, "Indian/Mauritius"sv},
338 KeyValuePair<String, String>{"Middle East Standard Time"sv, "Asia/Beirut"sv},
339 KeyValuePair<String, String>{"Montevideo Standard Time"sv, "America/Montevideo"sv},
340 KeyValuePair<String, String>{"Morocco Standard Time"sv, "Africa/Casablanca"sv},
341 KeyValuePair<String, String>{"Mountain Standard Time"sv, "America/Denver"sv},
342 KeyValuePair<String, String>{"Mountain Standard Time (Mexico)"sv, "America/Mazatlan"sv},
343 KeyValuePair<String, String>{"Myanmar Standard Time"sv, "Asia/Rangoon"sv},
344 KeyValuePair<String, String>{"N. Central Asia Standard Time"sv, "Asia/Novosibirsk"sv},
345 KeyValuePair<String, String>{"Namibia Standard Time"sv, "Africa/Windhoek"sv},
346 KeyValuePair<String, String>{"Nepal Standard Time"sv, "Asia/Katmandu"sv},
347 KeyValuePair<String, String>{"New Zealand Standard Time"sv, "Pacific/Auckland"sv},
348 KeyValuePair<String, String>{"Newfoundland Standard Time"sv, "America/St_Johns"sv},
349 KeyValuePair<String, String>{"North Asia East Standard Time"sv, "Asia/Irkutsk"sv},
350 KeyValuePair<String, String>{"North Asia Standard Time"sv, "Asia/Krasnoyarsk"sv},
351 KeyValuePair<String, String>{"Pacific SA Standard Time"sv, "America/Santiago"sv},
352 KeyValuePair<String, String>{"Pacific Standard Time"sv, "America/Los_Angeles"sv},
353 KeyValuePair<String, String>{"Pacific Standard Time (Mexico)"sv, "America/Tijuana"sv},
354 KeyValuePair<String, String>{"Pakistan Standard Time"sv, "Asia/Karachi"sv},
355 KeyValuePair<String, String>{"Paraguay Standard Time"sv, "America/Asuncion"sv},
356 KeyValuePair<String, String>{"Romance Standard Time"sv, "Europe/Paris"sv},
357 KeyValuePair<String, String>{"Russian Standard Time"sv, "Europe/Moscow"sv},
358 KeyValuePair<String, String>{"SA Eastern Standard Time"sv, "America/Cayenne"sv},
359 KeyValuePair<String, String>{"SA Pacific Standard Time"sv, "America/Lima"sv},
360 KeyValuePair<String, String>{"SA Western Standard Time"sv, "America/La_Paz"sv},
361 KeyValuePair<String, String>{"SE Asia Standard Time"sv, "Asia/Jakarta"sv},
362 KeyValuePair<String, String>{"Malay Peninsula Standard Time"sv, "Asia/Singapore"sv},
363 KeyValuePair<String, String>{"South Africa Standard Time"sv, "Africa/Harare"sv},
364 KeyValuePair<String, String>{"Syria Standard Time"sv, "Asia/Damascus"sv},
365 KeyValuePair<String, String>{"Taipei Standard Time"sv, "Asia/Taipei"sv},
366 KeyValuePair<String, String>{"Tasmania Standard Time"sv, "Australia/Hobart"sv},
367 KeyValuePair<String, String>{"Tokyo Standard Time"sv, "Asia/Tokyo"sv},
368 KeyValuePair<String, String>{"Tonga Standard Time"sv, "Pacific/Tongatapu"sv},
369 KeyValuePair<String, String>{"Turkey Standard Time"sv, "Asia/Istanbul"sv},
370 KeyValuePair<String, String>{"Ulaanbaatar Standard Time"sv, "Asia/Ulaanbaatar"sv},
371 KeyValuePair<String, String>{"US Eastern Standard Time"sv, "America/Indianapolis"sv},
372 KeyValuePair<String, String>{"US Mountain Standard Time"sv, "America/Denver"sv},
373 KeyValuePair<String, String>{"Venezuela Standard Time"sv, "America/Caracas"sv},
374 KeyValuePair<String, String>{"Vladivostok Standard Time"sv, "Asia/Vladivostok"sv},
375 KeyValuePair<String, String>{"W. Australia Standard Time"sv, "Australia/Perth"sv},
376 KeyValuePair<String, String>{"W. Central Africa Standard Time"sv, "Africa/Brazzaville"sv},
377 KeyValuePair<String, String>{"W. Europe Standard Time"sv, "Europe/Vienna"sv},
378 KeyValuePair<String, String>{"West Asia Standard Time"sv, "Asia/Tashkent"sv},
379 KeyValuePair<String, String>{"West Pacific Standard Time"sv, "Pacific/Port_Moresby"sv},
380 KeyValuePair<String, String>{"Yakutsk Standard Time"sv, "Asia/Yakutsk"sv},
381 };
382 TIME_ZONE_INFORMATION tzInfo{};
383 (void)::GetTimeZoneInformation (&tzInfo);
384 result.fStandardTime.fAbbreviation = String{tzInfo.StandardName};
385 result.fStandardTime.fName = String{tzInfo.StandardName};
386 result.fStandardTime.fBiasInMinutesFromUTC = -(tzInfo.StandardBias + tzInfo.Bias);
387 result.fDaylightSavingsTime.fAbbreviation = String{tzInfo.DaylightName};
388 result.fDaylightSavingsTime.fName = String{tzInfo.DaylightName};
389 result.fDaylightSavingsTime.fBiasInMinutesFromUTC = -(tzInfo.DaylightBias + tzInfo.Bias);
390 result.fID = kWinDoze2OlsonName_.LookupValue (tzInfo.StandardName, tzInfo.StandardName);
391#else
393#endif
394 return result;
395}
396
397/*
398 ********************************************************************************
399 **************************** IsDaylightSavingsTime_ ****************************
400 ********************************************************************************
401 */
402namespace {
403 bool IsDaylightSavingsTime_ (const Date& date, const optional<TimeOfDay>& tod)
404 {
405 struct tm asTM = Date2TM_ (date, tod);
406 /*
407 * From http://pubs.opengroup.org/onlinepubs/7908799/xsh/mktime.html:
408 *
409 * A positive or 0 value for tm_isdst causes mktime() to presume initially that Daylight Savings Time, respectively,
410 * is or is not in effect for the specified time. A negative value for tm_isdst causes mktime() to attempt to determine
411 * whether Daylight Saving Time is in effect for the specified time.
412 *
413 * Local timezone information is set as though mktime() called tzset().
414 *
415 * Upon successful completion, the values of the tm_wday and tm_yday components of the structure are set appropriately,
416 * and the other components are set to represent the specified time since the Epoch, but with their values forced to the
417 * ranges indicated in the <time.h> entry; the final value of tm_mday is not set until tm_mon and tm_year are determined.
418 *
419 * But http://linux.die.net/man/3/localtime says:
420 * The mktime() function modifies the fields of the tm structure as follows: tm_wday and tm_yday are set to values determined
421 * from the contents of the other fields; if structure members are outside their valid interval, they will be normalized
422 * (so that, for example, 40 October is changed into 9 November); tm_isdst is set (regardless of its initial value)
423 * to a positive value or to 0, respectively, to indicate whether DST is or is not in effect at the specified time.
424 *
425 * The POSIX part is not totally clear - but the linux docs do make it clear - that we can use this to test if is daylight savings time.
426 *
427 * APPEARS to work since... --LGP 2011-10-15
428 */
429 asTM.tm_isdst = -1; // force calc of correct daylight savings time flag
430 // http://stroika-bugs.sophists.com/browse/STK-515 only works back to 1970 (Unix epoch time) - else assume NOT daylight savings time
431 if (::mktime (&asTM) == -1) {
432 return false;
433 }
434 else {
435 return asTM.tm_isdst >= 1;
436 }
437 }
438
439 bool IsDaylightSavingsTime_ (const DateTime& d)
440 {
441 if (d.GetTimezone () == Timezone::kLocalTime) {
442 return IsDaylightSavingsTime_ (d.GetDate (), d.GetTimeOfDay ().has_value () ? *d.GetTimeOfDay () : TimeOfDay{0});
443 }
444 else {
445 AssertNotImplemented (); // maybe OK to just assume false given CURRENT (as of 2018-01-15) design of Timezone, but hope to expand soon!
446 return false;
447 }
448 }
449}
450
451/*
452 ********************************************************************************
453 ************************** GetLocaltimeToGMTOffset_ ****************************
454 ********************************************************************************
455 */
456namespace {
457 time_t GetLocaltimeToGMTOffset_ (bool applyDST)
458 {
459#if 0
460 // WRONG - but COULD use this API - but not sure needed
461#if qStroika_Foundation_Common_Platform_Windows
462 TIME_ZONE_INFORMATION tzInfo {};
463 (void)::GetTimeZoneInformation (&tzInfo);
464 int unsignedBias = abs (tzInfo.Bias);
465 int hrs = unsignedBias / 60;
466 int mins = unsignedBias - hrs * 60;
467 tzBiasString = ::Format (L"%s%.2d:%.2d", (tzInfo.Bias >= 0 ? L"-" : L"+"), hrs, mins);
468#endif
469#endif
470
471 /*
472 * COULD this be cached? It SHOULD be - but what about when the timezone changes? there maybe a better way to compute this using the
473 * timezone global var???
474 */
475 struct tm aTm{};
476 aTm.tm_year = 70;
477 aTm.tm_mon = 0; // Jan
478 aTm.tm_mday = 1;
479 constexpr bool kImplErrorUnderflow_{true}; // Only KNOWN to be needed on windows with TZ=America/New_York, but probably we should always do this
480 if (kImplErrorUnderflow_) {
481 ++aTm.tm_mday;
482 }
483 aTm.tm_isdst = applyDST;
484 time_t result = ::mktime (&aTm);
485 Assert (result != -1); // this shouldn't fail
486 if (kImplErrorUnderflow_) {
487 result -= 24 * 60 * 60;
488 }
489 Ensure (-60 * 60 * 24 <= result and result <= 60 * 60 * 24); // sanity check
490 return result;
491 }
492
493 time_t GetLocaltimeToGMTOffset_ (const DateTime& forTime)
494 {
495 return GetLocaltimeToGMTOffset_ (IsDaylightSavingsTime_ (forTime));
496 }
497}
#define AssertNotImplemented()
Definition Assertions.h:401
#define RequireNotNull(p)
Definition Assertions.h:347
#define AssertNotReached()
Definition Assertions.h:355
#define DbgTrace
Definition Trace.h:309
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
static String FromNarrowSDKString(const char *from)
Definition String.inl:470
nonvirtual String Trim(bool(*shouldBeTrimmed)(Character)=Character::IsWhitespace) const
Definition String.cpp:1592
Run the given command, and optionally support stdin/stdout/stderr as streams (either sync with Run,...
nonvirtual void Run(const Streams::InputStream::Ptr< byte > &in, const Streams::OutputStream::Ptr< byte > &out=nullptr, const Streams::OutputStream::Ptr< byte > &error=nullptr, ProgressMonitor::Updater progress=nullptr, Time::DurationSeconds timeout=Time::kInfinity)
Run the given external command/process (set by constructor) - with the given arguments,...
nonvirtual String ReadAll(size_t upTo=numeric_limits< size_t >::max()) const
nonvirtual BiasInMinutesFromUTCType GetBiasInMinutesFromUTC(const Date &date, const TimeOfDay &tod) const
Definition Timezone.cpp:156
static constexpr Traversal::Range< BiasInMinutesFromUTCType > kBiasInMinutesFromUTCTypeValidRange
Definition Timezone.h:120
nonvirtual optional< bool > IsDaylightSavingsTime(const Date &date, const optional< TimeOfDay > &tod)
Definition Timezone.cpp:175
nonvirtual String AsRFC1123(const Date &date, const TimeOfDay &tod) const
Definition Timezone.cpp:146
nonvirtual String AsHHMM(const Date &date, const TimeOfDay &tod, bool insertColon) const
Definition Timezone.cpp:138
nonvirtual Characters::String ToString() const
Definition Timezone.cpp:186
static const Timezone kLocalTime
Definition Timezone.h:153
static optional< Timezone > ParseTimezoneOffsetString(const char *tzStr)
Definition Timezone.cpp:75
constexpr bool Contains(Common::ArgByValueType< T > r) const
Definition Range.inl:193
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
Definition Throw.inl:43
Ptr New(const InputStream::Ptr< byte > &src, optional< AutomaticCodeCvtFlags > codeCvtFlags={}, optional< SeekableFlag > seekable={}, ReadAhead readAhead=eReadAheadAllowed)
Create an InputStream::Ptr<Character> from the arguments (usually binary source) - which can be used ...