diff options
author | Shauren <shauren.trinity@gmail.com> | 2023-11-21 12:25:22 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2023-11-21 12:25:22 +0100 |
commit | b888b1b09f71a8b8b4a9d45c804a1f164fb65ac3 (patch) | |
tree | ba507c4c1c5e8487bd223afbde44ecf9eeac162e /src/common/Time | |
parent | 8c072b93af3a4efcbde21cc85fedcf7fa48fa7b4 (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.h | 190 | ||||
-rw-r--r-- | src/common/Time/Timezone.cpp | 179 | ||||
-rw-r--r-- | src/common/Time/Timezone.h | 37 |
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 |