aboutsummaryrefslogtreecommitdiff
path: root/src/common/Time
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2023-11-21 12:25:22 +0100
committerShauren <shauren.trinity@gmail.com>2023-11-21 12:25:22 +0100
commitb888b1b09f71a8b8b4a9d45c804a1f164fb65ac3 (patch)
treeba507c4c1c5e8487bd223afbde44ecf9eeac162e /src/common/Time
parent8c072b93af3a4efcbde21cc85fedcf7fa48fa7b4 (diff)
Core/Calendar: Implement different timezone support for ingame calendar
Closes #8390 Closes #29427
Diffstat (limited to 'src/common/Time')
-rw-r--r--src/common/Time/Timer.h190
-rw-r--r--src/common/Time/Timezone.cpp179
-rw-r--r--src/common/Time/Timezone.h37
3 files changed, 406 insertions, 0 deletions
diff --git a/src/common/Time/Timer.h b/src/common/Time/Timer.h
new file mode 100644
index 00000000000..e0ecb5b2d13
--- /dev/null
+++ b/src/common/Time/Timer.h
@@ -0,0 +1,190 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITY_TIMER_H
+#define TRINITY_TIMER_H
+
+#include "Define.h"
+#include "Duration.h"
+
+inline TimePoint GetApplicationStartTime()
+{
+ using namespace std::chrono;
+
+ static const steady_clock::time_point ApplicationStartTime = steady_clock::now();
+
+ return ApplicationStartTime;
+}
+
+inline uint32 getMSTime()
+{
+ using namespace std::chrono;
+
+ return uint32(duration_cast<milliseconds>(steady_clock::now() - GetApplicationStartTime()).count());
+}
+
+inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
+{
+ // getMSTime() have limited data range and this is case when it overflow in this tick
+ if (oldMSTime > newMSTime)
+ return (0xFFFFFFFF - oldMSTime) + newMSTime;
+ else
+ return newMSTime - oldMSTime;
+}
+
+inline uint32 getMSTimeDiff(uint32 oldMSTime, TimePoint newTime)
+{
+ using namespace std::chrono;
+
+ uint32 newMSTime = uint32(duration_cast<milliseconds>(newTime - GetApplicationStartTime()).count());
+ return getMSTimeDiff(oldMSTime, newMSTime);
+}
+
+inline uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
+{
+ return getMSTimeDiff(oldMSTime, getMSTime());
+}
+
+struct IntervalTimer
+{
+public:
+
+ IntervalTimer()
+ : _interval(0), _current(0)
+ {
+ }
+
+ void Update(time_t diff)
+ {
+ _current += diff;
+ if (_current < 0)
+ _current = 0;
+ }
+
+ bool Passed()
+ {
+ return _current >= _interval;
+ }
+
+ void Reset()
+ {
+ if (_current >= _interval)
+ _current %= _interval;
+ }
+
+ void SetCurrent(time_t current)
+ {
+ _current = current;
+ }
+
+ void SetInterval(time_t interval)
+ {
+ _interval = interval;
+ }
+
+ time_t GetInterval() const
+ {
+ return _interval;
+ }
+
+ time_t GetCurrent() const
+ {
+ return _current;
+ }
+
+private:
+
+ time_t _interval;
+ time_t _current;
+};
+
+struct TimeTracker
+{
+public:
+ TimeTracker(int32 expiry = 0) : _expiryTime(expiry) { }
+ TimeTracker(Milliseconds expiry) : _expiryTime(expiry) { }
+
+ void Update(int32 diff)
+ {
+ Update(Milliseconds(diff));
+ }
+
+ void Update(Milliseconds diff)
+ {
+ _expiryTime -= diff;
+ }
+
+ bool Passed() const
+ {
+ return _expiryTime <= 0s;
+ }
+
+ void Reset(int32 expiry)
+ {
+ Reset(Milliseconds(expiry));
+ }
+
+ void Reset(Milliseconds expiry)
+ {
+ _expiryTime = expiry;
+ }
+
+ Milliseconds GetExpiry() const
+ {
+ return _expiryTime;
+ }
+
+private:
+ Milliseconds _expiryTime;
+};
+
+struct PeriodicTimer
+{
+public:
+
+ PeriodicTimer(int32 period, int32 start_time)
+ : i_period(period), i_expireTime(start_time)
+ {
+ }
+
+ bool Update(const uint32 diff)
+ {
+ if ((i_expireTime -= diff) > 0)
+ return false;
+
+ i_expireTime += i_period > int32(diff) ? i_period : diff;
+ return true;
+ }
+
+ void SetPeriodic(int32 period, int32 start_time)
+ {
+ i_expireTime = start_time;
+ i_period = period;
+ }
+
+ // Tracker interface
+ void TUpdate(int32 diff) { i_expireTime -= diff; }
+ bool TPassed() const { return i_expireTime <= 0; }
+ void TReset(int32 diff, int32 period) { i_expireTime += period > diff ? period : diff; }
+
+private:
+
+ int32 i_period;
+ int32 i_expireTime;
+};
+
+#endif
diff --git a/src/common/Time/Timezone.cpp b/src/common/Time/Timezone.cpp
new file mode 100644
index 00000000000..f7acfe5df5c
--- /dev/null
+++ b/src/common/Time/Timezone.cpp
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Timezone.h"
+#include "Hash.h"
+#include "MapUtils.h"
+#include "Util.h"
+#include <boost/locale/date_time_facet.hpp>
+#include <boost/locale/generator.hpp>
+#include <chrono>
+#include <unordered_map>
+
+namespace
+{
+std::unordered_map<uint32, Minutes, std::identity> InitTimezoneHashDb()
+{
+#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
+
+ // Generate our hash db to match values sent in client authentication packets
+ std::unordered_map<uint32, Minutes, std::identity> hashToOffset;
+ std::chrono::system_clock::time_point dummmy;
+ for (std::chrono::time_zone const& zone : std::chrono::get_tzdb().zones)
+ {
+ std::chrono::sys_info sysInfo = zone.get_info(dummmy);
+ Minutes offsetMinutes = std::chrono::duration_cast<Minutes>(sysInfo.offset);
+ std::string offsetStr = Trinity::ToString(offsetMinutes.count());
+ hashToOffset.emplace(Trinity::HashFnv1a(offsetStr), offsetMinutes);
+ }
+
+#else
+ // Pre-generated list of timezone offsets and their hashes for compilers (and their stl implementations) that dont support timezone api yet
+ std::unordered_map<uint32, Minutes, std::identity> hashToOffset =
+ {
+ { 0xAADC2D37u, -720min },
+ { 0x362F107Bu, -690min },
+ { 0x2C44C70Cu, -660min },
+ { 0xB84A209Eu, -640min },
+ { 0xBA3D57D1u, -630min },
+ { 0x4040695Au, -600min },
+ { 0xB65A75D0u, -570min },
+ { 0xC8614DEBu, -540min },
+ { 0x3A68BD26u, -510min },
+ { 0x51E8096Cu, -480min },
+ { 0x4DD8F896u, -420min },
+ { 0x674B7C0Fu, -360min },
+ { 0x633C6B39u, -300min },
+ { 0x0BAD340Au, -240min },
+ { 0x74B25683u, -225min },
+ { 0x09B9FCD7u, -210min },
+ { 0x150C169Bu, -180min },
+ { 0x191B2771u, -120min },
+ { 0xD7D3B14Eu, -60min },
+ { 0x47CE5170u, -44min },
+ { 0x350CA8AFu, 0min },
+ { 0x15E8E23Bu, 60min },
+ { 0x733864AEu, 120min },
+ { 0xF71F9C94u, 180min },
+ { 0xBDE50F54u, 210min },
+ { 0x2BDD6DB9u, 240min },
+ { 0xB1E07F42u, 270min },
+ { 0x454FF132u, 300min },
+ { 0x3F4DA929u, 330min },
+ { 0xD1554AC4u, 360min },
+ { 0xBB667143u, 390min },
+ { 0x9E2B78C9u, 420min },
+ { 0x1C377816u, 450min },
+ { 0x1A4440E3u, 480min },
+ { 0xB49DF789u, 525min },
+ { 0xC3A28C54u, 540min },
+ { 0x35A9FB8Fu, 570min },
+ { 0x889BD751u, 600min },
+ { 0x8CAAE827u, 660min },
+ { 0x7285EE60u, 690min },
+ { 0x1CC2DEF4u, 720min },
+ { 0x89B8FD2Fu, 765min },
+ { 0x98DBA70Eu, 780min },
+ { 0xC59585BBu, 840min }
+ };
+#endif
+
+ return hashToOffset;
+}
+
+std::unordered_map<uint32, Minutes, std::identity> const _timezoneOffsetsByHash = InitTimezoneHashDb();
+
+using ClientSupportedTimezone = std::pair<Minutes, std::string>;
+std::array<ClientSupportedTimezone, 11> const _clientSupportedTimezones =
+{{
+ { -480min, "America/Los_Angeles" },
+ { -420min, "America/Denver" },
+ { -360min, "America/Chicago" },
+ { -300min, "America/New_York" },
+ { -180min, "America/Sao_Paulo" },
+ { 0min, "Etc/UTC" },
+ { 60min, "Europe/Paris" },
+ { 480min, "Asia/Shanghai" },
+ { 480min, "Asia/Taipei" },
+ { 540min, "Asia/Seoul" },
+ { 600min, "Australia/Melbourne" },
+}};
+}
+
+namespace Trinity::Timezone
+{
+Minutes GetOffsetByHash(uint32 hash)
+{
+ if (Minutes const* offset = Containers::MapGetValuePtr(_timezoneOffsetsByHash, hash))
+ return *offset;
+
+ return 0min;
+}
+
+Minutes GetSystemZoneOffsetAt(SystemTimePoint date)
+{
+ Seconds offset;
+#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
+ offset = std::chrono::current_zone()->get_info(date).offset;
+#else
+ tm buf = TimeBreakdown(std::chrono::system_clock::to_time_t(date));
+ offset = Seconds(buf.tm_gmtoff);
+#endif
+ return std::chrono::duration_cast<Minutes>(offset);
+}
+
+Minutes GetSystemZoneOffset(bool applyDst /*= true*/)
+{
+ std::chrono::system_clock::time_point date = std::chrono::system_clock::from_time_t(std::time_t(0));
+ if (applyDst)
+ date = std::chrono::system_clock::now();
+
+ return GetSystemZoneOffsetAt(date);
+}
+
+std::string GetSystemZoneName()
+{
+#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
+ return std::string(std::chrono::current_zone()->name());
+#else
+ static std::locale calendarLocale = boost::locale::generator().generate("");
+ std::unique_ptr<boost::locale::abstract_calendar> p(std::use_facet<class boost::locale::calendar_facet>(calendarLocale).create_calendar());
+ return p->get_timezone();
+#endif
+}
+
+std::string_view FindClosestClientSupportedTimezone(std::string_view currentTimezone, Minutes currentTimezoneOffset)
+{
+ // try exact match
+ auto itr = std::find_if(_clientSupportedTimezones.begin(), _clientSupportedTimezones.end(), [currentTimezone](ClientSupportedTimezone const& tz)
+ {
+ return tz.second == currentTimezone;
+ });
+ if (itr != _clientSupportedTimezones.end())
+ return itr->second;
+
+ // try closest offset
+ itr = std::min_element(_clientSupportedTimezones.begin(), _clientSupportedTimezones.end(), [currentTimezoneOffset](ClientSupportedTimezone const& left, ClientSupportedTimezone const& right)
+ {
+ Minutes leftDiff = left.first - currentTimezoneOffset;
+ Minutes rightDiff = right.first - currentTimezoneOffset;
+ return std::abs(leftDiff.count()) < std::abs(rightDiff.count());
+ });
+
+ return itr->second;
+}
+}
diff --git a/src/common/Time/Timezone.h b/src/common/Time/Timezone.h
new file mode 100644
index 00000000000..769bf0c5921
--- /dev/null
+++ b/src/common/Time/Timezone.h
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITYCORE_TIMEZONE_H
+#define TRINITYCORE_TIMEZONE_H
+
+#include "Define.h"
+#include "Duration.h"
+
+namespace Trinity::Timezone
+{
+TC_COMMON_API Minutes GetOffsetByHash(uint32 hash);
+
+// Returns the time offset that must be added to UTC time to get localtime
+TC_COMMON_API Minutes GetSystemZoneOffsetAt(SystemTimePoint date);
+TC_COMMON_API Minutes GetSystemZoneOffset(bool applyDst = true);
+
+TC_COMMON_API std::string GetSystemZoneName();
+
+TC_COMMON_API std::string_view FindClosestClientSupportedTimezone(std::string_view currentTimezone, Minutes currentTimezoneOffset);
+}
+
+#endif // TRINITYCORE_TIMEZONE_H