Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
Timezone.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Time_Timezone_h_
5#define _Stroika_Foundation_Time_Timezone_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <compare>
10#include <ctime>
11#include <optional>
12
14#include "Stroika/Foundation/DataExchange/ValidationStrategy.h"
16
17/**
18 * \file
19 *
20 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
21 *
22 * TODO:
23 * @todo See https://en.cppreference.com/w/cpp/chrono/time_zone - and support this. But the C++20 doesn't REPLACE
24 * the Stroika timezone - just an extra thing a Stroika timezone can reference (may replace stroika tz database).
25 * But Stroika tz allows representing explicit stuff like +04:00 etc, which cannot be represented by time_zone
26 * class. --LGP 2022-11-07
27 *
28 * @todo http://stroika-bugs.sophists.com/browse/STK-636
29 * Enhance Timezone class to support real timezones (e.g. American/NewYork) and handle all the cases
30 * of mapping offsets (applying all teh rules for different dates)
31 *
32 * Could use boost or http://www.iana.org/time-zones/repository/releases/tzdata2015b.tar.gz or other.
33 *
34 * But good news - I think I have API right now. Just construct a Timezone object with enum or data read from file
35 * and can be used as is throughout rest of DateTime code (well, TimeZoneInformationType probably needs changes).
36 *
37 * REVIEW C++20 support here - I think they added support for named timezone map to offsets.
38 */
39
40namespace Stroika::Foundation::Time {
41
42 class Date;
43 class DateTime;
44 class TimeOfDay;
45
46 /**
47 * Information about the current system timezone setting.
48 *
49 * A timezone contains a name for BOTH 'standard time' - most of the year when you are in the normal
50 * time range - and 'daylight savings time' - a period of the year when you have an alternate timezone name
51 * and offset.
52 *
53 * @see http://en.wikipedia.org/wiki/Tz_database
54 * @see http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
55 * @see http://www.iana.org/time-zones
56 * @see http://www.iana.org/time-zones/repository/releases/tzdata2015b.tar.gz (this appears to be where the zones are defined)
57 *
58 * e.g:
59 * ID STD ABBR STD NAME DST ABBR DST NAME GMT offset DST adjustment DST Start Date rule Start time DST End date rule End time
60 *
61 * America/New_York EST Eastern Standard Time EDT Eastern Daylight Time -05:00:00 +01:00:00 2;0;3 +02:00:00 1;0;11 +02:00:00
62 *
63 * @see http://stroika-bugs.sophists.com/browse/STK-636
64 */
65 struct [[nodiscard]] TimeZoneInformationType {
66 struct Details {
67 optional<Characters::String> fName;
68 optional<Characters::String> fAbbreviation;
69 optional<int> fBiasInMinutesFromUTC; // UTC + bias => local time, so for example, EDT this is -300 (aka -5 hrs)
70 };
71 Details fStandardTime;
72 Details fDaylightSavingsTime;
73
74 /**
75 * This is the 'Olsen database name' (e..g. 'America/New_York').
76 *
77 * It is OFTEN not available (on older OSes).
78 */
79 optional<Characters::String> fID;
80 };
81
82 /**
83 * \brief return a summary of information about the currently configured default timezone on this system.
84 */
86
87 /**
88 * The Timezone class represents what one would think of as a timezone - mostly.
89 *
90 * But - it only supports these timezones:
91 * o UTC
92 * o LocalTime () special - whatever timezone this computer is in
93 * o Fixed Offset from UTC (suitable for reading and writing ISO8601 times)
94 *
95 * But it currently does NOT support the notion of timezone like American/NewYork (unless that happens to be localtime)
96 * That feature may be eventually added - http://stroika-bugs.sophists.com/browse/STK-636
97 *
98 * @see https://msdn.microsoft.com/en-us/library/system.timezone(v=vs.110).aspx
99 *
100 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
101 * static_assert (totally_ordered<Timezone>);
102 * o comparison of < not 100% intuitive, but unambiguous, so these can be compared (by numeric value of flags/offsets).
103 */
104 class [[nodiscard]] Timezone {
105 private:
106 enum class TZ_ : uint16_t {
107 eLocalTime,
108 eUTC,
109 eFixedOffsetBias
110 };
111 constexpr Timezone (TZ_ tz) noexcept;
112
113 public:
114 using BiasInMinutesFromUTCType = int16_t;
115
116 public:
117 /**
118 * According to https://en.wikipedia.org/wiki/List_of_UTC_time_offsets, these actually vary from -12, to 14. But logically, the only thing really crazy would be > 24 or < -24 hours.
119 */
120 static constexpr Traversal::Range<BiasInMinutesFromUTCType> kBiasInMinutesFromUTCTypeValidRange{-24 * 60, 24 * 60};
121
122 public:
123 /**
124 * if ValidationStrategy is eAssert (DEFAULT if not specified)
125 * \pre kBiasInMinutesFromUTCTypeValidRange.Contains (biasInMinutesFromUTC)
126 * else throws if out of range.
127 */
128 Timezone () = delete;
129 constexpr explicit Timezone (BiasInMinutesFromUTCType biasInMinutesFromUTC) noexcept;
130 constexpr Timezone (BiasInMinutesFromUTCType biasInMinutesFromUTC, DataExchange::ValidationStrategy validationStrategy);
131 constexpr Timezone (const Timezone& src) = default;
132 constexpr Timezone (Timezone&& src) noexcept = default;
133
134 public:
135 /**
136 */
137 nonvirtual Timezone& operator= (Timezone&& rhs) noexcept = default;
138 nonvirtual Timezone& operator= (const Timezone& rhs) = default;
139
140 public:
141 /**
142 * Returns Timezone object in UTC timezone.
143 */
144 static const Timezone kUTC; // defined constexpr
145
146 public:
147 /**
148 * Returns Timezone object in localtime timezone.
149 *
150 * \note - LocalTime is a STICKY property. This does NOT return the FIXED OFFSET for the current local-time, but rather a special
151 * Timezone which always references that global current timezone.
152 */
153 static const Timezone kLocalTime; // defined constexpr
154
155 public:
156 /**
157 * Returns Timezone object in localtime timezone.
158 */
159 static const optional<Timezone> kUnknown; // defined constexpr
160
161 public:
162 /**
163 * Parse string of the form:
164 * [+-]?HHMM, or [+-]?HH:MM, so for example -0500 is Timezone (-5*60), or -04:00 would be Timezone (-4*60).
165 *
166 * On empty string, return nullopt, but on ill-formed timezone offset string (including out of range),
167 * throw (even if the string is EST, or some such - this requires numeric offset).
168 *
169 * This parse function ignores any bad data at the end of tzStr (perhaps future version will be optionally more picky).
170 */
171 static optional<Timezone> ParseTimezoneOffsetString (const char* tzStr);
172 static optional<Timezone> ParseTimezoneOffsetString (const wchar_t* tzStr);
173 static optional<Timezone> ParseTimezoneOffsetString (const Characters::String& tzStr);
174
175 public:
176 /**
177 * generate a string of the form:
178 * [+-]?HHMM, or [+-]?HH:MM, so for example -0500 is Timezone (-5*60), or -04:00 would be Timezone (-4*60).
179 *
180 * \note Date/Time required in case Timezone is 'localtime' to determine DST
181 */
182 nonvirtual String AsHHMM (const Date& date, const TimeOfDay& tod, bool insertColon) const;
183
184 public:
185 /**
186 * @see https://tools.ietf.org/html/rfc822#section-5
187 *
188 * Can generate "GMT" or AsHMM(false) above.
189 *
190 * \note Date/Time required in case Timezone is 'localtime' to determine DST
191 *
192 * @aliases AsRFC822 ()
193 */
194 nonvirtual String AsRFC1123 (const Date& date, const TimeOfDay& tod) const;
195
196 public:
197 /**
198 * Depending on the form of the timezone, the offset from UTC could depend on the date (cuz of daylight savings time)
199 *
200 * This offset (number of minutes) - is added to a UTC time to get the time in that local timezone.
201 *
202 * \post (kBiasInMinutesFromUTCTypeValidRange.Contains (fBiasInMinutesFromUTC_));
203 */
204 nonvirtual BiasInMinutesFromUTCType GetBiasInMinutesFromUTC (const Date& date, const TimeOfDay& tod) const;
205
206 public:
207 /**
208 * Depending on the form of the timezone, the offset from UTC could depend on the date (cuz of daylight savings time)
209 *
210 * This offset (number of seconds) - is added to a UTC time to get the time in that local timezone.
211 */
212 nonvirtual make_signed_t<time_t> GetBiasFromUTC (const Date& date, const TimeOfDay& tod) const;
213
214 public:
215 /**
216 * For some kinds of timezones, there is no way to know (e.g. +4:00), but return true if known true, and false if known false.
217 */
218 nonvirtual optional<bool> IsDaylightSavingsTime (const Date& date, const optional<TimeOfDay>& tod);
219
220 public:
221 /**
222 */
223 nonvirtual constexpr auto operator<=> (const Timezone& rhs) const = default;
224
225 public:
226 /**
227 * @see Characters::ToString ();
228 */
229 nonvirtual Characters::String ToString () const;
230
231 private:
232 TZ_ fTZ_ : 16;
233 BiasInMinutesFromUTCType fBiasInMinutesFromUTC_ : 16;
234 };
235 namespace {
236 struct _HACK_2_TEST_4_static_assert_ {
237 uint32_t a;
238 };
239 }
240 static_assert (sizeof (Timezone) <= sizeof (_HACK_2_TEST_4_static_assert_),
241 "Timezone can/should be packed as much as practical since we could use a single uint16_ probably"); // make sure full struct as small as possible
242 static_assert (totally_ordered<Timezone>);
243
244}
245
246/*
247 ********************************************************************************
248 ***************************** Implementation Details ***************************
249 ********************************************************************************
250 */
251#include "Timezone.inl"
252
253#endif /*_Stroika_Foundation_Time_Timezone_h_*/
TimeZoneInformationType GetCurrentLocaleTimezoneInfo()
return a summary of information about the currently configured default timezone on this system.
Definition Timezone.cpp:214
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
static const optional< Timezone > kUnknown
Definition Timezone.h:159
static const Timezone kUTC
Definition Timezone.h:144
static const Timezone kLocalTime
Definition Timezone.h:153