From d2c5a3255ca77b59775a54ecb70fabc86335296a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 20:07:25 +0200 Subject: Adding debian version 2:20.4+dfsg-1. Signed-off-by: Daniel Baumann --- ...-Kodi-date-time-implementation-with-std-c.patch | 7004 ++++++++++++++++++++ 1 file changed, 7004 insertions(+) create mode 100644 debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch (limited to 'debian/patches/cdatetime-std-chrono') diff --git a/debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch b/debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch new file mode 100644 index 0000000..5c553eb --- /dev/null +++ b/debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch @@ -0,0 +1,7004 @@ +From c34f01771ab31da1450e0e8aec0dd4654ea5c557 Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Mon, 17 Feb 2020 22:41:25 -0800 +Subject: [PATCH 01/19] add support for date library and tzdata + +Signed-off-by: Vasyl Gello +--- + CMakeLists.txt | 6 + + cmake/modules/FindDate.cmake | 110 ++++++++++++++++++ + cmake/modules/FindTZData.cmake | 33 ++++++ + cmake/scripts/linux/Install.cmake | 7 ++ + .../date/0001-cmake-install-all-headers.patch | 74 ++++++++++++ + .../target/date/0002-Fix-Android-build.patch | 37 ++++++ + .../target/date/0003-Fix-UWP-build.patch | 58 +++++++++ + tools/depends/target/date/DATE-VERSION | 6 + + .../target/tzdata/001-cmakelists.patch | 11 ++ + tools/depends/target/tzdata/TZDATA-VERSION | 5 + + 10 files changed, 347 insertions(+) + create mode 100644 cmake/modules/FindDate.cmake + create mode 100644 cmake/modules/FindTZData.cmake + create mode 100644 tools/depends/target/date/0001-cmake-install-all-headers.patch + create mode 100644 tools/depends/target/date/0002-Fix-Android-build.patch + create mode 100644 tools/depends/target/date/0003-Fix-UWP-build.patch + create mode 100644 tools/depends/target/date/DATE-VERSION + create mode 100644 tools/depends/target/tzdata/001-cmakelists.patch + create mode 100644 tools/depends/target/tzdata/TZDATA-VERSION + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 19881e4708..55344f7626 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -85,6 +85,10 @@ if(UNIX) + endif() + # prefer kissfft from xbmc/contrib but let use system one on unices + cmake_dependent_option(ENABLE_INTERNAL_KISSFFT "Enable internal kissfft?" ON "UNIX" ON) ++# prefer internal date and tzdata but let use system one on unices ++cmake_dependent_option(ENABLE_INTERNAL_DATE "Enable internal date library?" ON "UNIX" ON) ++cmake_dependent_option(ENABLE_INTERNAL_TZDATA "Use Kodi bundled timezone information?" ON "NOT ENABLE_INTERNAL_DATE" ON) ++cmake_dependent_option(DATE_HAS_STRINGVIEW "Is system date library compiled with StringView support?" ON "NOT ENABLE_INTERNAL_DATE" ON) + # System options + if(NOT WIN32) + option(WITH_ARCH "build with given arch" OFF) +@@ -163,6 +167,7 @@ set(required_deps ASS + Cdio + CrossGUID + Curl ++ Date + FFMPEG + FlatBuffers + Fmt +@@ -181,6 +186,7 @@ set(required_deps ASS + Sqlite3 + TagLib + TinyXML ++ TZData + ZLIB + ${PLATFORM_REQUIRED_DEPS}) + +diff --git a/cmake/modules/FindDate.cmake b/cmake/modules/FindDate.cmake +new file mode 100644 +index 0000000000..61746e4d33 +--- /dev/null ++++ b/cmake/modules/FindDate.cmake +@@ -0,0 +1,110 @@ ++#.rst: ++# FindDate ++# ------- ++# Finds the DATE library and builds internal ++# DATE if requested ++# ++# This will define the following variables:: ++# ++# DATE_FOUND - system has DATE ++# DATE_INCLUDE_DIRS - the DATE include directory ++# DATE_LIBRARIES - the DATE libraries ++# DATE_DEFINITIONS - the DATE definitions ++# ++# and the following imported targets:: ++# ++# Date::Date - The Date library ++ ++if(ENABLE_INTERNAL_DATE) ++ include(cmake/scripts/common/ModuleHelpers.cmake) ++ ++ set(MODULE_LC date) ++ ++ SETUP_BUILD_VARS() ++ ++ set(DATE_VERSION ${${MODULE}_VER}) ++ ++ # Debug postfix only used for windows ++ if(WIN32 OR WINDOWS_STORE) ++ set(DATE_DEBUG_POSTFIX "d") ++ endif() ++ ++ # Propagate CMake definitions ++ ++ if(CORE_SYSTEM_NAME STREQUAL darwin_embedded) ++ set(EXTRA_ARGS -DIOS=ON) ++ elseif(WINDOWS_STORE) ++ set(EXTRA_ARGS -DWINRT=ON) ++ endif() ++ ++ set(CMAKE_ARGS -DCMAKE_CXX_STANDARD=17 ++ -DUSE_SYSTEM_TZ_DB=OFF ++ -DMANUAL_TZ_DB=ON ++ -DUSE_TZ_DB_IN_DOT=OFF ++ -DBUILD_SHARED_LIBS=OFF ++ -DBUILD_TZ_LIB=ON ++ ${EXTRA_ARGS}) ++ ++ # Work around old release ++ ++ file(GLOB patches "${CMAKE_SOURCE_DIR}/tools/depends/target/date/*.patch") ++ ++ generate_patchcommand("${patches}") ++ ++ BUILD_DEP_TARGET() ++else() ++ if(PKG_CONFIG_FOUND) ++ pkg_check_modules(PC_DATE libdate-tz>=3.0.1 QUIET) ++ endif() ++ ++ find_path(DATE_INCLUDE_DIR date/date.h ++ PATHS ${PC_DATE_INCLUDEDIR}) ++ find_library(DATE_LIBRARY_RELEASE NAMES date-tz libdate-tz ++ PATHS ${PC_DATE_LIBDIR}) ++ find_library(DATE_LIBRARY_DEBUG NAMES date-tzd libdate-tzd ++ PATHS ${PC_DATE_LIBDIR}) ++ set(DATE_VERSION ${PC_DATE_VERSION}) ++endif() ++ ++include(SelectLibraryConfigurations) ++select_library_configurations(DATE) ++ ++include(FindPackageHandleStandardArgs) ++find_package_handle_standard_args(Date ++ REQUIRED_VARS DATE_LIBRARY DATE_INCLUDE_DIR ++ VERSION_VAR DATE_VERSION) ++ ++if(DATE_FOUND) ++ set(DATE_INCLUDE_DIRS ${DATE_INCLUDE_DIR}) ++ set(DATE_LIBRARIES ${DATE_LIBRARY}) ++ ++ if(ENABLE_INTERNAL_TZDATA) ++ set(DATE_DEFINITIONS ${DATE_DEFINITIONS} -DDATE_INTERNAL_TZDATA) ++ endif() ++ ++ if(DATE_HAS_STRINGVIEW) ++ set(DATE_DEFINITIONS ${DATE_DEFINITIONS} -DDATE_HAS_STRINGVIEW) ++ endif() ++ ++ if(NOT TARGET Date::Date) ++ add_library(Date::Date UNKNOWN IMPORTED) ++ if(DATE_LIBRARY_RELEASE) ++ set_target_properties(Date::Date PROPERTIES ++ IMPORTED_CONFIGURATIONS RELEASE ++ IMPORTED_LOCATION "${DATE_LIBRARY_RELEASE}") ++ endif() ++ if(DATE_LIBRARY_DEBUG) ++ set_target_properties(Date::Date PROPERTIES ++ IMPORTED_CONFIGURATIONS DEBUG ++ IMPORTED_LOCATION "${DATE_LIBRARY_DEBUG}") ++ endif() ++ set_target_properties(Date::Date PROPERTIES ++ INTERFACE_INCLUDE_DIRECTORIES "${DATE_INCLUDE_DIR}") ++ if(TARGET date) ++ add_dependencies(Date::Date date) ++ endif() ++ endif() ++ set_property(GLOBAL APPEND PROPERTY INTERNAL_DEPS_PROP Date::Date) ++endif() ++ ++mark_as_advanced(DATE_INCLUDE_DIR DATE_LIBRARY) +diff --git a/cmake/modules/FindTZData.cmake b/cmake/modules/FindTZData.cmake +new file mode 100644 +index 0000000000..b7241a6b04 +--- /dev/null ++++ b/cmake/modules/FindTZData.cmake +@@ -0,0 +1,33 @@ ++#.rst: ++# FindTZData ++# ------- ++# Populates resource.timezone with TZDATA if requested ++# ++# This will define the following variables:: ++# ++# TZDATA_FOUND - system has internal TZDATA ++# TZDATA_VERSION - version of internal TZDATA ++ ++if(ENABLE_INTERNAL_TZDATA) ++ include(cmake/scripts/common/ModuleHelpers.cmake) ++ ++ set(MODULE_LC tzdata) ++ ++ SETUP_BUILD_VARS() ++ ++ # Mirror tzdata to resource.timezone ++ ++ set(CMAKE_ARGS -DINSTALL_DIR=${CMAKE_BINARY_DIR}/addons/resource.timezone/resources/tzdata) ++ ++ # Add CMakeLists.txt installing sources as target ++ ++ set(patches ${CMAKE_SOURCE_DIR}/tools/depends/target/tzdata/001-cmakelists.patch) ++ ++ generate_patchcommand("${patches}") ++ ++ BUILD_DEP_TARGET() ++else() ++ set(TZDATA_VERSION "none") ++endif() ++ ++set(TZDATA_FOUND TRUE) +diff --git a/cmake/scripts/linux/Install.cmake b/cmake/scripts/linux/Install.cmake +index 331722cb56..e3ceacbf76 100644 +--- a/cmake/scripts/linux/Install.cmake ++++ b/cmake/scripts/linux/Install.cmake +@@ -85,6 +85,13 @@ foreach(file ${install_data}) + COMPONENT kodi) + endforeach() + ++# Install timezone resources ++if(ENABLE_INTERNAL_TZDATA) ++ install(DIRECTORY ${CMAKE_BINARY_DIR}/addons/resource.timezone/resources/tzdata ++ DESTINATION ${datarootdir}/${APP_NAME_LC}/addons/resource.timezone/resources ++ COMPONENT kodi) ++endif() ++ + # Install xsession entry + install(FILES ${CMAKE_BINARY_DIR}/${CORE_BUILD_DIR}/${APP_NAME_LC}-xsession.desktop + RENAME ${APP_NAME_LC}.desktop +diff --git a/tools/depends/target/date/0001-cmake-install-all-headers.patch b/tools/depends/target/date/0001-cmake-install-all-headers.patch +new file mode 100644 +index 0000000000..e8ac76a48b +--- /dev/null ++++ b/tools/depends/target/date/0001-cmake-install-all-headers.patch +@@ -0,0 +1,74 @@ ++From 8b3fac88cb5e4fef81b55b7537232a6418752153 Mon Sep 17 00:00:00 2001 ++From: Andrea Pappacoda ++Date: Thu, 4 Aug 2022 11:35:41 +0200 ++Subject: [PATCH 1/4] cmake: install all headers ++ ++Lazy solution: mark all headers as either PUBLIC_ or PRIVATE_HEADER, ++requiring CMake 3.15. ++ ++Bug: https://github.com/HowardHinnant/date/pull/503 ++Bug-Debian: https://bugs.debian.org/1001895 ++--- ++ CMakeLists.txt | 20 +++++++++++++++----- ++ 1 file changed, 15 insertions(+), 5 deletions(-) ++ ++diff --git a/CMakeLists.txt b/CMakeLists.txt ++index 012512a..c0bda7f 100644 ++--- a/CMakeLists.txt +++++ b/CMakeLists.txt ++@@ -75,7 +75,13 @@ target_sources( date INTERFACE ++ ) ++ if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.15) ++ # public headers will get installed: ++- set_target_properties( date PROPERTIES PUBLIC_HEADER include/date/date.h ) +++ set_property(TARGET date PROPERTY PUBLIC_HEADER +++ include/date/date.h +++ include/date/islamic.h +++ include/date/iso_week.h +++ include/date/julian.h +++ include/date/solar_hijri.h +++ ) ++ endif () ++ ++ # These used to be set with generator expressions, ++@@ -137,14 +143,16 @@ if( BUILD_TZ_LIB ) ++ target_compile_definitions( date-tz PUBLIC DATE_BUILD_DLL=1 ) ++ endif() ++ ++- set(TZ_HEADERS include/date/tz.h) +++ set(TZ_PUBLIC_HEADERS include/date/ptz.h include/date/tz.h) +++ set(TZ_PRIVATE_HEADERS include/date/tz_private.h) ++ ++ if( IOS ) ++- list(APPEND TZ_HEADERS include/date/ios.h) +++ list(APPEND TZ_PRIVATE_HEADERS include/date/ios.h) ++ endif( ) ++ set_target_properties( date-tz PROPERTIES ++ POSITION_INDEPENDENT_CODE ON ++- PUBLIC_HEADER "${TZ_HEADERS}" +++ PUBLIC_HEADER "${TZ_PUBLIC_HEADERS}" +++ PRIVATE_HEADER "${TZ_PRIVATE_HEADERS}" ++ VERSION "${PROJECT_VERSION}" ++ SOVERSION "${ABI_VERSION}" ) ++ if( NOT MSVC ) ++@@ -170,7 +178,8 @@ write_basic_package_version_file( "${version_config}" ++ ++ install( TARGETS date ++ EXPORT dateConfig ++- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date ) +++ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date +++ PRIVATE_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date ) ++ export( TARGETS date NAMESPACE date:: FILE dateTargets.cmake ) ++ if (CMAKE_VERSION VERSION_LESS 3.15) ++ install( ++@@ -182,6 +191,7 @@ if( BUILD_TZ_LIB ) ++ install( TARGETS date-tz ++ EXPORT dateConfig ++ PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date +++ PRIVATE_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/date ++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ++ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ++ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # This is for Windows ++-- ++2.39.0 ++ +diff --git a/tools/depends/target/date/0002-Fix-Android-build.patch b/tools/depends/target/date/0002-Fix-Android-build.patch +new file mode 100644 +index 0000000000..e497b41bcf +--- /dev/null ++++ b/tools/depends/target/date/0002-Fix-Android-build.patch +@@ -0,0 +1,37 @@ ++From 5790d6aed57bda1dc1577860d8382678100ebfa3 Mon Sep 17 00:00:00 2001 ++From: Vasyl Gello ++Date: Wed, 11 Jan 2023 11:35:41 +0200 ++Subject: [PATCH 2/4] Fix Android build ++ ++Signed-off-by: Vasyl Gello ++--- ++ src/tz.cpp | 6 ++++-- ++ 1 file changed, 4 insertions(+), 2 deletions(-) ++ ++diff --git a/src/tz.cpp b/src/tz.cpp ++index 26babbd..69bab7c 100644 ++--- a/src/tz.cpp +++++ b/src/tz.cpp ++@@ -141,7 +141,7 @@ ++ # endif // HAS_REMOTE_API ++ #else // !_WIN32 ++ # include ++-# if !USE_OS_TZDB && !defined(INSTALL) +++# if !USE_OS_TZDB && !defined(INSTALL) && !defined(__ANDROID__) ++ # include ++ # endif ++ # include ++@@ -245,7 +245,9 @@ expand_path(std::string path) ++ { ++ # if TARGET_OS_IPHONE ++ return date::iOSUtils::get_tzdata_path(); ++-# else // !TARGET_OS_IPHONE +++# elif defined(__ANDROID__) +++ return "/data/local/tmp"; +++# else // !TARGET_OS_IPHONE && !__ANDROID__ ++ ::wordexp_t w{}; ++ std::unique_ptr<::wordexp_t, void(*)(::wordexp_t*)> hold{&w, ::wordfree}; ++ ::wordexp(path.c_str(), &w, 0); ++-- ++2.39.0 ++ +diff --git a/tools/depends/target/date/0003-Fix-UWP-build.patch b/tools/depends/target/date/0003-Fix-UWP-build.patch +new file mode 100644 +index 0000000000..683ed4126d +--- /dev/null ++++ b/tools/depends/target/date/0003-Fix-UWP-build.patch +@@ -0,0 +1,58 @@ ++From 1e512b082eb99b5d9cd790158f3bdaefcbac823d Mon Sep 17 00:00:00 2001 ++From: Vasyl Gello ++Date: Wed, 11 Jan 2023 09:58:34 +0200 ++Subject: [PATCH 3/4] Fix UWP build ++ ++We don't need download_folder to be determined. ++Bringing full UWP runtime just for that is an overkill. ++--- ++ CMakeLists.txt | 4 ++++ ++ src/tz.cpp | 17 +++++++++++++++++ ++ 2 files changed, 21 insertions(+) ++ ++diff --git a/CMakeLists.txt b/CMakeLists.txt ++index c0bda7f..0fa3dc2 100644 ++--- a/CMakeLists.txt +++++ b/CMakeLists.txt ++@@ -133,6 +133,10 @@ if( BUILD_TZ_LIB ) ++ target_compile_definitions( date-tz PRIVATE AUTO_DOWNLOAD=1 HAS_REMOTE_API=1 ) ++ endif() ++ +++ if(WINRT) +++ target_compile_definitions( date-tz PRIVATE WINRT=1 INSTALL=. ) +++ endif() +++ ++ if ( USE_SYSTEM_TZ_DB AND NOT WIN32 AND NOT MANUAL_TZ_DB ) ++ target_compile_definitions( date-tz PRIVATE INSTALL=. PUBLIC USE_OS_TZDB=1 ) ++ else() ++diff --git a/src/tz.cpp b/src/tz.cpp ++index 69bab7c..aa15192 100644 ++--- a/src/tz.cpp +++++ b/src/tz.cpp ++@@ -234,6 +234,23 @@ get_download_folder() ++ ++ # endif // !INSTALL ++ +++# else // WINRT +++static +++std::string +++get_program_folder() +++{ +++ return ""; +++} +++ +++# ifndef INSTALL +++ +++static +++std::string +++get_download_folder() +++{ +++ return ""; +++} +++# endif // !INSTALL ++ # endif // WINRT ++ # else // !_WIN32 ++ ++-- ++2.39.0 ++ +diff --git a/tools/depends/target/date/DATE-VERSION b/tools/depends/target/date/DATE-VERSION +new file mode 100644 +index 0000000000..0608029104 +--- /dev/null ++++ b/tools/depends/target/date/DATE-VERSION +@@ -0,0 +1,6 @@ ++LIBNAME=date ++VERSION=3.0.1 ++ARCHIVE=$(LIBNAME)-$(VERSION).tar.gz ++SHA512=6bdc7cba821d66e17a559250cc0ce0095808e9db81cec9e16eaa4c31abdfa705299c67b72016d9b06b302bc306d063e83a374eb00728071b83a5ad650d59034f ++BYPRODUCT=libdate-tz.a ++BYPRODUCT_WIN=date-tz.lib +diff --git a/tools/depends/target/tzdata/001-cmakelists.patch b/tools/depends/target/tzdata/001-cmakelists.patch +new file mode 100644 +index 0000000000..dda65fc542 +--- /dev/null ++++ b/tools/depends/target/tzdata/001-cmakelists.patch +@@ -0,0 +1,11 @@ ++diff --git a/CMakeLists.txt b/CMakeLists.txt ++new file mode 100644 ++index 0000000..8a015cd ++--- /dev/null +++++ b/CMakeLists.txt ++@@ -0,0 +1,5 @@ +++cmake_minimum_required(VERSION 3.2) +++ +++project(kodi-tzdata) +++ +++install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ DESTINATION ${INSTALL_DIR}) +diff --git a/tools/depends/target/tzdata/TZDATA-VERSION b/tools/depends/target/tzdata/TZDATA-VERSION +new file mode 100644 +index 0000000000..7f92f0b74c +--- /dev/null ++++ b/tools/depends/target/tzdata/TZDATA-VERSION +@@ -0,0 +1,5 @@ ++LIBNAME=tzdata ++VERSION=2022g ++ARCHIVE=$(LIBNAME)$(VERSION).tar.gz ++SHA512=7f79394295e00e3a24ebdbf9af3bc454a65f432a93b517e7e96c7f9db9949f6f5fdae9892a9d3789ff44ae0eb1bfe4744d36976b4624659af951d26414f94e65 ++SOURCE_DIR=addons/resource.timezone/resources/tzdata +-- +2.43.0 + + +From b4a3fafe6da802ae1d690ceb07fbf5a781e0301a Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Tue, 17 Mar 2020 13:12:04 -0700 +Subject: [PATCH 02/19] add resource.timezone + +--- + addons/kodi.resource/addon.xml | 1 + + addons/kodi.resource/timezone.xsd | 16 ++++++++++++ + addons/resource.timezone/addon.xml | 12 +++++++++ + cmake/installdata/common/addons.txt | 1 + + system/addon-manifest.xml | 1 + + xbmc/ServiceManager.cpp | 20 +++++++++++++++ + xbmc/addons/AddonBuilder.cpp | 3 +++ + xbmc/addons/CMakeLists.txt | 2 ++ + xbmc/addons/TimeZoneResource.cpp | 39 +++++++++++++++++++++++++++++ + xbmc/addons/TimeZoneResource.h | 25 ++++++++++++++++++ + xbmc/addons/addoninfo/AddonInfo.cpp | 3 ++- + xbmc/addons/addoninfo/AddonType.h | 1 + + xbmc/application/Application.cpp | 20 +++++++++++++++ + 13 files changed, 143 insertions(+), 1 deletion(-) + create mode 100644 addons/kodi.resource/timezone.xsd + create mode 100644 addons/resource.timezone/addon.xml + create mode 100644 xbmc/addons/TimeZoneResource.cpp + create mode 100644 xbmc/addons/TimeZoneResource.h + +diff --git a/addons/kodi.resource/addon.xml b/addons/kodi.resource/addon.xml +index 2bb3221b42..b615575744 100644 +--- a/addons/kodi.resource/addon.xml ++++ b/addons/kodi.resource/addon.xml +@@ -7,4 +7,5 @@ + + + ++ + +diff --git a/addons/kodi.resource/timezone.xsd b/addons/kodi.resource/timezone.xsd +new file mode 100644 +index 0000000000..990f8893ea +--- /dev/null ++++ b/addons/kodi.resource/timezone.xsd +@@ -0,0 +1,16 @@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +diff --git a/addons/resource.timezone/addon.xml b/addons/resource.timezone/addon.xml +new file mode 100644 +index 0000000000..174b3f5687 +--- /dev/null ++++ b/addons/resource.timezone/addon.xml +@@ -0,0 +1,12 @@ ++ ++ ++ ++ ++ ++ ++ ++ Kodi Timezone Database ++ Kodi Timezone Database ++ all ++ ++ +diff --git a/cmake/installdata/common/addons.txt b/cmake/installdata/common/addons.txt +index 263e57bb66..0411f8a3da 100644 +--- a/cmake/installdata/common/addons.txt ++++ b/cmake/installdata/common/addons.txt +@@ -26,6 +26,7 @@ addons/metadata.tvshows.themoviedb.org.python/* + addons/repository.xbmc.org/* + addons/resource.images.weathericons.default/* + addons/resource.language.en_gb/* ++addons/resource.timezone/* + addons/resource.uisounds.kodi/* + addons/screensaver.xbmc.builtin.black/* + addons/screensaver.xbmc.builtin.dim/* +diff --git a/system/addon-manifest.xml b/system/addon-manifest.xml +index 7df13a6d6f..842e4e8206 100644 +--- a/system/addon-manifest.xml ++++ b/system/addon-manifest.xml +@@ -38,6 +38,7 @@ + repository.xbmc.org + resource.images.weathericons.default + resource.language.en_gb ++ resource.timezone + resource.uisounds.kodi + screensaver.xbmc.builtin.black + screensaver.xbmc.builtin.dim +diff --git a/xbmc/ServiceManager.cpp b/xbmc/ServiceManager.cpp +index 02d9900e9b..83070489e3 100644 +--- a/xbmc/ServiceManager.cpp ++++ b/xbmc/ServiceManager.cpp +@@ -17,6 +17,7 @@ + #include "addons/RepositoryUpdater.h" + #include "addons/Service.h" + #include "addons/VFSEntry.h" ++#include "addons/addoninfo/AddonType.h" + #include "addons/binary-addons/BinaryAddonManager.h" + #include "cores/DataCacheCore.h" + #include "cores/RetroPlayer/guibridge/GUIGameRenderManager.h" +@@ -45,9 +46,14 @@ + #endif + #include "storage/MediaManager.h" + #include "utils/FileExtensionProvider.h" ++#include "utils/URIUtils.h" + #include "utils/log.h" + #include "weather/WeatherManager.h" + ++#define USE_OS_TZDB 0 ++#define HAS_REMOTE_API 0 ++#include ++ + using namespace KODI; + + CServiceManager::CServiceManager() = default; +@@ -79,6 +85,20 @@ bool CServiceManager::InitForTesting() + m_extsMimeSupportList.reset(new ADDONS::CExtsMimeSupportList(*m_addonMgr)); + m_fileExtensionProvider.reset(new CFileExtensionProvider(*m_addonMgr)); + ++#if defined(DATE_INTERNAL_TZDATA) ++ ADDON::AddonPtr addon; ++ if (!m_addonMgr->GetAddon("resource.timezone", addon, ADDON::AddonType::RESOURCE_TIMEZONE, ++ ADDON::OnlyEnabled::CHOICE_YES)) ++ { ++ CLog::LogF(LOGFATAL, "failed to find resource.timezone"); ++ return false; ++ } ++ ++ std::string tzdataPath = URIUtils::AddFileToFolder(addon->Path(), "resources", "tzdata"); ++ CLog::LogF(LOGDEBUG, "tzdata path: {}", tzdataPath); ++ date::set_install(tzdataPath); ++#endif ++ + init_level = 1; + return true; + } +diff --git a/xbmc/addons/AddonBuilder.cpp b/xbmc/addons/AddonBuilder.cpp +index 7d52ee0431..dd72a08705 100644 +--- a/xbmc/addons/AddonBuilder.cpp ++++ b/xbmc/addons/AddonBuilder.cpp +@@ -19,6 +19,7 @@ + #include "addons/Scraper.h" + #include "addons/Service.h" + #include "addons/Skin.h" ++#include "addons/TimeZoneResource.h" + #include "addons/UISoundsResource.h" + #include "addons/Webinterface.h" + #include "addons/addoninfo/AddonInfo.h" +@@ -106,6 +107,8 @@ AddonPtr CAddonBuilder::Generate(const AddonInfoPtr& info, AddonType type) + return std::make_shared(info); + case AddonType::RESOURCE_UISOUNDS: + return std::make_shared(info); ++ case AddonType::RESOURCE_TIMEZONE: ++ return std::make_shared(info); + case AddonType::REPOSITORY: + return std::make_shared(info); + case AddonType::CONTEXTMENU_ITEM: +diff --git a/xbmc/addons/CMakeLists.txt b/xbmc/addons/CMakeLists.txt +index 947a885b4c..101371f12d 100644 +--- a/xbmc/addons/CMakeLists.txt ++++ b/xbmc/addons/CMakeLists.txt +@@ -26,6 +26,7 @@ set(SOURCES Addon.cpp + ScreenSaver.cpp + Service.cpp + Skin.cpp ++ TimeZoneResource.cpp + UISoundsResource.cpp + VFSEntry.cpp + Visualization.cpp +@@ -66,6 +67,7 @@ set(HEADERS Addon.h + ScreenSaver.h + Service.h + Skin.h ++ TimeZoneResource.h + UISoundsResource.h + VFSEntry.h + Visualization.h +diff --git a/xbmc/addons/TimeZoneResource.cpp b/xbmc/addons/TimeZoneResource.cpp +new file mode 100644 +index 0000000000..94246c9e18 +--- /dev/null ++++ b/xbmc/addons/TimeZoneResource.cpp +@@ -0,0 +1,39 @@ ++/* ++ * Copyright (C) 2015-2018 Team Kodi ++ * This file is part of Kodi - https://kodi.tv ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ * See LICENSES/README.md for more information. ++ */ ++#include "TimeZoneResource.h" ++ ++#include "addons/addoninfo/AddonType.h" ++#include "utils/StringUtils.h" ++#include "utils/URIUtils.h" ++ ++#include ++ ++namespace ADDON ++{ ++ ++CTimeZoneResource::CTimeZoneResource(const AddonInfoPtr& addonInfo) ++ : CResource(addonInfo, AddonType::RESOURCE_TIMEZONE) ++{ ++} ++ ++bool CTimeZoneResource::IsAllowed(const std::string& file) const ++{ ++ return true; ++} ++ ++bool CTimeZoneResource::IsInUse() const ++{ ++ return true; ++} ++ ++void CTimeZoneResource::OnPostInstall(bool update, bool modal) ++{ ++ date::reload_tzdb(); ++} ++ ++} // namespace ADDON +diff --git a/xbmc/addons/TimeZoneResource.h b/xbmc/addons/TimeZoneResource.h +new file mode 100644 +index 0000000000..a979f05dec +--- /dev/null ++++ b/xbmc/addons/TimeZoneResource.h +@@ -0,0 +1,25 @@ ++/* ++ * Copyright (C) 2015-2018 Team Kodi ++ * This file is part of Kodi - https://kodi.tv ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ * See LICENSES/README.md for more information. ++ */ ++ ++#pragma once ++ ++#include "addons/Resource.h" ++ ++namespace ADDON ++{ ++ ++class CTimeZoneResource : public CResource ++{ ++public: ++ explicit CTimeZoneResource(const AddonInfoPtr& addonInfo); ++ bool IsAllowed(const std::string& file) const override; ++ bool IsInUse() const override; ++ void OnPostInstall(bool update, bool modal) override; ++}; ++ ++} // namespace ADDON +diff --git a/xbmc/addons/addoninfo/AddonInfo.cpp b/xbmc/addons/addoninfo/AddonInfo.cpp +index 61bc087443..f82a947981 100644 +--- a/xbmc/addons/addoninfo/AddonInfo.cpp ++++ b/xbmc/addons/addoninfo/AddonInfo.cpp +@@ -37,7 +37,7 @@ typedef struct + } TypeMapping; + + // clang-format off +-static constexpr const std::array types = ++static constexpr const std::array types = + {{ + {"unknown", "", AddonType::UNKNOWN, 0, AddonInstanceSupport::SUPPORT_NONE, "" }, + {"xbmc.metadata.scraper.albums", "", AddonType::SCRAPER_ALBUMS, 24016, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonAlbumInfo.png" }, +@@ -73,6 +73,7 @@ static constexpr const std::array types = + {"xbmc.service", "", AddonType::SERVICE, 24018, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonService.png" }, + {"kodi.resource.images", "", AddonType::RESOURCE_IMAGES, 24035, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonImages.png" }, + {"kodi.resource.language", "", AddonType::RESOURCE_LANGUAGE, 24026, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonLanguage.png" }, ++ {"kodi.resource.timezone", "", AddonType::RESOURCE_TIMEZONE, 24026, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonTimeZone.png" }, + {"kodi.resource.uisounds", "", AddonType::RESOURCE_UISOUNDS, 24006, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonUISounds.png" }, + {"kodi.resource.games", "", AddonType::RESOURCE_GAMES, 35209, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonGame.png" }, + {"kodi.resource.font", "", AddonType::RESOURCE_FONT, 13303, AddonInstanceSupport::SUPPORT_NONE, "DefaultAddonFont.png" }, +diff --git a/xbmc/addons/addoninfo/AddonType.h b/xbmc/addons/addoninfo/AddonType.h +index 9a0ee260cf..27f49e3138 100644 +--- a/xbmc/addons/addoninfo/AddonType.h ++++ b/xbmc/addons/addoninfo/AddonType.h +@@ -46,6 +46,7 @@ enum class AddonType + AUDIODECODER, + RESOURCE_IMAGES, + RESOURCE_LANGUAGE, ++ RESOURCE_TIMEZONE, + RESOURCE_UISOUNDS, + RESOURCE_GAMES, + RESOURCE_FONT, +diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp +index 0765cf3723..ea1b152dfd 100644 +--- a/xbmc/application/Application.cpp ++++ b/xbmc/application/Application.cpp +@@ -195,8 +195,12 @@ + #include "pictures/GUIWindowSlideShow.h" + #include "utils/CharsetConverter.h" + ++#define USE_OS_TZDB 0 ++#define HAS_REMOTE_API 0 + #include + ++#include ++ + using namespace ADDON; + using namespace XFILE; + #ifdef HAS_DVD_DRIVE +@@ -435,6 +439,22 @@ bool CApplication::Create() + return false; + } + ++ // Load timezone information ++#if defined(DATE_INTERNAL_TZDATA) ++ ADDON::AddonPtr addon; ++ if (!CServiceBroker::GetAddonMgr().GetAddon("resource.timezone", addon, ++ ADDON::AddonType::RESOURCE_TIMEZONE, ++ ADDON::OnlyEnabled::CHOICE_YES)) ++ { ++ CLog::LogF(LOGFATAL, "failed to find resource.timezone"); ++ return false; ++ } ++ ++ std::string tzdataPath = URIUtils::AddFileToFolder(addon->Path(), "resources", "tzdata"); ++ CLog::LogF(LOGDEBUG, "tzdata path: {}", tzdataPath); ++ date::set_install(tzdataPath); ++#endif ++ + m_pActiveAE.reset(new ActiveAE::CActiveAE()); + CServiceBroker::RegisterAE(m_pActiveAE.get()); + +-- +2.43.0 + + +From 6f004e0aa809505cbf71612df44343a15011d0cf Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Mon, 10 Feb 2020 15:13:43 -0800 +Subject: [PATCH 03/19] CDateTime: convert to use std::chrono + +--- + xbmc/XBDateTime.cpp | 604 ++++++++-------------- + xbmc/XBDateTime.h | 31 +- + xbmc/platform/posix/PosixTimezone.cpp | 3 - + xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp | 2 - + 4 files changed, 244 insertions(+), 396 deletions(-) + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index 85d5d466d3..47c6b8d1a6 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -17,29 +17,17 @@ + + #include + +-#define SECONDS_PER_DAY 86400L +-#define SECONDS_PER_HOUR 3600L +-#define SECONDS_PER_MINUTE 60L +-#define SECONDS_TO_FILETIME 10000000L ++#define USE_OS_TZDB 0 ++#define HAS_REMOTE_API 0 ++#include ++#include ++#include + +-static const char *DAY_NAMES[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + +-///////////////////////////////////////////////// +-// +-// CDateTimeSpan +-// +- +-CDateTimeSpan::CDateTimeSpan() +-{ +- m_timeSpan.highDateTime = 0; +- m_timeSpan.lowDateTime = 0; +-} +- + CDateTimeSpan::CDateTimeSpan(const CDateTimeSpan& span) + { +- m_timeSpan.highDateTime = span.m_timeSpan.highDateTime; +- m_timeSpan.lowDateTime = span.m_timeSpan.lowDateTime; ++ m_timeSpan = span.m_timeSpan; + } + + CDateTimeSpan::CDateTimeSpan(int day, int hour, int minute, int second) +@@ -49,7 +37,7 @@ CDateTimeSpan::CDateTimeSpan(int day, int hour, int minute, int second) + + bool CDateTimeSpan::operator >(const CDateTimeSpan& right) const + { +- return KODI::TIME::CompareFileTime(&m_timeSpan, &right.m_timeSpan) > 0; ++ return m_timeSpan > right.m_timeSpan; + } + + bool CDateTimeSpan::operator >=(const CDateTimeSpan& right) const +@@ -59,7 +47,7 @@ bool CDateTimeSpan::operator >=(const CDateTimeSpan& right) const + + bool CDateTimeSpan::operator <(const CDateTimeSpan& right) const + { +- return KODI::TIME::CompareFileTime(&m_timeSpan, &right.m_timeSpan) < 0; ++ return m_timeSpan < right.m_timeSpan; + } + + bool CDateTimeSpan::operator <=(const CDateTimeSpan& right) const +@@ -69,7 +57,7 @@ bool CDateTimeSpan::operator <=(const CDateTimeSpan& right) const + + bool CDateTimeSpan::operator ==(const CDateTimeSpan& right) const + { +- return KODI::TIME::CompareFileTime(&m_timeSpan, &right.m_timeSpan) == 0; ++ return m_timeSpan == right.m_timeSpan; + } + + bool CDateTimeSpan::operator !=(const CDateTimeSpan& right) const +@@ -81,15 +69,7 @@ CDateTimeSpan CDateTimeSpan::operator +(const CDateTimeSpan& right) const + { + CDateTimeSpan left(*this); + +- LARGE_INTEGER timeLeft; +- left.ToLargeInt(timeLeft); +- +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); +- +- timeLeft.QuadPart+=timeRight.QuadPart; +- +- left.FromLargeInt(timeLeft); ++ left.m_timeSpan += right.m_timeSpan; + + return left; + } +@@ -98,122 +78,75 @@ CDateTimeSpan CDateTimeSpan::operator -(const CDateTimeSpan& right) const + { + CDateTimeSpan left(*this); + +- LARGE_INTEGER timeLeft; +- left.ToLargeInt(timeLeft); +- +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); +- +- timeLeft.QuadPart-=timeRight.QuadPart; +- +- left.FromLargeInt(timeLeft); ++ left.m_timeSpan -= right.m_timeSpan; + + return left; + } + + const CDateTimeSpan& CDateTimeSpan::operator +=(const CDateTimeSpan& right) + { +- LARGE_INTEGER timeThis; +- ToLargeInt(timeThis); +- +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); +- +- timeThis.QuadPart+=timeRight.QuadPart; +- +- FromLargeInt(timeThis); ++ m_timeSpan += right.m_timeSpan; + + return *this; + } + + const CDateTimeSpan& CDateTimeSpan::operator -=(const CDateTimeSpan& right) + { +- LARGE_INTEGER timeThis; +- ToLargeInt(timeThis); +- +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); +- +- timeThis.QuadPart-=timeRight.QuadPart; +- +- FromLargeInt(timeThis); ++ m_timeSpan -= right.m_timeSpan; + + return *this; + } + +-void CDateTimeSpan::ToLargeInt(LARGE_INTEGER& time) const +-{ +- time.u.HighPart = m_timeSpan.highDateTime; +- time.u.LowPart = m_timeSpan.lowDateTime; +-} +- +-void CDateTimeSpan::FromLargeInt(const LARGE_INTEGER& time) +-{ +- m_timeSpan.highDateTime = time.u.HighPart; +- m_timeSpan.lowDateTime = time.u.LowPart; +-} +- + void CDateTimeSpan::SetDateTimeSpan(int day, int hour, int minute, int second) + { +- LARGE_INTEGER time; +- ToLargeInt(time); +- +- time.QuadPart= static_cast(day) *SECONDS_PER_DAY*SECONDS_TO_FILETIME; +- time.QuadPart+= static_cast(hour) *SECONDS_PER_HOUR*SECONDS_TO_FILETIME; +- time.QuadPart+= static_cast(minute) *SECONDS_PER_MINUTE*SECONDS_TO_FILETIME; +- time.QuadPart+= static_cast(second) *SECONDS_TO_FILETIME; +- +- FromLargeInt(time); ++ m_timeSpan = std::chrono::duration_cast(date::days(day)) + ++ std::chrono::duration_cast(std::chrono::hours(hour)) + ++ std::chrono::duration_cast(std::chrono::minutes(minute)) + ++ std::chrono::duration_cast(std::chrono::seconds(second)); + } + + void CDateTimeSpan::SetFromTimeString(const std::string& time) // hh:mm + { + if (time.size() >= 5 && time[2] == ':') + { +- int hour = atoi(time.substr(0, 2).c_str()); +- int minutes = atoi(time.substr(3, 2).c_str()); +- SetDateTimeSpan(0,hour,minutes,0); ++ int hour = std::stoi(time.substr(0, 2)); ++ int minutes = std::stoi(time.substr(3, 2)); ++ SetDateTimeSpan(0, hour, minutes, 0); + } + } + + int CDateTimeSpan::GetDays() const + { +- LARGE_INTEGER time; +- ToLargeInt(time); +- +- return (int)(time.QuadPart/SECONDS_TO_FILETIME)/SECONDS_PER_DAY; ++ return date::floor(m_timeSpan).count(); + } + + int CDateTimeSpan::GetHours() const + { +- LARGE_INTEGER time; +- ToLargeInt(time); ++ auto time = date::floor(m_timeSpan); ++ auto dp = date::make_time(m_timeSpan - time); + +- return (int)((time.QuadPart/SECONDS_TO_FILETIME)%SECONDS_PER_DAY)/SECONDS_PER_HOUR; ++ return dp.hours().count(); + } + + int CDateTimeSpan::GetMinutes() const + { +- LARGE_INTEGER time; +- ToLargeInt(time); ++ auto time = date::floor(m_timeSpan); ++ auto dp = date::make_time(m_timeSpan - time); + +- return (int)((time.QuadPart/SECONDS_TO_FILETIME%SECONDS_PER_DAY)%SECONDS_PER_HOUR)/SECONDS_PER_MINUTE; ++ return dp.minutes().count(); + } + + int CDateTimeSpan::GetSeconds() const + { +- LARGE_INTEGER time; +- ToLargeInt(time); ++ auto time = date::floor(m_timeSpan); ++ auto dp = date::make_time(m_timeSpan - time); + +- return (int)(((time.QuadPart/SECONDS_TO_FILETIME%SECONDS_PER_DAY)%SECONDS_PER_HOUR)%SECONDS_PER_MINUTE)%SECONDS_PER_MINUTE; ++ return dp.seconds().count(); + } + + int CDateTimeSpan::GetSecondsTotal() const + { +- LARGE_INTEGER time; +- ToLargeInt(time); +- +- return (int)(time.QuadPart/SECONDS_TO_FILETIME); ++ return std::chrono::duration_cast(m_timeSpan).count(); + } + + void CDateTimeSpan::SetFromPeriod(const std::string &period) +@@ -233,25 +166,26 @@ void CDateTimeSpan::SetFromPeriod(const std::string &period) + SetDateTimeSpan(days, 0, 0, 0); + } + +-///////////////////////////////////////////////// +-// +-// CDateTime +-// +- + CDateTime::CDateTime() + { + Reset(); + } + +-CDateTime::CDateTime(const KODI::TIME::SystemTime& time) ++CDateTime::CDateTime(const KODI::TIME::SystemTime& systemTime) + { +- // we store internally as a FileTime +- m_state = ToFileTime(time, m_time) ? valid : invalid; ++ KODI::TIME::FileTime fileTime; ++ m_state = ToFileTime(systemTime, fileTime) ? valid : invalid; ++ ++ time_t time; ++ KODI::TIME::FileTimeToTimeT(&fileTime, &time); ++ m_time = std::chrono::system_clock::from_time_t(time); + } + +-CDateTime::CDateTime(const KODI::TIME::FileTime& time) : m_time(time) ++CDateTime::CDateTime(const KODI::TIME::FileTime& fileTime) + { +- SetValid(true); ++ time_t time; ++ KODI::TIME::FileTimeToTimeT(&fileTime, &time); ++ m_time = std::chrono::system_clock::from_time_t(time); + } + + CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time) +@@ -261,12 +195,20 @@ CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time) + + CDateTime::CDateTime(const time_t& time) + { +- m_state = ToFileTime(time, m_time) ? valid : invalid; ++ m_time = std::chrono::system_clock::from_time_t(time); ++ SetValid(true); ++} ++ ++CDateTime::CDateTime(const std::chrono::system_clock::time_point& time) ++{ ++ m_time = time; ++ SetValid(true); + } + + CDateTime::CDateTime(const tm& time) + { +- m_state = ToFileTime(time, m_time) ? valid : invalid; ++ m_time = std::chrono::system_clock::from_time_t(std::mktime(const_cast(&time))); ++ SetValid(true); + } + + CDateTime::CDateTime(int year, int month, int day, int hour, int minute, int second) +@@ -276,52 +218,66 @@ CDateTime::CDateTime(int year, int month, int day, int hour, int minute, int sec + + CDateTime CDateTime::GetCurrentDateTime() + { +- // get the current time +- KODI::TIME::SystemTime time; +- KODI::TIME::GetLocalTime(&time); ++ auto zone = date::make_zoned(date::current_zone(), std::chrono::system_clock::now()); + +- return CDateTime(time); ++ return CDateTime( ++ std::chrono::duration_cast(zone.get_local_time().time_since_epoch()) ++ .count()); + } + + CDateTime CDateTime::GetUTCDateTime() + { +- CDateTime time(GetCurrentDateTime()); +- time += GetTimezoneBias(); +- return time; ++ return CDateTime(std::chrono::system_clock::now()); + } + + const CDateTime& CDateTime::operator=(const KODI::TIME::SystemTime& right) + { +- m_state = ToFileTime(right, m_time) ? valid : invalid; ++ KODI::TIME::FileTime fileTime; ++ m_state = ToFileTime(right, fileTime) ? valid : invalid; ++ ++ time_t time; ++ KODI::TIME::FileTimeToTimeT(&fileTime, &time); ++ m_time = std::chrono::system_clock::from_time_t(time); + + return *this; + } + + const CDateTime& CDateTime::operator=(const KODI::TIME::FileTime& right) + { +- m_time=right; +- SetValid(true); ++ time_t time; ++ KODI::TIME::FileTimeToTimeT(&right, &time); ++ m_time = std::chrono::system_clock::from_time_t(time); + + return *this; + } + + const CDateTime& CDateTime::operator =(const time_t& right) + { +- m_state = ToFileTime(right, m_time) ? valid : invalid; ++ m_time = std::chrono::system_clock::from_time_t(right); ++ SetValid(true); + + return *this; + } + + const CDateTime& CDateTime::operator =(const tm& right) + { +- m_state = ToFileTime(right, m_time) ? valid : invalid; ++ m_time = std::chrono::system_clock::from_time_t(std::mktime(const_cast(&right))); ++ SetValid(true); ++ ++ return *this; ++} ++ ++const CDateTime& CDateTime::operator=(const std::chrono::system_clock::time_point& right) ++{ ++ m_time = right; ++ SetValid(true); + + return *this; + } + + bool CDateTime::operator >(const CDateTime& right) const + { +- return operator >(right.m_time); ++ return m_time > right.m_time; + } + + bool CDateTime::operator >=(const CDateTime& right) const +@@ -331,7 +287,7 @@ bool CDateTime::operator >=(const CDateTime& right) const + + bool CDateTime::operator <(const CDateTime& right) const + { +- return operator <(right.m_time); ++ return m_time < right.m_time; + } + + bool CDateTime::operator <=(const CDateTime& right) const +@@ -341,7 +297,7 @@ bool CDateTime::operator <=(const CDateTime& right) const + + bool CDateTime::operator ==(const CDateTime& right) const + { +- return operator ==(right.m_time); ++ return m_time == right.m_time; + } + + bool CDateTime::operator !=(const CDateTime& right) const +@@ -351,7 +307,10 @@ bool CDateTime::operator !=(const CDateTime& right) const + + bool CDateTime::operator>(const KODI::TIME::FileTime& right) const + { +- return KODI::TIME::CompareFileTime(&m_time, &right) > 0; ++ time_t time; ++ KODI::TIME::FileTimeToTimeT(&right, &time); ++ ++ return m_time > std::chrono::system_clock::from_time_t(time); + } + + bool CDateTime::operator>=(const KODI::TIME::FileTime& right) const +@@ -361,7 +320,10 @@ bool CDateTime::operator>=(const KODI::TIME::FileTime& right) const + + bool CDateTime::operator<(const KODI::TIME::FileTime& right) const + { +- return KODI::TIME::CompareFileTime(&m_time, &right) < 0; ++ time_t time; ++ KODI::TIME::FileTimeToTimeT(&right, &time); ++ ++ return m_time < std::chrono::system_clock::from_time_t(time); + } + + bool CDateTime::operator<=(const KODI::TIME::FileTime& right) const +@@ -371,7 +333,10 @@ bool CDateTime::operator<=(const KODI::TIME::FileTime& right) const + + bool CDateTime::operator==(const KODI::TIME::FileTime& right) const + { +- return KODI::TIME::CompareFileTime(&m_time, &right) == 0; ++ time_t time; ++ KODI::TIME::FileTimeToTimeT(&right, &time); ++ ++ return m_time == std::chrono::system_clock::from_time_t(time); + } + + bool CDateTime::operator!=(const KODI::TIME::FileTime& right) const +@@ -420,10 +385,7 @@ bool CDateTime::operator!=(const KODI::TIME::SystemTime& right) const + + bool CDateTime::operator >(const time_t& right) const + { +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator >(time); ++ return m_time > std::chrono::system_clock::from_time_t(right); + } + + bool CDateTime::operator >=(const time_t& right) const +@@ -433,10 +395,7 @@ bool CDateTime::operator >=(const time_t& right) const + + bool CDateTime::operator <(const time_t& right) const + { +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator <(time); ++ return m_time < std::chrono::system_clock::from_time_t(right); + } + + bool CDateTime::operator <=(const time_t& right) const +@@ -446,10 +405,7 @@ bool CDateTime::operator <=(const time_t& right) const + + bool CDateTime::operator ==(const time_t& right) const + { +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator ==(time); ++ return m_time == std::chrono::system_clock::from_time_t(right); + } + + bool CDateTime::operator !=(const time_t& right) const +@@ -459,10 +415,7 @@ bool CDateTime::operator !=(const time_t& right) const + + bool CDateTime::operator >(const tm& right) const + { +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator >(time); ++ return m_time > std::chrono::system_clock::from_time_t(std::mktime(const_cast(&right))); + } + + bool CDateTime::operator >=(const tm& right) const +@@ -472,10 +425,7 @@ bool CDateTime::operator >=(const tm& right) const + + bool CDateTime::operator <(const tm& right) const + { +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator <(time); ++ return m_time < std::chrono::system_clock::from_time_t(std::mktime(const_cast(&right))); + } + + bool CDateTime::operator <=(const tm& right) const +@@ -485,10 +435,7 @@ bool CDateTime::operator <=(const tm& right) const + + bool CDateTime::operator ==(const tm& right) const + { +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator ==(time); ++ return m_time == std::chrono::system_clock::from_time_t(std::mktime(const_cast(&right))); + } + + bool CDateTime::operator !=(const tm& right) const +@@ -496,66 +443,64 @@ bool CDateTime::operator !=(const tm& right) const + return !operator ==(right); + } + +-CDateTime CDateTime::operator +(const CDateTimeSpan& right) const ++bool CDateTime::operator>(const std::chrono::system_clock::time_point& right) const + { +- CDateTime left(*this); ++ return m_time > right; ++} + +- LARGE_INTEGER timeLeft; +- left.ToLargeInt(timeLeft); ++bool CDateTime::operator>=(const std::chrono::system_clock::time_point& right) const ++{ ++ return operator>(right) || operator==(right); ++} + +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); ++bool CDateTime::operator<(const std::chrono::system_clock::time_point& right) const ++{ ++ return m_time < right; ++} + +- timeLeft.QuadPart+=timeRight.QuadPart; ++bool CDateTime::operator<=(const std::chrono::system_clock::time_point& right) const ++{ ++ return operator<(right) || operator==(right); ++} + +- left.FromLargeInt(timeLeft); ++bool CDateTime::operator==(const std::chrono::system_clock::time_point& right) const ++{ ++ return m_time == right; ++} + +- return left; ++bool CDateTime::operator!=(const std::chrono::system_clock::time_point& right) const ++{ ++ return !operator==(right); + } + +-CDateTime CDateTime::operator -(const CDateTimeSpan& right) const ++CDateTime CDateTime::operator+(const CDateTimeSpan& right) const + { + CDateTime left(*this); + +- LARGE_INTEGER timeLeft; +- left.ToLargeInt(timeLeft); ++ left.m_time + right.m_timeSpan; + +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); ++ return left; ++} + +- timeLeft.QuadPart-=timeRight.QuadPart; ++CDateTime CDateTime::operator-(const CDateTimeSpan& right) const ++{ ++ CDateTime left(*this); + +- left.FromLargeInt(timeLeft); ++ left.m_time - right.m_timeSpan; + + return left; + } + + const CDateTime& CDateTime::operator +=(const CDateTimeSpan& right) + { +- LARGE_INTEGER timeThis; +- ToLargeInt(timeThis); +- +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); +- +- timeThis.QuadPart+=timeRight.QuadPart; +- +- FromLargeInt(timeThis); ++ m_time += right.m_timeSpan; + + return *this; + } + + const CDateTime& CDateTime::operator -=(const CDateTimeSpan& right) + { +- LARGE_INTEGER timeThis; +- ToLargeInt(timeThis); +- +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); +- +- timeThis.QuadPart-=timeRight.QuadPart; +- +- FromLargeInt(timeThis); ++ m_time -= right.m_timeSpan; + + return *this; + } +@@ -564,25 +509,16 @@ CDateTimeSpan CDateTime::operator -(const CDateTime& right) const + { + CDateTimeSpan left; + +- LARGE_INTEGER timeLeft; +- left.ToLargeInt(timeLeft); +- +- LARGE_INTEGER timeThis; +- ToLargeInt(timeThis); +- +- LARGE_INTEGER timeRight; +- right.ToLargeInt(timeRight); +- +- timeLeft.QuadPart=timeThis.QuadPart-timeRight.QuadPart; +- +- left.FromLargeInt(timeLeft); +- ++ left.m_timeSpan = std::chrono::duration_cast(m_time - right.m_time); + return left; + } + + CDateTime::operator KODI::TIME::FileTime() const + { +- return m_time; ++ KODI::TIME::FileTime fileTime; ++ time_t time = std::chrono::system_clock::to_time_t(m_time); ++ KODI::TIME::TimeTToFileTime(time, &fileTime); ++ return fileTime; + } + + void CDateTime::Archive(CArchive& ar) +@@ -607,25 +543,38 @@ void CDateTime::Archive(CArchive& ar) + { + KODI::TIME::SystemTime st; + ar>>st; +- ToFileTime(st, m_time); ++ ToTimePoint(st, m_time); + } + } + } + + void CDateTime::Reset() + { +- SetDateTime(1601, 1, 1, 0, 0, 0); ++ m_time = {}; + SetValid(false); + } + + void CDateTime::SetValid(bool yesNo) + { +- m_state=yesNo ? valid : invalid; ++ m_state = yesNo ? valid : invalid; + } + + bool CDateTime::IsValid() const + { +- return m_state==valid; ++ return m_state == valid; ++} ++ ++bool CDateTime::ToTimePoint(const KODI::TIME::SystemTime& systemTime, ++ std::chrono::system_clock::time_point& timePoint) const ++{ ++ KODI::TIME::FileTime fileTime; ++ KODI::TIME::SystemTimeToFileTime(&systemTime, &fileTime); ++ ++ time_t time; ++ KODI::TIME::FileTimeToTimeT(&fileTime, &time); ++ ++ timePoint = std::chrono::system_clock::from_time_t(time); ++ return true; + } + + bool CDateTime::ToFileTime(const KODI::TIME::SystemTime& time, KODI::TIME::FileTime& fileTime) const +@@ -646,33 +595,6 @@ bool CDateTime::ToFileTime(const time_t& time, KODI::TIME::FileTime& fileTime) c + return true; + } + +-bool CDateTime::ToFileTime(const tm& time, KODI::TIME::FileTime& fileTime) const +-{ +- KODI::TIME::SystemTime st = {}; +- +- st.year = time.tm_year + 1900; +- st.month = time.tm_mon + 1; +- st.dayOfWeek = time.tm_wday; +- st.day = time.tm_mday; +- st.hour = time.tm_hour; +- st.minute = time.tm_min; +- st.second = time.tm_sec; +- +- return SystemTimeToFileTime(&st, &fileTime) == 1; +-} +- +-void CDateTime::ToLargeInt(LARGE_INTEGER& time) const +-{ +- time.u.HighPart = m_time.highDateTime; +- time.u.LowPart = m_time.lowDateTime; +-} +- +-void CDateTime::FromLargeInt(const LARGE_INTEGER& time) +-{ +- m_time.highDateTime = time.u.HighPart; +- m_time.lowDateTime = time.u.LowPart; +-} +- + bool CDateTime::SetFromDateString(const std::string &date) + { + //! @todo STRING_CLEANUP +@@ -714,80 +636,83 @@ bool CDateTime::SetFromDateString(const std::string &date) + + int CDateTime::GetDay() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto dp = date::floor(m_time); ++ auto ymd = date::year_month_day{dp}; + +- return st.day; ++ return static_cast(ymd.day()); + } + + int CDateTime::GetMonth() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto dp = date::floor(m_time); ++ auto ymd = date::year_month_day{dp}; + +- return st.month; ++ return static_cast(ymd.month()); + } + + int CDateTime::GetYear() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto dp = date::floor(m_time); ++ auto ymd = date::year_month_day{dp}; + +- return st.year; ++ return static_cast(ymd.year()); + } + + int CDateTime::GetHour() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto dp = date::floor(m_time); ++ auto time = date::make_time(m_time - dp); + +- return st.hour; ++ return time.hours().count(); + } + + int CDateTime::GetMinute() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto dp = date::floor(m_time); ++ auto time = date::make_time(m_time - dp); + +- return st.minute; ++ return time.minutes().count(); + } + + int CDateTime::GetSecond() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto dp = date::floor(m_time); ++ auto time = date::make_time(m_time - dp); + +- return st.second; ++ return time.seconds().count(); + } + + int CDateTime::GetDayOfWeek() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto dp = date::floor(m_time); ++ auto yww = iso_week::year_weeknum_weekday{dp}; + +- return st.dayOfWeek; ++ return static_cast(yww.weekday()); + } + + int CDateTime::GetMinuteOfDay() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); +- return st.hour * 60 + st.minute; ++ auto dp = date::floor(m_time); ++ ; ++ auto time = date::make_time(m_time - dp); ++ ++ return time.hours().count() * 60 + time.minutes().count(); + } + + bool CDateTime::SetDateTime(int year, int month, int day, int hour, int minute, int second) + { +- KODI::TIME::SystemTime st = {}; ++ auto ymd = date::year(year) / month / day; ++ if (!ymd.ok()) ++ { ++ SetValid(false); ++ return false; ++ } + +- st.year = year; +- st.month = month; +- st.day = day; +- st.hour = hour; +- st.minute = minute; +- st.second = second; ++ m_time = date::sys_days(ymd) + std::chrono::hours(hour) + std::chrono::minutes(minute) + ++ std::chrono::seconds(second); + +- m_state = ToFileTime(st, m_time) ? valid : invalid; +- return m_state == valid; ++ SetValid(true); ++ return true; + } + + bool CDateTime::SetDate(int year, int month, int day) +@@ -797,119 +722,75 @@ bool CDateTime::SetDate(int year, int month, int day) + + bool CDateTime::SetTime(int hour, int minute, int second) + { +- // 01.01.1601 00:00:00 is 0 as filetime +- return SetDateTime(1601, 1, 1, hour, minute, second); ++ m_time = date::sys_seconds(std::chrono::seconds(0)) + std::chrono::hours(hour) + ++ std::chrono::minutes(minute) + std::chrono::seconds(second); ++ ++ SetValid(true); ++ return true; + } + +-void CDateTime::GetAsSystemTime(KODI::TIME::SystemTime& time) const ++void CDateTime::GetAsSystemTime(KODI::TIME::SystemTime& systemTime) const + { +- FileTimeToSystemTime(&m_time, &time); ++ const time_t time = std::chrono::system_clock::to_time_t(m_time); ++ KODI::TIME::FileTime fileTime; ++ ToFileTime(time, fileTime); ++ KODI::TIME::FileTimeToSystemTime(&fileTime, &systemTime); + } + +-#define UNIX_BASE_TIME 116444736000000000LL /* nanoseconds since epoch */ + void CDateTime::GetAsTime(time_t& time) const + { +- long long ll = (static_cast(m_time.highDateTime) << 32) + m_time.lowDateTime; +- time=(time_t)((ll - UNIX_BASE_TIME) / 10000000); ++ time = std::chrono::system_clock::to_time_t(m_time); + } + + void CDateTime::GetAsTm(tm& time) const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto t = std::chrono::system_clock::to_time_t(m_time); + + time = {}; +- time.tm_year = st.year - 1900; +- time.tm_mon = st.month - 1; +- time.tm_wday = st.dayOfWeek; +- time.tm_mday = st.day; +- time.tm_hour = st.hour; +- time.tm_min = st.minute; +- time.tm_sec = st.second; +- time.tm_isdst = -1; +- +- mktime(&time); ++ localtime_r(&t, &time); + } + +-void CDateTime::GetAsTimeStamp(KODI::TIME::FileTime& time) const ++std::chrono::system_clock::time_point CDateTime::GetAsTimePoint() const + { +- KODI::TIME::LocalFileTimeToFileTime(&m_time, &time); ++ return m_time; + } + ++// void CDateTime::GetAsTimeStamp(KODI::TIME::FileTime& fileTime) const ++// { ++// time_t time = std::chrono::system_clock::to_time_t(m_time); ++// KODI::TIME::TimeTToFileTime(time, &fileTime); ++// } ++ + std::string CDateTime::GetAsDBDate() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); +- +- return StringUtils::Format("{:04}-{:02}-{:02}", st.year, st.month, st.day); ++ return date::format("%F", m_time); + } + + std::string CDateTime::GetAsDBTime() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); +- +- return StringUtils::Format("{:02}:{:02}:{:02}", st.hour, st.minute, st.second); ++ auto sp = date::floor(m_time); ++ return date::format("%T", sp); + } + + std::string CDateTime::GetAsDBDateTime() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto sp = date::floor(m_time); + +- return StringUtils::Format("{:04}-{:02}-{:02} {:02}:{:02}:{:02}", st.year, st.month, st.day, +- st.hour, st.minute, st.second); ++ return date::format("%F %T", sp); + } + + std::string CDateTime::GetAsSaveString() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); ++ auto sp = date::floor(m_time); + +- return StringUtils::Format("{:04}{:02}{:02}_{:02}{:02}{:02}", st.year, st.month, st.day, st.hour, +- st.minute, st.second); ++ return date::format("%Y%m%d_%H%M%S", sp); + } + + bool CDateTime::SetFromUTCDateTime(const CDateTime &dateTime) + { +- CDateTime tmp(dateTime); +- tmp -= GetTimezoneBias(); +- +- m_time = tmp.m_time; +- m_state = tmp.m_state; +- return m_state == valid; +-} +- +-static bool bGotTimezoneBias = false; +- +-void CDateTime::ResetTimezoneBias(void) +-{ +- bGotTimezoneBias = false; +-} +- +-CDateTimeSpan CDateTime::GetTimezoneBias(void) +-{ +- static CDateTimeSpan timezoneBias; +- +- if (!bGotTimezoneBias) +- { +- bGotTimezoneBias = true; +- KODI::TIME::TimeZoneInformation tz; +- switch (KODI::TIME::GetTimeZoneInformation(&tz)) +- { +- case KODI::TIME::KODI_TIME_ZONE_ID_DAYLIGHT: +- timezoneBias = CDateTimeSpan(0, 0, tz.bias + tz.daylightBias, 0); +- break; +- case KODI::TIME::KODI_TIME_ZONE_ID_STANDARD: +- timezoneBias = CDateTimeSpan(0, 0, tz.bias + tz.standardBias, 0); +- break; +- case KODI::TIME::KODI_TIME_ZONE_ID_UNKNOWN: +- timezoneBias = CDateTimeSpan(0, 0, tz.bias, 0); +- break; +- } +- } +- +- return timezoneBias; ++ m_time = dateTime.m_time; ++ m_state = valid; ++ return true; + } + + bool CDateTime::SetFromUTCDateTime(const time_t &dateTime) +@@ -1511,60 +1392,31 @@ std::string CDateTime::GetAsLocalizedTime(TIME_FORMAT format, bool withSeconds / + CDateTime CDateTime::GetAsUTCDateTime() const + { + CDateTime time(m_time); +- time += GetTimezoneBias(); + return time; + } + + std::string CDateTime::GetAsRFC1123DateTime() const + { +- CDateTime time(GetAsUTCDateTime()); ++ auto time = date::floor(m_time); + +- int weekDay = time.GetDayOfWeek(); +- if (weekDay < 0) +- weekDay = 0; +- else if (weekDay > 6) +- weekDay = 6; +- if (weekDay != time.GetDayOfWeek()) +- CLog::Log(LOGWARNING, "Invalid day of week {} in {}", time.GetDayOfWeek(), +- time.GetAsDBDateTime()); +- +- int month = time.GetMonth(); +- if (month < 1) +- month = 1; +- else if (month > 12) +- month = 12; +- if (month != time.GetMonth()) +- CLog::Log(LOGWARNING, "Invalid month {} in {}", time.GetMonth(), time.GetAsDBDateTime()); +- +- return StringUtils::Format("{}, {:02} {} {:04} {:02}:{:02}:{:02} GMT", DAY_NAMES[weekDay], +- time.GetDay(), MONTH_NAMES[month - 1], time.GetYear(), time.GetHour(), +- time.GetMinute(), time.GetSecond()); ++ return date::format("%a, %d %b %Y %T GMT", time); + } + + std::string CDateTime::GetAsW3CDate() const + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); +- +- return StringUtils::Format("{:04}-{:02}-{:02}", st.year, st.month, st.day); ++ return GetAsDBDate(); + } + + std::string CDateTime::GetAsW3CDateTime(bool asUtc /* = false */) const + { +- CDateTime w3cDate = *this; +- if (asUtc) +- w3cDate = GetAsUTCDateTime(); +- KODI::TIME::SystemTime st; +- w3cDate.GetAsSystemTime(st); ++ auto time = date::floor(m_time); + +- std::string result = StringUtils::Format("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}", st.year, st.month, +- st.day, st.hour, st.minute, st.second); + if (asUtc) +- return result + "Z"; ++ return date::format("%FT%TZ", time); ++ ++ auto zt = date::make_zoned(date::current_zone(), time); + +- CDateTimeSpan bias = GetTimezoneBias(); +- return result + StringUtils::Format("{}{:02}:{:02}", (bias.GetSecondsTotal() >= 0 ? '+' : '-'), +- abs(bias.GetHours()), abs(bias.GetMinutes())); ++ return date::format("%FT%T%Ez", zt); + } + + int CDateTime::MonthStringToMonthNum(const std::string& month) +diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h +index fb18e7c0de..60d06e417a 100644 +--- a/xbmc/XBDateTime.h ++++ b/xbmc/XBDateTime.h +@@ -12,6 +12,7 @@ + #include "utils/TimeFormat.h" + #include "utils/XTimeUtils.h" + ++#include + #include + + #include "PlatformDefs.h" +@@ -21,7 +22,7 @@ class CDateTime; + class CDateTimeSpan + { + public: +- CDateTimeSpan(); ++ CDateTimeSpan() = default; + CDateTimeSpan(const CDateTimeSpan& span); + CDateTimeSpan& operator=(const CDateTimeSpan&) = default; + CDateTimeSpan(int day, int hour, int minute, int second); +@@ -50,11 +51,7 @@ public: + int GetSecondsTotal() const; + + private: +- void ToLargeInt(LARGE_INTEGER& time) const; +- void FromLargeInt(const LARGE_INTEGER& time); +- +-private: +- KODI::TIME::FileTime m_timeSpan; ++ std::chrono::duration m_timeSpan{0}; + + friend class CDateTime; + }; +@@ -70,6 +67,7 @@ public: + explicit CDateTime(const KODI::TIME::FileTime& time); + explicit CDateTime(const time_t& time); + explicit CDateTime(const tm& time); ++ explicit CDateTime(const std::chrono::system_clock::time_point& time); + CDateTime(int year, int month, int day, int hour, int minute, int second); + + static CDateTime GetCurrentDateTime(); +@@ -90,6 +88,7 @@ public: + const CDateTime& operator=(const KODI::TIME::FileTime& right); + const CDateTime& operator =(const time_t& right); + const CDateTime& operator =(const tm& right); ++ const CDateTime& operator=(const std::chrono::system_clock::time_point& right); + + bool operator >(const CDateTime& right) const; + bool operator >=(const CDateTime& right) const; +@@ -126,6 +125,13 @@ public: + bool operator ==(const tm& right) const; + bool operator !=(const tm& right) const; + ++ bool operator>(const std::chrono::system_clock::time_point& right) const; ++ bool operator>=(const std::chrono::system_clock::time_point& right) const; ++ bool operator<(const std::chrono::system_clock::time_point& right) const; ++ bool operator<=(const std::chrono::system_clock::time_point& right) const; ++ bool operator==(const std::chrono::system_clock::time_point& right) const; ++ bool operator!=(const std::chrono::system_clock::time_point& right) const; ++ + CDateTime operator +(const CDateTimeSpan& right) const; + CDateTime operator -(const CDateTimeSpan& right) const; + +@@ -170,7 +176,7 @@ public: + void GetAsSystemTime(KODI::TIME::SystemTime& time) const; + void GetAsTime(time_t& time) const; + void GetAsTm(tm& time) const; +- void GetAsTimeStamp(KODI::TIME::FileTime& time) const; ++ std::chrono::system_clock::time_point GetAsTimePoint() const; + + CDateTime GetAsUTCDateTime() const; + std::string GetAsSaveString() const; +@@ -189,19 +195,14 @@ public: + void SetValid(bool yesNo); + bool IsValid() const; + +- static void ResetTimezoneBias(void); +- static CDateTimeSpan GetTimezoneBias(void); +- + private: ++ bool ToTimePoint(const KODI::TIME::SystemTime& time, ++ std::chrono::system_clock::time_point& timePoint) const; + bool ToFileTime(const KODI::TIME::SystemTime& time, KODI::TIME::FileTime& fileTime) const; + bool ToFileTime(const time_t& time, KODI::TIME::FileTime& fileTime) const; +- bool ToFileTime(const tm& time, KODI::TIME::FileTime& fileTime) const; +- +- void ToLargeInt(LARGE_INTEGER& time) const; +- void FromLargeInt(const LARGE_INTEGER& time); + + private: +- KODI::TIME::FileTime m_time; ++ std::chrono::system_clock::time_point m_time; + + typedef enum _STATE + { +diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp +index 6f276a37b2..ab2ddcf570 100644 +--- a/xbmc/platform/posix/PosixTimezone.cpp ++++ b/xbmc/platform/posix/PosixTimezone.cpp +@@ -143,8 +143,6 @@ void CPosixTimezone::OnSettingChanged(const std::shared_ptr& set + if (settingId == CSettings::SETTING_LOCALE_TIMEZONE) + { + SetTimezone(std::static_pointer_cast(setting)->GetValue()); +- +- CDateTime::ResetTimezoneBias(); + } + else if (settingId == CSettings::SETTING_LOCALE_TIMEZONECOUNTRY) + { +@@ -157,7 +155,6 @@ void CPosixTimezone::OnSettingChanged(const std::shared_ptr& set + void CPosixTimezone::OnSettingsLoaded() + { + SetTimezone(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONE)); +- CDateTime::ResetTimezoneBias(); + } + + std::vector CPosixTimezone::GetCounties() +diff --git a/xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp b/xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp +index 53bbf815f6..4ae42a4803 100644 +--- a/xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp ++++ b/xbmc/pvr/guilib/PVRGUIActionsDatabase.cpp +@@ -205,8 +205,6 @@ bool CPVRGUIActionsDatabase::ResetDatabase(bool bResetEPGOnly) + bResetClients = selector.IsResetClientsSelected(); + } + +- CDateTime::ResetTimezoneBias(); +- + CLog::LogFC(LOGDEBUG, LOGPVR, "PVR clearing {} database", bResetEPGOnly ? "EPG" : "PVR and EPG"); + + pDlgProgress->SetHeading(CVariant{313}); // "Cleaning database" +-- +2.43.0 + + +From 8433756427c68642013698c678263b4d6ee5759d Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Mon, 17 Feb 2020 22:40:56 -0800 +Subject: [PATCH 04/19] fix TestDateTime to work with std::chrono + +--- + xbmc/test/TestDateTime.cpp | 89 ++++++++------------------------------ + 1 file changed, 17 insertions(+), 72 deletions(-) + +diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp +index 7e51df8cee..a553bd1887 100644 +--- a/xbmc/test/TestDateTime.cpp ++++ b/xbmc/test/TestDateTime.cpp +@@ -13,6 +13,10 @@ + #include + #include + ++#define USE_OS_TZDB 0 ++#define HAS_REMOTE_API 0 ++#include ++#include + #include + + class TestDateTime : public testing::Test +@@ -32,24 +36,6 @@ TEST_F(TestDateTime, DateTimeOperators) + EXPECT_FALSE(dateTime1 == dateTime2); + } + +-TEST_F(TestDateTime, FileTimeOperators) +-{ +- CDateTime dateTime1(1991, 5, 14, 12, 34, 56); +- CDateTime dateTime2(1991, 5, 14, 12, 34, 57); +- +- KODI::TIME::FileTime fileTime1; +- KODI::TIME::FileTime fileTime2; +- +- dateTime1.GetAsTimeStamp(fileTime1); +- dateTime2.GetAsTimeStamp(fileTime2); +- +- CDateTime dateTime3(fileTime1); +- +- EXPECT_TRUE(dateTime3 < fileTime2); +- EXPECT_FALSE(dateTime3 > fileTime2); +- EXPECT_FALSE(dateTime3 == fileTime2); +-} +- + TEST_F(TestDateTime, SystemTimeOperators) + { + CDateTime dateTime1(1991, 5, 14, 12, 34, 56); +@@ -161,7 +147,7 @@ TEST_F(TestDateTime, MonthStringToMonthNum) + } + + // this method is broken as SetFromDBDate() will return true +-TEST_F(TestDateTime, DISABLED_SetFromDateString) ++TEST_F(TestDateTime, SetFromDateString) + { + CDateTime dateTime; + EXPECT_TRUE(dateTime.SetFromDateString("tuesday may 14, 1991")); +@@ -190,13 +176,7 @@ TEST_F(TestDateTime, SetFromDBDate) + EXPECT_EQ(dateTime.GetDay(), 2); + } + +-// disabled on osx and freebsd as their mktime functions +-// don't work for dates before 1900 +-#if defined(TARGET_DARWIN_OSX) || defined(TARGET_FREEBSD) +-TEST_F(TestDateTime, DISABLED_SetFromDBTime) +-#else + TEST_F(TestDateTime, SetFromDBTime) +-#endif + { + CDateTime dateTime1; + EXPECT_TRUE(dateTime1.SetFromDBTime("12:34")); +@@ -237,10 +217,8 @@ TEST_F(TestDateTime, SetFromW3CDate) + + TEST_F(TestDateTime, SetFromW3CDateTime) + { +- CDateTimeSpan bias = CDateTime::GetTimezoneBias(); + CDateTime dateTime; + dateTime.SetFromDBDateTime("1994-11-05 13:15:30"); +- dateTime += bias; + std::string dateTimeStr = dateTime.GetAsDBDate() + "T" + dateTime.GetAsDBTime() + "Z"; + + CDateTime dateTime1; +@@ -264,11 +242,8 @@ TEST_F(TestDateTime, SetFromW3CDateTime) + + TEST_F(TestDateTime, SetFromUTCDateTime) + { +- CDateTimeSpan bias = CDateTime::GetTimezoneBias(); +- + CDateTime dateTime1; + dateTime1.SetFromDBDateTime("1991-05-14 12:34:56"); +- dateTime1 += bias; + + CDateTime dateTime2; + EXPECT_TRUE(dateTime2.SetFromUTCDateTime(dateTime1)); +@@ -279,7 +254,7 @@ TEST_F(TestDateTime, SetFromUTCDateTime) + EXPECT_EQ(dateTime2.GetMinute(), 34); + EXPECT_EQ(dateTime2.GetSecond(), 56); + +- const time_t time = 674224496 + bias.GetSecondsTotal(); ++ const time_t time = 674224496; + + CDateTime dateTime3; + EXPECT_TRUE(dateTime3.SetFromUTCDateTime(time)); +@@ -325,18 +300,14 @@ TEST_F(TestDateTime, SetDateTime) + EXPECT_EQ(dateTime2.GetMinute(), 0); + EXPECT_EQ(dateTime2.GetSecond(), 0); + +-// disabled on osx and freebsd as their mktime functions +-// don't work for dates before 1900 +-#if !defined(TARGET_DARWIN_OSX) && !defined(TARGET_FREEBSD) + CDateTime dateTime3; + EXPECT_TRUE(dateTime3.SetTime(12, 34, 56)); +- EXPECT_EQ(dateTime3.GetYear(), 1601); ++ EXPECT_EQ(dateTime3.GetYear(), 1970); + EXPECT_EQ(dateTime3.GetMonth(), 1); + EXPECT_EQ(dateTime3.GetDay(), 1); + EXPECT_EQ(dateTime3.GetHour(), 12); + EXPECT_EQ(dateTime3.GetMinute(), 34); + EXPECT_EQ(dateTime3.GetSecond(), 56); +-#endif + } + + TEST_F(TestDateTime, GetAsStrings) +@@ -351,22 +322,20 @@ TEST_F(TestDateTime, GetAsStrings) + EXPECT_EQ(dateTime.GetAsW3CDate(), "1991-05-14"); + } + +-// disabled because we have no way to validate these values +-// GetTimezoneBias() always returns a positive value so +-// there is no way to detect the direction of the offset +-TEST_F(TestDateTime, DISABLED_GetAsStringsWithBias) ++TEST_F(TestDateTime, GetAsStringsWithBias) + { +- CDateTimeSpan bias = CDateTime::GetTimezoneBias(); +- + CDateTime dateTime; + dateTime.SetDateTime(1991, 05, 14, 12, 34, 56); + +- CDateTime dateTimeWithBias(dateTime); +- dateTimeWithBias += bias; ++ std::cout << dateTime.GetAsRFC1123DateTime() << std::endl; ++ std::cout << dateTime.GetAsW3CDateTime(false) << std::endl; ++ std::cout << dateTime.GetAsW3CDateTime(true) << std::endl; + +- EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 20:34:56 GMT"); +- EXPECT_EQ(dateTime.GetAsW3CDateTime(false), "1991-05-14T12:34:56+08:00"); +- EXPECT_EQ(dateTime.GetAsW3CDateTime(true), "1991-05-14T20:34:56Z"); ++ auto zone = date::make_zoned(date::current_zone(), dateTime.GetAsTimePoint()); ++ ++ EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 12:34:56 GMT"); ++ EXPECT_EQ(dateTime.GetAsW3CDateTime(false), "1991-05-14T05:34:56" + date::format("%Ez", zone)); ++ EXPECT_EQ(dateTime.GetAsW3CDateTime(true), "1991-05-14T12:34:56Z"); + } + + TEST_F(TestDateTime, GetAsLocalized) +@@ -604,31 +573,13 @@ TEST_F(TestDateTime, GetAsTm) + } + } + +-// Disabled pending std::chrono and std::date changes. +-TEST_F(TestDateTime, DISABLED_GetAsTimeStamp) +-{ +- CDateTimeSpan bias = CDateTime::GetTimezoneBias(); +- +- CDateTime dateTime; +- dateTime.SetDateTime(1991, 05, 14, 12, 34, 56); +- +- KODI::TIME::FileTime fileTime; +- dateTime.GetAsTimeStamp(fileTime); +- dateTime += bias; +- +- EXPECT_TRUE(dateTime == fileTime); +-} +- + TEST_F(TestDateTime, GetAsUTCDateTime) + { +- CDateTimeSpan bias = CDateTime::GetTimezoneBias(); +- + CDateTime dateTime1; + dateTime1.SetDateTime(1991, 05, 14, 12, 34, 56); + + CDateTime dateTime2; + dateTime2 = dateTime1.GetAsUTCDateTime(); +- dateTime2 -= bias; + + EXPECT_EQ(dateTime2.GetYear(), 1991); + EXPECT_EQ(dateTime2.GetMonth(), 5); +@@ -638,20 +589,14 @@ TEST_F(TestDateTime, GetAsUTCDateTime) + EXPECT_EQ(dateTime2.GetSecond(), 56); + } + +-// disabled on osx and freebsd as their mktime functions +-// don't work for dates before 1900 +-#if defined(TARGET_DARWIN_OSX) || defined(TARGET_FREEBSD) +-TEST_F(TestDateTime, DISABLED_Reset) +-#else + TEST_F(TestDateTime, Reset) +-#endif + { + CDateTime dateTime; + dateTime.SetDateTime(1991, 05, 14, 12, 34, 56); + + dateTime.Reset(); + +- EXPECT_EQ(dateTime.GetYear(), 1601); ++ EXPECT_EQ(dateTime.GetYear(), 1970); + EXPECT_EQ(dateTime.GetMonth(), 1); + EXPECT_EQ(dateTime.GetDay(), 1); + EXPECT_EQ(dateTime.GetHour(), 0); +-- +2.43.0 + + +From ca3e502a5c37975173e376653a167053928b5311 Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Thu, 10 Sep 2020 08:19:35 -0700 +Subject: [PATCH 05/19] cleanup CPosixTimezone + +--- + xbmc/application/Application.cpp | 6 +- + xbmc/platform/posix/PosixTimezone.cpp | 418 ++++++++++++++++---------- + xbmc/platform/posix/PosixTimezone.h | 16 +- + 3 files changed, 266 insertions(+), 174 deletions(-) + +diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp +index ea1b152dfd..3227933296 100644 +--- a/xbmc/application/Application.cpp ++++ b/xbmc/application/Application.cpp +@@ -172,8 +172,9 @@ + #include "utils/URIUtils.h" + + #ifdef TARGET_POSIX +-#include "platform/posix/XHandle.h" + #include "platform/posix/PlatformPosix.h" ++#include "platform/posix/PosixTimezone.h" ++#include "platform/posix/XHandle.h" + #endif + + #if defined(TARGET_ANDROID) +@@ -358,6 +359,9 @@ bool CApplication::Create() + // Register JobManager service + CServiceBroker::RegisterJobManager(std::make_shared()); + ++ // Initialize,timezone ++ g_timezone.Init(); ++ + // Announcement service + m_pAnnouncementManager = std::make_shared(); + m_pAnnouncementManager->Start(); +diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp +index ab2ddcf570..7f0b8d4096 100644 +--- a/xbmc/platform/posix/PosixTimezone.cpp ++++ b/xbmc/platform/posix/PosixTimezone.cpp +@@ -6,7 +6,6 @@ + * See LICENSES/README.md for more information. + */ + +-#include + #ifdef TARGET_ANDROID + #include "platform/android/bionic_supplement/bionic_supplement.h" + #endif +@@ -23,120 +22,215 @@ + #include "settings/SettingsComponent.h" + #include + +-#include ++#define USE_OS_TZDB 0 ++#define HAS_REMOTE_API 0 ++#include "filesystem/File.h" ++#include "platform/MessagePrinter.h" + +-CPosixTimezone::CPosixTimezone() ++#include ++ ++void CPosixTimezone::Init() + { +- char* line = NULL; +- size_t linelen = 0; +- int nameonfourthfield = 0; +- std::string s; +- std::vector tokens; +- +- // Load timezones +- FILE* fp = fopen("/usr/share/zoneinfo/zone.tab", "r"); +- if (fp) +- { +- std::string countryCode; +- std::string timezoneName; +- +- while (getdelim(&line, &linelen, '\n', fp) > 0) ++#if defined(DATE_INTERNAL_TZDATA) ++ XFILE::CFileStream zonetab; ++ ++ if (!zonetab.Open("special://xbmc/addons/resource.timezone/resources/tzdata/zone.tab")) ++ { ++ CMessagePrinter::DisplayMessage("failed to open zone.tab"); ++ return; ++ } ++ ++ std::string countryCode; ++ std::string timezoneName; ++ ++ std::vector tokens; ++ ++ for (std::string s; std::getline(zonetab, s);) ++ { ++ tokens.clear(); ++ std::string line = s; ++ StringUtils::Trim(line); ++ ++ if (line.length() == 0) ++ continue; ++ ++ if (line[0] == '#') ++ continue; ++ ++ StringUtils::Tokenize(line, tokens, " \t"); ++ if (tokens.size() < 3) ++ continue; ++ ++ countryCode = tokens[0]; ++ timezoneName = tokens[2]; ++ ++ if (m_timezonesByCountryCode.count(countryCode) == 0) ++ { ++ std::vector timezones; ++ timezones.push_back(timezoneName); ++ m_timezonesByCountryCode[countryCode] = timezones; ++ } ++ else ++ { ++ std::vector& timezones = m_timezonesByCountryCode[countryCode]; ++ timezones.push_back(timezoneName); ++ } ++ ++ m_countriesByTimezoneName[timezoneName] = countryCode; ++ } ++ ++ XFILE::CFileStream isotab; ++ ++ if (!isotab.Open("special://xbmc/addons/resource.timezone/resources/tzdata/iso3166.tab")) ++ { ++ CMessagePrinter::DisplayMessage("failed to open iso3166.tab"); ++ return; ++ } ++ ++ std::string countryName; ++ ++ for (std::string s; std::getline(isotab, s);) ++ { ++ tokens.clear(); ++ std::string line = s; ++ StringUtils::Trim(line); ++ ++ if (line.length() == 0) ++ continue; ++ ++ if (line[0] == '#') ++ continue; ++ ++ StringUtils::Tokenize(line, tokens, "\t"); ++ if (tokens.size() < 2) ++ continue; ++ ++ countryCode = tokens[0]; ++ countryName = tokens[1]; ++ ++ m_countries.push_back(countryName); ++ m_countryByCode[countryCode] = countryName; ++ m_countryByName[countryName] = countryCode; ++ } ++ ++ sort(m_countries.begin(), m_countries.end(), sortstringbyname()); ++#else ++ char* line = NULL; ++ size_t linelen = 0; ++ int nameonfourthfield = 0; ++ std::string s; ++ std::vector tokens; ++ ++ // Load timezones ++ FILE* fp = fopen("/usr/share/zoneinfo/zone.tab", "r"); ++ if (fp) ++ { ++ std::string countryCode; ++ std::string timezoneName; ++ ++ while (getdelim(&line, &linelen, '\n', fp) > 0) ++ { ++ tokens.clear(); ++ s = line; ++ StringUtils::Trim(s); ++ ++ if (s.length() == 0) ++ continue; ++ ++ if (s[0] == '#') ++ continue; ++ ++ StringUtils::Tokenize(s, tokens, " \t"); ++ if (tokens.size() < 3) ++ continue; ++ ++ countryCode = tokens[0]; ++ timezoneName = tokens[2]; ++ ++ if (m_timezonesByCountryCode.count(countryCode) == 0) ++ { ++ std::vector timezones; ++ timezones.push_back(timezoneName); ++ m_timezonesByCountryCode[countryCode] = timezones; ++ } ++ else + { +- tokens.clear(); +- s = line; +- StringUtils::Trim(s); +- +- if (s.length() == 0) +- continue; +- +- if (s[0] == '#') +- continue; +- +- StringUtils::Tokenize(s, tokens, " \t"); +- if (tokens.size() < 3) +- continue; +- +- countryCode = tokens[0]; +- timezoneName = tokens[2]; +- +- if (m_timezonesByCountryCode.count(countryCode) == 0) +- { +- std::vector timezones; +- timezones.push_back(timezoneName); +- m_timezonesByCountryCode[countryCode] = timezones; +- } +- else +- { +- std::vector& timezones = m_timezonesByCountryCode[countryCode]; +- timezones.push_back(timezoneName); +- } +- +- m_countriesByTimezoneName[timezoneName] = countryCode; ++ std::vector& timezones = m_timezonesByCountryCode[countryCode]; ++ timezones.push_back(timezoneName); + } +- fclose(fp); +- } +- +- if (line) +- { +- free(line); +- line = NULL; +- linelen = 0; +- } +- +- // Load countries +- fp = fopen("/usr/share/zoneinfo/iso3166.tab", "r"); +- if (!fp) +- { +- fp = fopen("/usr/share/misc/iso3166", "r"); +- nameonfourthfield = 1; +- } +- if (fp) +- { +- std::string countryCode; +- std::string countryName; +- +- while (getdelim(&line, &linelen, '\n', fp) > 0) ++ ++ m_countriesByTimezoneName[timezoneName] = countryCode; ++ } ++ fclose(fp); ++ } ++ ++ if (line) ++ { ++ free(line); ++ line = NULL; ++ linelen = 0; ++ } ++ ++ // Load countries ++ fp = fopen("/usr/share/zoneinfo/iso3166.tab", "r"); ++ if (!fp) ++ { ++ fp = fopen("/usr/share/misc/iso3166", "r"); ++ nameonfourthfield = 1; ++ } ++ if (fp) ++ { ++ std::string countryCode; ++ std::string countryName; ++ ++ while (getdelim(&line, &linelen, '\n', fp) > 0) ++ { ++ s = line; ++ StringUtils::Trim(s); ++ ++ //! @todo STRING_CLEANUP ++ if (s.length() == 0) ++ continue; ++ ++ if (s[0] == '#') ++ continue; ++ ++ // Search for the first non space from the 2nd character and on ++ int i = 2; ++ while (s[i] == ' ' || s[i] == '\t') ++ i++; ++ ++ if (nameonfourthfield) + { +- s = line; +- StringUtils::Trim(s); +- +- //! @todo STRING_CLEANUP +- if (s.length() == 0) +- continue; +- +- if (s[0] == '#') +- continue; +- +- // Search for the first non space from the 2nd character and on +- int i = 2; +- while (s[i] == ' ' || s[i] == '\t') i++; +- +- if (nameonfourthfield) +- { +- // skip three letter +- while (s[i] != ' ' && s[i] != '\t') i++; +- while (s[i] == ' ' || s[i] == '\t') i++; +- // skip number +- while (s[i] != ' ' && s[i] != '\t') i++; +- while (s[i] == ' ' || s[i] == '\t') i++; +- } +- +- countryCode = s.substr(0, 2); +- countryName = s.substr(i); +- +- m_counties.push_back(countryName); +- m_countryByCode[countryCode] = countryName; +- m_countryByName[countryName] = countryCode; ++ // skip three letter ++ while (s[i] != ' ' && s[i] != '\t') ++ i++; ++ while (s[i] == ' ' || s[i] == '\t') ++ i++; ++ // skip number ++ while (s[i] != ' ' && s[i] != '\t') ++ i++; ++ while (s[i] == ' ' || s[i] == '\t') ++ i++; + } +- sort(m_counties.begin(), m_counties.end(), sortstringbyname()); +- fclose(fp); +- } +- free(line); ++ ++ countryCode = s.substr(0, 2); ++ countryName = s.substr(i); ++ ++ m_countries.push_back(countryName); ++ m_countryByCode[countryCode] = countryName; ++ m_countryByName[countryName] = countryCode; ++ } ++ sort(m_countries.begin(), m_countries.end(), sortstringbyname()); ++ fclose(fp); ++ } ++ free(line); ++#endif + } + + void CPosixTimezone::OnSettingChanged(const std::shared_ptr& setting) + { +- if (setting == NULL) ++ if (setting == nullptr) + return; + + const std::string &settingId = setting->GetId(); +@@ -157,82 +251,74 @@ void CPosixTimezone::OnSettingsLoaded() + SetTimezone(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONE)); + } + +-std::vector CPosixTimezone::GetCounties() ++std::vector CPosixTimezone::GetCountries() + { +- return m_counties; ++ return m_countries; + } + + std::vector CPosixTimezone::GetTimezonesByCountry(const std::string& country) + { +- return m_timezonesByCountryCode[m_countryByName[country]]; ++ return m_timezonesByCountryCode[m_countryByName[country]]; + } + + std::string CPosixTimezone::GetCountryByTimezone(const std::string& timezone) + { + #if defined(TARGET_DARWIN) +- return "?"; +-#else +- return m_countryByCode[m_countriesByTimezoneName[timezone]]; ++ return "?"; + #endif ++ ++ return m_countryByCode[m_countriesByTimezoneName[timezone]]; + } + + void CPosixTimezone::SetTimezone(const std::string& timezoneName) + { +-#if !defined(TARGET_DARWIN) +- bool use_timezone = true; +-#else +- bool use_timezone = false; ++#if defined(TARGET_DARWIN) ++ return; + #endif + +- if (use_timezone) +- { +- static char env_var[255]; +- sprintf(env_var, "TZ=:%s", timezoneName.c_str()); +- putenv(env_var); +- tzset(); +- } ++ setenv("TZ", timezoneName.c_str(), 1); ++ tzset(); + } + + std::string CPosixTimezone::GetOSConfiguredTimezone() + { +- char timezoneName[255]; +- +- // try Slackware approach first +- ssize_t rlrc = readlink("/etc/localtime-copied-from" +- , timezoneName, sizeof(timezoneName)-1); +- +- // RHEL and maybe other distros make /etc/localtime a symlink +- if (rlrc == -1) +- rlrc = readlink("/etc/localtime", timezoneName, sizeof(timezoneName)-1); +- +- if (rlrc != -1) +- { +- timezoneName[rlrc] = '\0'; +- +- char* p = strrchr(timezoneName,'/'); +- if (p) +- { // we want the previous '/' +- char* q = p; +- *q = 0; +- p = strrchr(timezoneName,'/'); +- *q = '/'; +- if (p) +- p++; +- } +- return p; +- } +- +- // now try Debian approach +- timezoneName[0] = 0; +- FILE* fp = fopen("/etc/timezone", "r"); +- if (fp) +- { +- if (fgets(timezoneName, sizeof(timezoneName), fp)) +- timezoneName[strlen(timezoneName)-1] = '\0'; +- fclose(fp); +- } +- +- return timezoneName; ++ char timezoneName[255]; ++ ++ // try Slackware approach first ++ ssize_t rlrc = readlink("/etc/localtime-copied-from", timezoneName, sizeof(timezoneName) - 1); ++ ++ // RHEL and maybe other distros make /etc/localtime a symlink ++ if (rlrc == -1) ++ rlrc = readlink("/etc/localtime", timezoneName, sizeof(timezoneName) - 1); ++ ++ if (rlrc != -1) ++ { ++ timezoneName[rlrc] = '\0'; ++ ++ char* p = strrchr(timezoneName, '/'); ++ if (p) ++ { // we want the previous '/' ++ char* q = p; ++ *q = 0; ++ p = strrchr(timezoneName, '/'); ++ *q = '/'; ++ if (p) ++ p++; ++ } ++ return p; ++ } ++ ++ // now try Debian approach ++ timezoneName[0] = 0; ++ FILE* fp = fopen("/etc/timezone", "r"); ++ if (fp) ++ { ++ if (fgets(timezoneName, sizeof(timezoneName), fp)) ++ timezoneName[strlen(timezoneName) - 1] = '\0'; ++ fclose(fp); ++ } ++ ++ return timezoneName; + } + + void CPosixTimezone::SettingOptionsTimezoneCountriesFiller( +@@ -241,9 +327,9 @@ void CPosixTimezone::SettingOptionsTimezoneCountriesFiller( + std::string& current, + void* data) + { +- std::vector countries = g_timezone.GetCounties(); +- for (unsigned int i = 0; i < countries.size(); i++) +- list.emplace_back(countries[i], countries[i]); ++ std::vector countries = g_timezone.GetCountries(); ++ for (const auto& country : countries) ++ list.emplace_back(country, country); + } + + void CPosixTimezone::SettingOptionsTimezonesFiller(const std::shared_ptr& setting, +@@ -254,12 +340,12 @@ void CPosixTimezone::SettingOptionsTimezonesFiller(const std::shared_ptr(setting)->GetValue(); + bool found = false; + std::vector timezones = g_timezone.GetTimezonesByCountry(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONECOUNTRY)); +- for (unsigned int i = 0; i < timezones.size(); i++) ++ for (const auto& timezone : timezones) + { +- if (!found && StringUtils::EqualsNoCase(timezones[i], current)) ++ if (!found && StringUtils::EqualsNoCase(timezone, current)) + found = true; + +- list.emplace_back(timezones[i], timezones[i]); ++ list.emplace_back(timezone, timezone); + } + + if (!found && !timezones.empty()) +diff --git a/xbmc/platform/posix/PosixTimezone.h b/xbmc/platform/posix/PosixTimezone.h +index 076e87fa51..238c6daf85 100644 +--- a/xbmc/platform/posix/PosixTimezone.h ++++ b/xbmc/platform/posix/PosixTimezone.h +@@ -21,7 +21,9 @@ struct StringSettingOption; + class CPosixTimezone : public ISettingCallback, public ISettingsHandler + { + public: +- CPosixTimezone(); ++ CPosixTimezone() = default; ++ ++ void Init(); + + void OnSettingChanged(const std::shared_ptr& setting) override; + +@@ -29,7 +31,7 @@ public: + + std::string GetOSConfiguredTimezone(); + +- std::vector GetCounties(); ++ std::vector GetCountries(); + std::vector GetTimezonesByCountry(const std::string& country); + std::string GetCountryByTimezone(const std::string& timezone); + +@@ -46,12 +48,12 @@ public: + void* data); + + private: +- std::vector m_counties; +- std::map m_countryByCode; +- std::map m_countryByName; ++ std::vector m_countries; ++ std::map m_countryByCode; ++ std::map m_countryByName; + +- std::map > m_timezonesByCountryCode; +- std::map m_countriesByTimezoneName; ++ std::map> m_timezonesByCountryCode; ++ std::map m_countriesByTimezoneName; + }; + + extern CPosixTimezone g_timezone; +-- +2.43.0 + + +From a36c95155a2b8d22f69ad0809d895c11838620ed Mon Sep 17 00:00:00 2001 +From: Lukas Rusak +Date: Thu, 10 Sep 2020 08:41:57 -0700 +Subject: [PATCH 06/19] utils/XTimeUtils: clean up SystemTime and FileTime + functions + +--- + xbmc/XBDateTime.cpp | 213 ++---------------- + xbmc/XBDateTime.h | 27 --- + .../addons/interfaces/gui/dialogs/Numeric.cpp | 10 +- + xbmc/dialogs/GUIDialogNumeric.cpp | 203 ++++++++++------- + xbmc/dialogs/GUIDialogNumeric.h | 12 +- + xbmc/filesystem/NFSDirectory.cpp | 18 +- + xbmc/guilib/GUIEditControl.cpp | 13 +- + xbmc/guilib/GUIRSSControl.h | 2 +- + xbmc/interfaces/legacy/Dialog.cpp | 44 ++-- + xbmc/music/infoscanner/MusicInfoScanner.cpp | 4 +- + xbmc/network/upnp/UPnPInternal.cpp | 18 +- + xbmc/platform/posix/XTimeUtils.cpp | 211 ----------------- + .../posix/filesystem/PosixDirectory.cpp | 5 +- + .../posix/filesystem/SMBDirectory.cpp | 16 +- + .../pvr/dialogs/GUIDialogPVRTimerSettings.cpp | 18 +- + xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h | 1 - + xbmc/pvr/windows/GUIWindowPVRGuide.cpp | 7 +- + xbmc/settings/windows/GUIControlSettings.cpp | 14 +- + xbmc/test/TestDateTime.cpp | 18 +- + xbmc/utils/Archive.cpp | 8 +- + xbmc/utils/Archive.h | 4 +- + xbmc/utils/RssManager.cpp | 2 +- + xbmc/utils/RssManager.h | 2 +- + xbmc/utils/RssReader.cpp | 25 +- + xbmc/utils/RssReader.h | 10 +- + xbmc/utils/XTimeUtils.h | 44 ---- + xbmc/utils/test/TestArchive.cpp | 22 +- + xbmc/video/VideoInfoScanner.cpp | 4 +- + 28 files changed, 253 insertions(+), 722 deletions(-) + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index 47c6b8d1a6..d8187b71e6 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -171,23 +171,6 @@ CDateTime::CDateTime() + Reset(); + } + +-CDateTime::CDateTime(const KODI::TIME::SystemTime& systemTime) +-{ +- KODI::TIME::FileTime fileTime; +- m_state = ToFileTime(systemTime, fileTime) ? valid : invalid; +- +- time_t time; +- KODI::TIME::FileTimeToTimeT(&fileTime, &time); +- m_time = std::chrono::system_clock::from_time_t(time); +-} +- +-CDateTime::CDateTime(const KODI::TIME::FileTime& fileTime) +-{ +- time_t time; +- KODI::TIME::FileTimeToTimeT(&fileTime, &time); +- m_time = std::chrono::system_clock::from_time_t(time); +-} +- + CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time) + { + m_state=time.m_state; +@@ -230,27 +213,6 @@ CDateTime CDateTime::GetUTCDateTime() + return CDateTime(std::chrono::system_clock::now()); + } + +-const CDateTime& CDateTime::operator=(const KODI::TIME::SystemTime& right) +-{ +- KODI::TIME::FileTime fileTime; +- m_state = ToFileTime(right, fileTime) ? valid : invalid; +- +- time_t time; +- KODI::TIME::FileTimeToTimeT(&fileTime, &time); +- m_time = std::chrono::system_clock::from_time_t(time); +- +- return *this; +-} +- +-const CDateTime& CDateTime::operator=(const KODI::TIME::FileTime& right) +-{ +- time_t time; +- KODI::TIME::FileTimeToTimeT(&right, &time); +- m_time = std::chrono::system_clock::from_time_t(time); +- +- return *this; +-} +- + const CDateTime& CDateTime::operator =(const time_t& right) + { + m_time = std::chrono::system_clock::from_time_t(right); +@@ -305,84 +267,6 @@ bool CDateTime::operator !=(const CDateTime& right) const + return !operator ==(right); + } + +-bool CDateTime::operator>(const KODI::TIME::FileTime& right) const +-{ +- time_t time; +- KODI::TIME::FileTimeToTimeT(&right, &time); +- +- return m_time > std::chrono::system_clock::from_time_t(time); +-} +- +-bool CDateTime::operator>=(const KODI::TIME::FileTime& right) const +-{ +- return operator >(right) || operator ==(right); +-} +- +-bool CDateTime::operator<(const KODI::TIME::FileTime& right) const +-{ +- time_t time; +- KODI::TIME::FileTimeToTimeT(&right, &time); +- +- return m_time < std::chrono::system_clock::from_time_t(time); +-} +- +-bool CDateTime::operator<=(const KODI::TIME::FileTime& right) const +-{ +- return operator <(right) || operator ==(right); +-} +- +-bool CDateTime::operator==(const KODI::TIME::FileTime& right) const +-{ +- time_t time; +- KODI::TIME::FileTimeToTimeT(&right, &time); +- +- return m_time == std::chrono::system_clock::from_time_t(time); +-} +- +-bool CDateTime::operator!=(const KODI::TIME::FileTime& right) const +-{ +- return !operator ==(right); +-} +- +-bool CDateTime::operator>(const KODI::TIME::SystemTime& right) const +-{ +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator >(time); +-} +- +-bool CDateTime::operator>=(const KODI::TIME::SystemTime& right) const +-{ +- return operator >(right) || operator ==(right); +-} +- +-bool CDateTime::operator<(const KODI::TIME::SystemTime& right) const +-{ +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator <(time); +-} +- +-bool CDateTime::operator<=(const KODI::TIME::SystemTime& right) const +-{ +- return operator <(right) || operator ==(right); +-} +- +-bool CDateTime::operator==(const KODI::TIME::SystemTime& right) const +-{ +- KODI::TIME::FileTime time; +- ToFileTime(right, time); +- +- return operator ==(time); +-} +- +-bool CDateTime::operator!=(const KODI::TIME::SystemTime& right) const +-{ +- return !operator ==(right); +-} +- + bool CDateTime::operator >(const time_t& right) const + { + return m_time > std::chrono::system_clock::from_time_t(right); +@@ -477,7 +361,7 @@ CDateTime CDateTime::operator+(const CDateTimeSpan& right) const + { + CDateTime left(*this); + +- left.m_time + right.m_timeSpan; ++ left.m_time += right.m_timeSpan; + + return left; + } +@@ -486,7 +370,7 @@ CDateTime CDateTime::operator-(const CDateTimeSpan& right) const + { + CDateTime left(*this); + +- left.m_time - right.m_timeSpan; ++ left.m_time -= right.m_timeSpan; + + return left; + } +@@ -513,14 +397,6 @@ CDateTimeSpan CDateTime::operator -(const CDateTime& right) const + return left; + } + +-CDateTime::operator KODI::TIME::FileTime() const +-{ +- KODI::TIME::FileTime fileTime; +- time_t time = std::chrono::system_clock::to_time_t(m_time); +- KODI::TIME::TimeTToFileTime(time, &fileTime); +- return fileTime; +-} +- + void CDateTime::Archive(CArchive& ar) + { + if (ar.IsStoring()) +@@ -528,9 +404,7 @@ void CDateTime::Archive(CArchive& ar) + ar<<(int)m_state; + if (m_state==valid) + { +- KODI::TIME::SystemTime st; +- GetAsSystemTime(st); +- ar<>st; +- ToTimePoint(st, m_time); ++ ar >> m_time; + } + } + } +@@ -564,37 +436,6 @@ bool CDateTime::IsValid() const + return m_state == valid; + } + +-bool CDateTime::ToTimePoint(const KODI::TIME::SystemTime& systemTime, +- std::chrono::system_clock::time_point& timePoint) const +-{ +- KODI::TIME::FileTime fileTime; +- KODI::TIME::SystemTimeToFileTime(&systemTime, &fileTime); +- +- time_t time; +- KODI::TIME::FileTimeToTimeT(&fileTime, &time); +- +- timePoint = std::chrono::system_clock::from_time_t(time); +- return true; +-} +- +-bool CDateTime::ToFileTime(const KODI::TIME::SystemTime& time, KODI::TIME::FileTime& fileTime) const +-{ +- return KODI::TIME::SystemTimeToFileTime(&time, &fileTime) == 1 && +- (fileTime.lowDateTime > 0 || fileTime.highDateTime > 0); +-} +- +-bool CDateTime::ToFileTime(const time_t& time, KODI::TIME::FileTime& fileTime) const +-{ +- long long ll = time; +- ll *= 10000000ll; +- ll += 0x19DB1DED53E8000LL; +- +- fileTime.lowDateTime = (DWORD)(ll & 0xFFFFFFFF); +- fileTime.highDateTime = (DWORD)(ll >> 32); +- +- return true; +-} +- + bool CDateTime::SetFromDateString(const std::string &date) + { + //! @todo STRING_CLEANUP +@@ -729,14 +570,6 @@ bool CDateTime::SetTime(int hour, int minute, int second) + return true; + } + +-void CDateTime::GetAsSystemTime(KODI::TIME::SystemTime& systemTime) const +-{ +- const time_t time = std::chrono::system_clock::to_time_t(m_time); +- KODI::TIME::FileTime fileTime; +- ToFileTime(time, fileTime); +- KODI::TIME::FileTimeToSystemTime(&fileTime, &systemTime); +-} +- + void CDateTime::GetAsTime(time_t& time) const + { + time = std::chrono::system_clock::to_time_t(m_time); +@@ -755,12 +588,6 @@ std::chrono::system_clock::time_point CDateTime::GetAsTimePoint() const + return m_time; + } + +-// void CDateTime::GetAsTimeStamp(KODI::TIME::FileTime& fileTime) const +-// { +-// time_t time = std::chrono::system_clock::to_time_t(m_time); +-// KODI::TIME::TimeTToFileTime(time, &fileTime); +-// } +- + std::string CDateTime::GetAsDBDate() const + { + return date::format("%F", m_time); +@@ -1052,12 +879,9 @@ std::string CDateTime::GetAsLocalizedTime(const std::string &format, bool withSe + std::string strOut; + const std::string& strFormat = format.empty() ? g_langInfo.GetTimeFormat() : format; + +- KODI::TIME::SystemTime dateTime; +- GetAsSystemTime(dateTime); +- + // Prefetch meridiem symbol + const std::string& strMeridiem = +- CLangInfo::MeridiemSymbolToString(dateTime.hour > 11 ? MeridiemSymbolPM : MeridiemSymbolAM); ++ CLangInfo::MeridiemSymbolToString(GetHour() > 11 ? MeridiemSymbolPM : MeridiemSymbolAM); + + size_t length = strFormat.size(); + for (size_t i=0; i < length; ++i) +@@ -1106,7 +930,7 @@ std::string CDateTime::GetAsLocalizedTime(const std::string &format, bool withSe + i=length; + } + +- int hour = dateTime.hour; ++ int hour = GetHour(); + if (c=='h') + { // recalc to 12 hour clock + if (hour > 11) +@@ -1145,9 +969,9 @@ std::string CDateTime::GetAsLocalizedTime(const std::string &format, bool withSe + // Format minute string with the length of the mask + std::string str; + if (partLength==1) +- str = std::to_string(dateTime.minute); ++ str = std::to_string(GetMinute()); + else +- str = StringUtils::Format("{:02}", dateTime.minute); ++ str = StringUtils::Format("{:02}", GetMinute()); + + strOut+=str; + } +@@ -1174,9 +998,9 @@ std::string CDateTime::GetAsLocalizedTime(const std::string &format, bool withSe + // Format seconds string with the length of the mask + std::string str; + if (partLength==1) +- str = std::to_string(dateTime.second); ++ str = std::to_string(GetSecond()); + else +- str = StringUtils::Format("{:02}", dateTime.second); ++ str = StringUtils::Format("{:02}", GetSecond()); + + strOut+=str; + } +@@ -1215,9 +1039,6 @@ std::string CDateTime::GetAsLocalizedDate(const std::string &strFormat) const + { + std::string strOut; + +- KODI::TIME::SystemTime dateTime; +- GetAsSystemTime(dateTime); +- + size_t length = strFormat.size(); + for (size_t i = 0; i < length; ++i) + { +@@ -1267,12 +1088,12 @@ std::string CDateTime::GetAsLocalizedDate(const std::string &strFormat) const + // Format string with the length of the mask + std::string str; + if (partLength==1) // single-digit number +- str = std::to_string(dateTime.day); ++ str = std::to_string(GetDay()); + else if (partLength==2) // two-digit number +- str = StringUtils::Format("{:02}", dateTime.day); ++ str = StringUtils::Format("{:02}", GetDay()); + else // Day of week string + { +- int wday = dateTime.dayOfWeek; ++ int wday = GetDayOfWeek(); + if (wday < 1 || wday > 7) wday = 7; + str = g_localizeStrings.Get((c =='d' ? 40 : 10) + wday); + } +@@ -1299,12 +1120,12 @@ std::string CDateTime::GetAsLocalizedDate(const std::string &strFormat) const + // Format string with the length of the mask + std::string str; + if (partLength==1) // single-digit number +- str = std::to_string(dateTime.month); ++ str = std::to_string(GetMonth()); + else if (partLength==2) // two-digit number +- str = StringUtils::Format("{:02}", dateTime.month); ++ str = StringUtils::Format("{:02}", GetMonth()); + else // Month string + { +- int wmonth = dateTime.month; ++ int wmonth = GetMonth(); + if (wmonth < 1 || wmonth > 12) wmonth = 12; + str = g_localizeStrings.Get((c =='m' ? 50 : 20) + wmonth); + } +@@ -1329,7 +1150,7 @@ std::string CDateTime::GetAsLocalizedDate(const std::string &strFormat) const + } + + // Format string with the length of the mask +- std::string str = std::to_string(dateTime.year); // four-digit number ++ std::string str = std::to_string(GetYear()); // four-digit number + if (partLength <= 2) + str.erase(0, 2); // two-digit number + +diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h +index 60d06e417a..16d6217c8a 100644 +--- a/xbmc/XBDateTime.h ++++ b/xbmc/XBDateTime.h +@@ -63,8 +63,6 @@ public: + CDateTime(); + CDateTime(const CDateTime& time); + CDateTime& operator=(const CDateTime&) = default; +- explicit CDateTime(const KODI::TIME::SystemTime& time); +- explicit CDateTime(const KODI::TIME::FileTime& time); + explicit CDateTime(const time_t& time); + explicit CDateTime(const tm& time); + explicit CDateTime(const std::chrono::system_clock::time_point& time); +@@ -84,8 +82,6 @@ public: + static CDateTime FromUTCDateTime(const time_t &dateTime); + static CDateTime FromRFC1123DateTime(const std::string &dateTime); + +- const CDateTime& operator=(const KODI::TIME::SystemTime& right); +- const CDateTime& operator=(const KODI::TIME::FileTime& right); + const CDateTime& operator =(const time_t& right); + const CDateTime& operator =(const tm& right); + const CDateTime& operator=(const std::chrono::system_clock::time_point& right); +@@ -97,20 +93,6 @@ public: + bool operator ==(const CDateTime& right) const; + bool operator !=(const CDateTime& right) const; + +- bool operator>(const KODI::TIME::FileTime& right) const; +- bool operator>=(const KODI::TIME::FileTime& right) const; +- bool operator<(const KODI::TIME::FileTime& right) const; +- bool operator<=(const KODI::TIME::FileTime& right) const; +- bool operator==(const KODI::TIME::FileTime& right) const; +- bool operator!=(const KODI::TIME::FileTime& right) const; +- +- bool operator>(const KODI::TIME::SystemTime& right) const; +- bool operator>=(const KODI::TIME::SystemTime& right) const; +- bool operator<(const KODI::TIME::SystemTime& right) const; +- bool operator<=(const KODI::TIME::SystemTime& right) const; +- bool operator==(const KODI::TIME::SystemTime& right) const; +- bool operator!=(const KODI::TIME::SystemTime& right) const; +- + bool operator >(const time_t& right) const; + bool operator >=(const time_t& right) const; + bool operator <(const time_t& right) const; +@@ -140,8 +122,6 @@ public: + + CDateTimeSpan operator -(const CDateTime& right) const; + +- operator KODI::TIME::FileTime() const; +- + void Archive(CArchive& ar) override; + + void Reset(); +@@ -173,7 +153,6 @@ public: + */ + bool SetFromDBDateTime(const std::string &dateTime); + +- void GetAsSystemTime(KODI::TIME::SystemTime& time) const; + void GetAsTime(time_t& time) const; + void GetAsTm(tm& time) const; + std::chrono::system_clock::time_point GetAsTimePoint() const; +@@ -195,12 +174,6 @@ public: + void SetValid(bool yesNo); + bool IsValid() const; + +-private: +- bool ToTimePoint(const KODI::TIME::SystemTime& time, +- std::chrono::system_clock::time_point& timePoint) const; +- bool ToFileTime(const KODI::TIME::SystemTime& time, KODI::TIME::FileTime& fileTime) const; +- bool ToFileTime(const time_t& time, KODI::TIME::FileTime& fileTime) const; +- + private: + std::chrono::system_clock::time_point m_time; + +diff --git a/xbmc/addons/interfaces/gui/dialogs/Numeric.cpp b/xbmc/addons/interfaces/gui/dialogs/Numeric.cpp +index a78aa4af60..3acc48e768 100644 +--- a/xbmc/addons/interfaces/gui/dialogs/Numeric.cpp ++++ b/xbmc/addons/interfaces/gui/dialogs/Numeric.cpp +@@ -135,12 +135,9 @@ bool Interface_GUIDialogNumeric::show_and_get_time(KODI_HANDLE kodiBase, + return false; + } + +- KODI::TIME::SystemTime systemTime; + CDateTime dateTime(*time); +- dateTime.GetAsSystemTime(systemTime); +- if (CGUIDialogNumeric::ShowAndGetTime(systemTime, heading)) ++ if (CGUIDialogNumeric::ShowAndGetTime(dateTime, heading)) + { +- dateTime = systemTime; + dateTime.GetAsTm(*time); + return true; + } +@@ -167,12 +164,9 @@ bool Interface_GUIDialogNumeric::show_and_get_date(KODI_HANDLE kodiBase, + return false; + } + +- KODI::TIME::SystemTime systemTime; + CDateTime dateTime(*date); +- dateTime.GetAsSystemTime(systemTime); +- if (CGUIDialogNumeric::ShowAndGetDate(systemTime, heading)) ++ if (CGUIDialogNumeric::ShowAndGetDate(dateTime, heading)) + { +- dateTime = systemTime; + dateTime.GetAsTm(*date); + return true; + } +diff --git a/xbmc/dialogs/GUIDialogNumeric.cpp b/xbmc/dialogs/GUIDialogNumeric.cpp +index 182cf6a3af..bba219e44e 100644 +--- a/xbmc/dialogs/GUIDialogNumeric.cpp ++++ b/xbmc/dialogs/GUIDialogNumeric.cpp +@@ -45,7 +45,7 @@ CGUIDialogNumeric::CGUIDialogNumeric(void) + m_lastblock{}, + m_dirty{false} + { +- memset(&m_datetime, 0, sizeof(KODI::TIME::SystemTime)); ++ m_datetime.Reset(); + m_loadType = KEEP_IN_MEMORY; + } + +@@ -231,59 +231,76 @@ void CGUIDialogNumeric::OnBackSpace() + } + else if (m_mode == INPUT_TIME) + { ++ int hour = m_datetime.GetHour(); ++ int minute = m_datetime.GetMinute(); ++ + if (m_block == 0) +- m_datetime.hour /= 10; +- else if (m_datetime.minute) +- m_datetime.minute /= 10; ++ hour /= 10; ++ else if (minute) ++ minute /= 10; + else + { + m_block = 0; + m_dirty = false; + } ++ ++ m_datetime.SetTime(hour, minute, 0); + } + else if (m_mode == INPUT_TIME_SECONDS) + { ++ int hour = m_datetime.GetHour(); ++ int minute = m_datetime.GetMinute(); ++ int second = m_datetime.GetSecond(); ++ + if (m_block == 0) +- m_datetime.hour /= 10; ++ hour /= 10; + else if (m_block == 1) + { +- if (m_datetime.minute) +- m_datetime.minute /= 10; ++ if (minute) ++ minute /= 10; + else + { + m_block = 0; + m_dirty = false; + } + } +- else if (m_datetime.second) +- m_datetime.minute /= 10; ++ else if (second) ++ minute /= 10; + else + { + m_block = 0; + m_dirty = false; + } ++ ++ m_datetime.SetTime(hour, minute, second); + } + else if (m_mode == INPUT_DATE) + { ++ int day = m_datetime.GetDay(); ++ int month = m_datetime.GetMonth(); ++ int year = m_datetime.GetYear(); ++ + if (m_block == 0) +- m_datetime.day /= 10; ++ day /= 10; + else if (m_block == 1) + { +- if (m_datetime.month) +- m_datetime.month /= 10; ++ if (month) ++ month /= 10; + else + { + m_block = 0; + m_dirty = false; + } + } +- else if (m_datetime.year) // m_block == 2 +- m_datetime.year /= 10; ++ else if (year) // m_block == 2 ++ year /= 10; + else + { + m_block = 1; + m_dirty = false; + } ++ ++ m_datetime.SetDate(year, month, day); + } + } + +@@ -317,21 +334,21 @@ void CGUIDialogNumeric::FrameMove() + strLabel = m_number; + else if (m_mode == INPUT_TIME) + { // format up the time +- strLabel = StringUtils::Format("{:2}:{:02}", m_datetime.hour, m_datetime.minute); ++ strLabel = StringUtils::Format("{:2}:{:02}", m_datetime.GetHour(), m_datetime.GetMinute()); + start = m_block * 3; + end = m_block * 3 + 2; + } + else if (m_mode == INPUT_TIME_SECONDS) + { // format up the time +- strLabel = StringUtils::Format("{:2}:{:02}:{:02}", m_datetime.hour, m_datetime.minute, +- m_datetime.second); ++ strLabel = StringUtils::Format("{:2}:{:02}:{:02}", m_datetime.GetHour(), m_datetime.GetMinute(), ++ m_datetime.GetSecond()); + start = m_block * 3; + end = m_block * 3 + 2; + } + else if (m_mode == INPUT_DATE) + { // format up the date +- strLabel = +- StringUtils::Format("{:2}/{:2}/{:4}", m_datetime.day, m_datetime.month, m_datetime.year); ++ strLabel = StringUtils::Format("{:2}/{:2}/{:4}", m_datetime.GetDay(), m_datetime.GetMonth(), ++ m_datetime.GetYear()); + start = m_block * 3; + end = m_block * 3 + 2; + if (m_block == 2) +@@ -377,7 +394,7 @@ void CGUIDialogNumeric::OnNumber(uint32_t num) + } + } + +-void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const KODI::TIME::SystemTime& initial) ++void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const CDateTime& initial) + { + m_mode = mode; + m_block = 0; +@@ -425,7 +442,7 @@ void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const std::string &initial) + if (!dateTime.IsValid()) + return; + +- dateTime.GetAsSystemTime(m_datetime); ++ m_datetime = dateTime; + m_lastblock = (m_mode == INPUT_DATE) ? 2 : 1; + } + else if (m_mode == INPUT_IP_ADDRESS) +@@ -447,7 +464,7 @@ void CGUIDialogNumeric::SetMode(INPUT_MODE mode, const std::string &initial) + m_number = initial; + } + +-KODI::TIME::SystemTime CGUIDialogNumeric::GetOutput() const ++CDateTime CGUIDialogNumeric::GetOutput() const + { + assert(m_mode == INPUT_TIME || m_mode == INPUT_TIME_SECONDS || m_mode == INPUT_DATE); + return m_datetime; +@@ -458,13 +475,13 @@ std::string CGUIDialogNumeric::GetOutputString() const + switch (m_mode) + { + case INPUT_DATE: +- return StringUtils::Format("{:02}/{:02}/{:04}", m_datetime.day, m_datetime.month, +- m_datetime.year); ++ return StringUtils::Format("{:02}/{:02}/{:04}", m_datetime.GetDay(), m_datetime.GetMonth(), ++ m_datetime.GetYear()); + case INPUT_TIME: +- return StringUtils::Format("{}:{:02}", m_datetime.hour, m_datetime.minute); ++ return StringUtils::Format("{}:{:02}", m_datetime.GetHour(), m_datetime.GetMinute()); + case INPUT_TIME_SECONDS: +- return StringUtils::Format("{}:{:02}:{:02}", m_datetime.hour, m_datetime.minute, +- m_datetime.second); ++ return StringUtils::Format("{}:{:02}:{:02}", m_datetime.GetHour(), m_datetime.GetMinute(), ++ m_datetime.GetSecond()); + case INPUT_IP_ADDRESS: + return StringUtils::Format("{}.{}.{}.{}", m_ip[0], m_ip[1], m_ip[2], m_ip[3]); + case INPUT_NUMBER: +@@ -480,23 +497,25 @@ bool CGUIDialogNumeric::ShowAndGetSeconds(std::string &timeString, const std::st + { + CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_NUMERIC); + if (!pDialog) return false; +- int seconds = StringUtils::TimeStringToSeconds(timeString); +- KODI::TIME::SystemTime time = {}; +- time.hour = seconds / 3600; +- time.minute = (seconds - time.hour * 3600) / 60; +- time.second = seconds - time.hour * 3600 - time.minute * 60; +- pDialog->SetMode(INPUT_TIME_SECONDS, time); ++ ++ std::chrono::system_clock::time_point time{ ++ std::chrono::seconds{StringUtils::TimeStringToSeconds(timeString)}}; ++ ++ CDateTime datetime(time); ++ ++ pDialog->SetMode(INPUT_TIME_SECONDS, datetime); + pDialog->SetHeading(heading); + pDialog->Open(); + if (!pDialog->IsConfirmed() || pDialog->IsCanceled()) + return false; +- time = pDialog->GetOutput(); +- seconds = time.hour * 3600 + time.minute * 60 + time.second; +- timeString = StringUtils::SecondsToTimeString(seconds); ++ datetime = pDialog->GetOutput(); ++ time = datetime.GetAsTimePoint(); ++ timeString = StringUtils::SecondsToTimeString( ++ std::chrono::duration_cast(time.time_since_epoch()).count()); + return true; + } + +-bool CGUIDialogNumeric::ShowAndGetTime(KODI::TIME::SystemTime& time, const std::string& heading) ++bool CGUIDialogNumeric::ShowAndGetTime(CDateTime& time, const std::string& heading) + { + CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_NUMERIC); + if (!pDialog) return false; +@@ -509,7 +528,7 @@ bool CGUIDialogNumeric::ShowAndGetTime(KODI::TIME::SystemTime& time, const std:: + return true; + } + +-bool CGUIDialogNumeric::ShowAndGetDate(KODI::TIME::SystemTime& date, const std::string& heading) ++bool CGUIDialogNumeric::ShowAndGetDate(CDateTime& date, const std::string& heading) + { + CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_NUMERIC); + if (!pDialog) return false; +@@ -680,28 +699,33 @@ void CGUIDialogNumeric::SetHeading(const std::string& strHeading) + + void CGUIDialogNumeric::VerifyDate(bool checkYear) + { +- if (m_datetime.day == 0) +- m_datetime.day = 1; +- if (m_datetime.month == 0) +- m_datetime.month = 1; ++ int day = m_datetime.GetDay(); ++ int month = m_datetime.GetMonth(); ++ int year = m_datetime.GetYear(); ++ ++ if (day == 0) ++ day = 1; ++ if (month == 0) ++ month = 1; + // check for number of days in the month +- if (m_datetime.day == 31) ++ if (day == 31) + { +- if (m_datetime.month == 4 || m_datetime.month == 6 || m_datetime.month == 9 || +- m_datetime.month == 11) +- m_datetime.day = 30; ++ if (month == 4 || month == 6 || month == 9 || month == 11) ++ day = 30; + } +- if (m_datetime.month == 2 && m_datetime.day > 28) ++ if (month == 2 && day > 28) + { +- m_datetime.day = 29; // max in february. ++ day = 29; // max in february. + if (checkYear) + { + // leap years occur when the year is divisible by 4 but not by 100, or the year is divisible by 400 + // thus they don't occur, if the year has a remainder when divided by 4, or when the year is divisible by 100 but not by 400 +- if ((m_datetime.year % 4) || (!(m_datetime.year % 100) && (m_datetime.year % 400))) +- m_datetime.day = 28; ++ if ((year % 4) || (!(year % 100) && (year % 400))) ++ day = 28; + } + } ++ ++ m_datetime.SetDate(year, month, day); + } + + void CGUIDialogNumeric::OnOK() +@@ -741,17 +765,21 @@ void CGUIDialogNumeric::HandleInputIP(uint32_t num) + + void CGUIDialogNumeric::HandleInputDate(uint32_t num) + { ++ int day = m_datetime.GetDay(); ++ int month = m_datetime.GetMonth(); ++ int year = m_datetime.GetYear(); ++ + if (m_block == 0) // day of month + { +- if (m_dirty && (m_datetime.day < 3 || num < 2)) ++ if (m_dirty && (day < 3 || num < 2)) + { +- m_datetime.day *= 10; +- m_datetime.day += num; ++ day *= 10; ++ day += num; + } + else +- m_datetime.day = num; ++ day = num; + +- if (m_datetime.day > 3) ++ if (day > 3) + { + m_block = 1; // move to months + m_dirty = false; +@@ -763,13 +791,13 @@ void CGUIDialogNumeric::HandleInputDate(uint32_t num) + { + if (m_dirty && num < 3) + { +- m_datetime.month *= 10; +- m_datetime.month += num; ++ month *= 10; ++ month += num; + } + else +- m_datetime.month = num; ++ month = num; + +- if (m_datetime.month > 1) ++ if (month > 1) + { + VerifyDate(false); + m_block = 2; // move to year +@@ -780,15 +808,15 @@ void CGUIDialogNumeric::HandleInputDate(uint32_t num) + } + else // year + { +- if (m_dirty && m_datetime.year < 1000) // have taken input ++ if (m_dirty && year < 1000) // have taken input + { +- m_datetime.year *= 10; +- m_datetime.year += num; ++ year *= 10; ++ year += num; + } + else +- m_datetime.year = num; ++ year = num; + +- if (m_datetime.year > 1000) ++ if (year > 1000) + { + VerifyDate(true); + m_block = 0; // move to day of month +@@ -797,22 +825,28 @@ void CGUIDialogNumeric::HandleInputDate(uint32_t num) + else + m_dirty = true; + } ++ ++ m_datetime.SetDate(year, month, day); + } + + void CGUIDialogNumeric::HandleInputSeconds(uint32_t num) + { ++ int hour = m_datetime.GetHour(); ++ int minute = m_datetime.GetMinute(); ++ int second = m_datetime.GetSecond(); ++ + if (m_block == 0) // hour + { + if (m_dirty) // have input the first digit + { +- m_datetime.hour *= 10; +- m_datetime.hour += num; ++ hour *= 10; ++ hour += num; + m_block = 1; // move to minutes - allows up to 99 hours + m_dirty = false; + } + else // this is the first digit + { +- m_datetime.hour = num; ++ hour = num; + m_dirty = true; + } + } +@@ -820,14 +854,14 @@ void CGUIDialogNumeric::HandleInputSeconds(uint32_t num) + { + if (m_dirty) // have input the first digit + { +- m_datetime.minute *= 10; +- m_datetime.minute += num; ++ minute *= 10; ++ minute += num; + m_block = 2; // move to seconds - allows up to 99 minutes + m_dirty = false; + } + else // this is the first digit + { +- m_datetime.minute = num; ++ minute = num; + if (num > 5) + { + m_block = 2; // move to seconds +@@ -841,14 +875,14 @@ void CGUIDialogNumeric::HandleInputSeconds(uint32_t num) + { + if (m_dirty) // have input the first digit + { +- m_datetime.second *= 10; +- m_datetime.second += num; ++ second *= 10; ++ second += num; + m_block = 0; // move to hours + m_dirty = false; + } + else // this is the first digit + { +- m_datetime.second = num; ++ second = num; + if (num > 5) + { + m_block = 0; // move to hours +@@ -858,28 +892,33 @@ void CGUIDialogNumeric::HandleInputSeconds(uint32_t num) + m_dirty = true; + } + } ++ ++ m_datetime.SetTime(hour, minute, second); + } + + void CGUIDialogNumeric::HandleInputTime(uint32_t num) + { ++ int hour = m_datetime.GetHour(); ++ int minute = m_datetime.GetMinute(); ++ + if (m_block == 0) // hour + { + if (m_dirty) // have input the first digit + { +- if (m_datetime.hour < 2 || num < 4) ++ if (hour < 2 || num < 4) + { +- m_datetime.hour *= 10; +- m_datetime.hour += num; ++ hour *= 10; ++ hour += num; + } + else +- m_datetime.hour = num; ++ hour = num; + + m_block = 1; // move to minutes + m_dirty = false; + } + else // this is the first digit + { +- m_datetime.hour = num; ++ hour = num; + + if (num > 2) + { +@@ -894,14 +933,14 @@ void CGUIDialogNumeric::HandleInputTime(uint32_t num) + { + if (m_dirty) // have input the first digit + { +- m_datetime.minute *= 10; +- m_datetime.minute += num; ++ minute *= 10; ++ minute += num; + m_block = 0; // move to hours + m_dirty = false; + } + else // this is the first digit + { +- m_datetime.minute = num; ++ minute = num; + + if (num > 5) + { +@@ -912,5 +951,7 @@ void CGUIDialogNumeric::HandleInputTime(uint32_t num) + m_dirty = true; + } + } ++ ++ m_datetime.SetTime(hour, minute, 0); + } + +diff --git a/xbmc/dialogs/GUIDialogNumeric.h b/xbmc/dialogs/GUIDialogNumeric.h +index 2932053021..d39b2702cd 100644 +--- a/xbmc/dialogs/GUIDialogNumeric.h ++++ b/xbmc/dialogs/GUIDialogNumeric.h +@@ -8,8 +8,8 @@ + + #pragma once + ++#include "XBDateTime.h" + #include "guilib/GUIDialog.h" +-#include "utils/XTimeUtils.h" + + #include + +@@ -41,13 +41,13 @@ public: + static InputVerificationResult ShowAndVerifyInput(std::string& strPassword, const std::string& strHeading, bool bGetUserInput); + + void SetHeading(const std::string &strHeading); +- void SetMode(INPUT_MODE mode, const KODI::TIME::SystemTime& initial); ++ void SetMode(INPUT_MODE mode, const CDateTime& initial); + void SetMode(INPUT_MODE mode, const std::string &initial); +- KODI::TIME::SystemTime GetOutput() const; ++ CDateTime GetOutput() const; + std::string GetOutputString() const; + +- static bool ShowAndGetTime(KODI::TIME::SystemTime& time, const std::string& heading); +- static bool ShowAndGetDate(KODI::TIME::SystemTime& date, const std::string& heading); ++ static bool ShowAndGetTime(CDateTime& time, const std::string& heading); ++ static bool ShowAndGetDate(CDateTime& date, const std::string& heading); + static bool ShowAndGetIPAddress(std::string &IPAddress, const std::string &heading); + static bool ShowAndGetNumber(std::string& strInput, const std::string &strHeading, unsigned int iAutoCloseTimeoutMs = 0, bool bSetHidden = false); + static bool ShowAndGetSeconds(std::string& timeString, const std::string &heading); +@@ -73,7 +73,7 @@ protected: + bool m_bCanceled; + + INPUT_MODE m_mode; // the current input mode +- KODI::TIME::SystemTime m_datetime; // for time and date modes ++ CDateTime m_datetime; // for time and date modes + uint8_t m_ip[4]; // for ip address mode + uint32_t m_block; // for time, date, and IP methods. + uint32_t m_lastblock; +diff --git a/xbmc/filesystem/NFSDirectory.cpp b/xbmc/filesystem/NFSDirectory.cpp +index 7feba534c7..69a5dad0f6 100644 +--- a/xbmc/filesystem/NFSDirectory.cpp ++++ b/xbmc/filesystem/NFSDirectory.cpp +@@ -202,7 +202,6 @@ bool CNFSDirectory::GetDirectory(const CURL& url, CFileItemList &items) + { + // We accept nfs://server/path[/file]]]] + int ret = 0; +- KODI::TIME::FileTime fileTime, localTime; + std::unique_lock lock(gNfsConnection); + std::string strDirName=""; + std::string myStrPath(url.Get()); +@@ -248,7 +247,6 @@ bool CNFSDirectory::GetDirectory(const CURL& url, CFileItemList &items) + std::string path(myStrPath + strName); + int64_t iSize = 0; + bool bIsDir = false; +- int64_t lTimeDate = 0; + + //reslove symlinks + if(tmpDirent.type == NF3LNK) +@@ -265,25 +263,13 @@ bool CNFSDirectory::GetDirectory(const CURL& url, CFileItemList &items) + + iSize = tmpDirent.size; + bIsDir = tmpDirent.type == NF3DIR; +- lTimeDate = tmpDirent.mtime.tv_sec; + + if (!StringUtils::EqualsNoCase(strName,".") && !StringUtils::EqualsNoCase(strName,"..") + && !StringUtils::EqualsNoCase(strName,"lost+found")) + { +- if(lTimeDate == 0) // if modification date is missing, use create date +- { +- lTimeDate = tmpDirent.ctime.tv_sec; +- } +- +- long long ll = lTimeDate & 0xffffffff; +- ll *= 10000000ll; +- ll += 116444736000000000ll; +- fileTime.lowDateTime = (DWORD)(ll & 0xffffffff); +- fileTime.highDateTime = (DWORD)(ll >> 32); +- KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime); +- + CFileItemPtr pItem(new CFileItem(tmpDirent.name)); +- pItem->m_dateTime=localTime; ++ pItem->m_dateTime = ++ tmpDirent.mtime.tv_sec != 0 ? tmpDirent.mtime.tv_sec : tmpDirent.ctime.tv_sec; + pItem->m_dwSize = iSize; + + if (bIsDir) +diff --git a/xbmc/guilib/GUIEditControl.cpp b/xbmc/guilib/GUIEditControl.cpp +index 325a703152..f5c25adb37 100644 +--- a/xbmc/guilib/GUIEditControl.cpp ++++ b/xbmc/guilib/GUIEditControl.cpp +@@ -297,11 +297,9 @@ void CGUIEditControl::OnClick() + { + CDateTime dateTime; + dateTime.SetFromDBTime(utf8); +- KODI::TIME::SystemTime time; +- dateTime.GetAsSystemTime(time); +- if (CGUIDialogNumeric::ShowAndGetTime(time, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420))) ++ if (CGUIDialogNumeric::ShowAndGetTime( ++ dateTime, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420))) + { +- dateTime = CDateTime(time); + utf8 = dateTime.GetAsLocalizedTime("", false); + textChanged = true; + } +@@ -313,11 +311,10 @@ void CGUIEditControl::OnClick() + dateTime.SetFromDBDate(utf8); + if (dateTime < CDateTime(2000,1, 1, 0, 0, 0)) + dateTime = CDateTime(2000, 1, 1, 0, 0, 0); +- KODI::TIME::SystemTime date; +- dateTime.GetAsSystemTime(date); +- if (CGUIDialogNumeric::ShowAndGetDate(date, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420))) ++ ++ if (CGUIDialogNumeric::ShowAndGetDate( ++ dateTime, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420))) + { +- dateTime = CDateTime(date); + utf8 = dateTime.GetAsDBDate(); + textChanged = true; + } +diff --git a/xbmc/guilib/GUIRSSControl.h b/xbmc/guilib/GUIRSSControl.h +index 851f72fb3c..1ade92dbaf 100644 +--- a/xbmc/guilib/GUIRSSControl.h ++++ b/xbmc/guilib/GUIRSSControl.h +@@ -62,7 +62,7 @@ protected: + KODI::GUILIB::GUIINFO::CGUIInfoColor m_headlineColor; + + std::vector m_vecUrls; +- std::vector m_vecIntervals; ++ std::vector m_vecIntervals; + bool m_rtl; + CScrollInfo m_scrollInfo; + bool m_dirty; +diff --git a/xbmc/interfaces/legacy/Dialog.cpp b/xbmc/interfaces/legacy/Dialog.cpp +index 6dd00f179b..5e9db03da6 100644 +--- a/xbmc/interfaces/legacy/Dialog.cpp ++++ b/xbmc/interfaces/legacy/Dialog.cpp +@@ -286,8 +286,8 @@ namespace XBMCAddon + { + DelayedCallGuard dcguard(languageHook); + std::string value; +- KODI::TIME::SystemTime timedate; +- KODI::TIME::GetLocalTime(&timedate); ++ ++ auto timedate = CDateTime::GetCurrentDateTime(); + + if (!heading.empty()) + { +@@ -295,14 +295,13 @@ namespace XBMCAddon + { + if (!defaultt.empty() && defaultt.size() == 10) + { +- const std::string& sDefault = defaultt; +- timedate.day = atoi(sDefault.substr(0, 2).c_str()); +- timedate.month = atoi(sDefault.substr(3, 4).c_str()); +- timedate.year = atoi(sDefault.substr(sDefault.size() - 4).c_str()); ++ const std::string sDefault = defaultt; ++ timedate.SetFromDBDate(sDefault.substr(sDefault.size() - 4) + "-" + ++ sDefault.substr(3, 4) + "-" + sDefault.substr(0, 2)); + } + if (CGUIDialogNumeric::ShowAndGetDate(timedate, heading)) +- value = +- StringUtils::Format("{:2}/{:2}/{:4}", timedate.day, timedate.month, timedate.year); ++ value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.GetDay(), timedate.GetMonth(), ++ timedate.GetYear()); + else + return emptyString; + } +@@ -310,12 +309,11 @@ namespace XBMCAddon + { + if (!defaultt.empty() && defaultt.size() == 5) + { +- const std::string& sDefault = defaultt; +- timedate.hour = atoi(sDefault.substr(0, 2).c_str()); +- timedate.minute = atoi(sDefault.substr(3, 2).c_str()); ++ const std::string sDefault = defaultt; ++ timedate.SetFromDBTime(sDefault.substr(0, 2) + ":" + sDefault.substr(3, 2)); + } + if (CGUIDialogNumeric::ShowAndGetTime(timedate, heading)) +- value = StringUtils::Format("{:2}:{:02}", timedate.hour, timedate.minute); ++ value = StringUtils::Format("{:2}:{:02}", timedate.GetHour(), timedate.GetMinute()); + else + return emptyString; + } +@@ -367,8 +365,8 @@ namespace XBMCAddon + { + DelayedCallGuard dcguard(languageHook); + std::string value(defaultt); +- KODI::TIME::SystemTime timedate; +- KODI::TIME::GetLocalTime(&timedate); ++ ++ auto timedate = CDateTime::GetCurrentDateTime(); + + switch (type) + { +@@ -389,14 +387,13 @@ namespace XBMCAddon + { + if (!defaultt.empty() && defaultt.size() == 10) + { +- const std::string& sDefault = defaultt; +- timedate.day = atoi(sDefault.substr(0, 2).c_str()); +- timedate.month = atoi(sDefault.substr(3, 4).c_str()); +- timedate.year = atoi(sDefault.substr(sDefault.size() - 4).c_str()); ++ const std::string sDefault = defaultt; ++ timedate.SetFromDBDate(sDefault.substr(sDefault.size() - 4) + "-" + ++ sDefault.substr(3, 4) + "-" + sDefault.substr(0, 2)); + } + if (CGUIDialogNumeric::ShowAndGetDate(timedate, heading)) +- value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.day, timedate.month, +- timedate.year); ++ value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.GetDay(), timedate.GetMonth(), ++ timedate.GetYear()); + else + value = emptyString; + } +@@ -405,12 +402,11 @@ namespace XBMCAddon + { + if (!defaultt.empty() && defaultt.size() == 5) + { +- const std::string& sDefault = defaultt; +- timedate.hour = atoi(sDefault.substr(0, 2).c_str()); +- timedate.minute = atoi(sDefault.substr(3, 2).c_str()); ++ const std::string sDefault = defaultt; ++ timedate.SetFromDBTime(sDefault.substr(0, 2) + ":" + sDefault.substr(3, 2)); + } + if (CGUIDialogNumeric::ShowAndGetTime(timedate, heading)) +- value = StringUtils::Format("{:2}:{:02}", timedate.hour, timedate.minute); ++ value = StringUtils::Format("{:2}:{:02}", timedate.GetHour(), timedate.GetMinute()); + else + value = emptyString; + } +diff --git a/xbmc/music/infoscanner/MusicInfoScanner.cpp b/xbmc/music/infoscanner/MusicInfoScanner.cpp +index f828b44914..3912892343 100644 +--- a/xbmc/music/infoscanner/MusicInfoScanner.cpp ++++ b/xbmc/music/infoscanner/MusicInfoScanner.cpp +@@ -1269,8 +1269,8 @@ int CMusicInfoScanner::GetPathHash(const CFileItemList &items, std::string &hash + const CFileItemPtr pItem = items[i]; + digest.Update(pItem->GetPath()); + digest.Update((unsigned char *)&pItem->m_dwSize, sizeof(pItem->m_dwSize)); +- KODI::TIME::FileTime time = pItem->m_dateTime; +- digest.Update((unsigned char*)&time, sizeof(KODI::TIME::FileTime)); ++ const auto time = pItem->m_dateTime.GetAsTimePoint().time_since_epoch().count(); ++ digest.Update(&time, sizeof(std::chrono::nanoseconds)); + if (pItem->IsAudio() && !pItem->IsPlayList() && !pItem->IsNFO()) + count++; + } +diff --git a/xbmc/network/upnp/UPnPInternal.cpp b/xbmc/network/upnp/UPnPInternal.cpp +index 406615768a..1a98b86c50 100644 +--- a/xbmc/network/upnp/UPnPInternal.cpp ++++ b/xbmc/network/upnp/UPnPInternal.cpp +@@ -1067,11 +1067,19 @@ std::shared_ptr BuildObject(PLT_MediaObject* entry, + } + + // look for date? +- if(entry->m_Description.date.GetLength()) { +- KODI::TIME::SystemTime time = {}; +- sscanf(entry->m_Description.date, "%hu-%hu-%huT%hu:%hu:%hu", &time.year, &time.month, &time.day, +- &time.hour, &time.minute, &time.second); +- pItem->m_dateTime = time; ++ if (entry->m_Description.date.GetLength()) ++ { ++ int year; ++ int month; ++ int day; ++ int hour; ++ int minute; ++ int second; ++ ++ sscanf(entry->m_Description.date, "%u-%u-%uT%u:%u:%u", &year, &month, &day, &hour, &minute, ++ &second); ++ ++ pItem->m_dateTime.SetDateTime(year, month, day, hour, minute, second); + } + + // if there is a thumbnail available set it here +diff --git a/xbmc/platform/posix/XTimeUtils.cpp b/xbmc/platform/posix/XTimeUtils.cpp +index e78e5cff48..7d4b17686a 100644 +--- a/xbmc/platform/posix/XTimeUtils.cpp ++++ b/xbmc/platform/posix/XTimeUtils.cpp +@@ -8,221 +8,10 @@ + + #include "utils/XTimeUtils.h" + +-#include "PosixTimezone.h" +- +-#include +-#include +-#include +- +-#include +- +-#if defined(TARGET_ANDROID) && !defined(__LP64__) +-#include +-#endif +- +-#define WIN32_TIME_OFFSET ((unsigned long long)(369 * 365 + 89) * 24 * 3600 * 10000000) +- + namespace KODI + { + namespace TIME + { + +-/* +- * A Leap year is any year that is divisible by four, but not by 100 unless also +- * divisible by 400 +- */ +-#define IsLeapYear(y) ((!(y % 4)) ? (((!(y % 400)) && (y % 100)) ? 1 : 0) : 0) +- +-uint32_t GetTimeZoneInformation(TimeZoneInformation* timeZoneInformation) +-{ +- if (!timeZoneInformation) +- return KODI_TIME_ZONE_ID_INVALID; +- +- struct tm t; +- time_t tt = time(NULL); +- if (localtime_r(&tt, &t)) +- timeZoneInformation->bias = -t.tm_gmtoff / 60; +- +- timeZoneInformation->standardName = tzname[0]; +- timeZoneInformation->daylightName = tzname[1]; +- +- return KODI_TIME_ZONE_ID_UNKNOWN; +-} +- +-void GetLocalTime(SystemTime* systemTime) +-{ +- const time_t t = time(NULL); +- struct tm now; +- +- localtime_r(&t, &now); +- systemTime->year = now.tm_year + 1900; +- systemTime->month = now.tm_mon + 1; +- systemTime->dayOfWeek = now.tm_wday; +- systemTime->day = now.tm_mday; +- systemTime->hour = now.tm_hour; +- systemTime->minute = now.tm_min; +- systemTime->second = now.tm_sec; +- systemTime->milliseconds = 0; +- // NOTE: localtime_r() is not required to set this, but we Assume that it's set here. +- g_timezone.m_IsDST = now.tm_isdst; +-} +- +-int FileTimeToLocalFileTime(const FileTime* fileTime, FileTime* localFileTime) +-{ +- ULARGE_INTEGER l; +- l.u.LowPart = fileTime->lowDateTime; +- l.u.HighPart = fileTime->highDateTime; +- +- time_t ft; +- struct tm tm_ft; +- FileTimeToTimeT(fileTime, &ft); +- localtime_r(&ft, &tm_ft); +- +- l.QuadPart += static_cast(tm_ft.tm_gmtoff) * 10000000; +- +- localFileTime->lowDateTime = l.u.LowPart; +- localFileTime->highDateTime = l.u.HighPart; +- return 1; +-} +- +-int SystemTimeToFileTime(const SystemTime* systemTime, FileTime* fileTime) +-{ +- static const int dayoffset[12] = {0, 31, 59, 90, 120, 151, 182, 212, 243, 273, 304, 334}; +-#if defined(TARGET_DARWIN) +- static std::mutex timegm_lock; +-#endif +- +- struct tm sysTime = {}; +- sysTime.tm_year = systemTime->year - 1900; +- sysTime.tm_mon = systemTime->month - 1; +- sysTime.tm_wday = systemTime->dayOfWeek; +- sysTime.tm_mday = systemTime->day; +- sysTime.tm_hour = systemTime->hour; +- sysTime.tm_min = systemTime->minute; +- sysTime.tm_sec = systemTime->second; +- sysTime.tm_yday = dayoffset[sysTime.tm_mon] + (sysTime.tm_mday - 1); +- sysTime.tm_isdst = g_timezone.m_IsDST; +- +- // If this is a leap year, and we're past the 28th of Feb, increment tm_yday. +- if (IsLeapYear(systemTime->year) && (sysTime.tm_yday > 58)) +- sysTime.tm_yday++; +- +-#if defined(TARGET_DARWIN) +- std::lock_guard lock(timegm_lock); +-#endif +- +-#if defined(TARGET_ANDROID) && !defined(__LP64__) +- time64_t t = timegm64(&sysTime); +-#else +- time_t t = timegm(&sysTime); +-#endif +- +- LARGE_INTEGER result; +- result.QuadPart = (long long)t * 10000000 + (long long)systemTime->milliseconds * 10000; +- result.QuadPart += WIN32_TIME_OFFSET; +- +- fileTime->lowDateTime = result.u.LowPart; +- fileTime->highDateTime = result.u.HighPart; +- +- return 1; +-} +- +-long CompareFileTime(const FileTime* fileTime1, const FileTime* fileTime2) +-{ +- ULARGE_INTEGER t1; +- t1.u.LowPart = fileTime1->lowDateTime; +- t1.u.HighPart = fileTime1->highDateTime; +- +- ULARGE_INTEGER t2; +- t2.u.LowPart = fileTime2->lowDateTime; +- t2.u.HighPart = fileTime2->highDateTime; +- +- if (t1.QuadPart == t2.QuadPart) +- return 0; +- else if (t1.QuadPart < t2.QuadPart) +- return -1; +- else +- return 1; +-} +- +-int FileTimeToSystemTime(const FileTime* fileTime, SystemTime* systemTime) +-{ +- LARGE_INTEGER file; +- file.u.LowPart = fileTime->lowDateTime; +- file.u.HighPart = fileTime->highDateTime; +- +- file.QuadPart -= WIN32_TIME_OFFSET; +- file.QuadPart /= 10000; /* to milliseconds */ +- systemTime->milliseconds = file.QuadPart % 1000; +- file.QuadPart /= 1000; /* to seconds */ +- +- time_t ft = file.QuadPart; +- +- struct tm tm_ft; +- gmtime_r(&ft, &tm_ft); +- +- systemTime->year = tm_ft.tm_year + 1900; +- systemTime->month = tm_ft.tm_mon + 1; +- systemTime->dayOfWeek = tm_ft.tm_wday; +- systemTime->day = tm_ft.tm_mday; +- systemTime->hour = tm_ft.tm_hour; +- systemTime->minute = tm_ft.tm_min; +- systemTime->second = tm_ft.tm_sec; +- +- return 1; +-} +- +-int LocalFileTimeToFileTime(const FileTime* localFileTime, FileTime* fileTime) +-{ +- ULARGE_INTEGER l; +- l.u.LowPart = localFileTime->lowDateTime; +- l.u.HighPart = localFileTime->highDateTime; +- +- l.QuadPart += (unsigned long long) timezone * 10000000; +- +- fileTime->lowDateTime = l.u.LowPart; +- fileTime->highDateTime = l.u.HighPart; +- +- return 1; +-} +- +- +-int FileTimeToTimeT(const FileTime* localFileTime, time_t* pTimeT) +-{ +- if (!localFileTime || !pTimeT) +- return false; +- +- ULARGE_INTEGER fileTime; +- fileTime.u.LowPart = localFileTime->lowDateTime; +- fileTime.u.HighPart = localFileTime->highDateTime; +- +- fileTime.QuadPart -= WIN32_TIME_OFFSET; +- fileTime.QuadPart /= 10000; /* to milliseconds */ +- fileTime.QuadPart /= 1000; /* to seconds */ +- +- time_t ft = fileTime.QuadPart; +- +- struct tm tm_ft; +- localtime_r(&ft, &tm_ft); +- +- *pTimeT = mktime(&tm_ft); +- return 1; +-} +- +-int TimeTToFileTime(time_t timeT, FileTime* localFileTime) +-{ +- if (!localFileTime) +- return false; +- +- ULARGE_INTEGER result; +- result.QuadPart = (unsigned long long) timeT * 10000000; +- result.QuadPart += WIN32_TIME_OFFSET; +- +- localFileTime->lowDateTime = result.u.LowPart; +- localFileTime->highDateTime = result.u.HighPart; +- +- return 1; +-} +- + } // namespace TIME + } // namespace KODI +diff --git a/xbmc/platform/posix/filesystem/PosixDirectory.cpp b/xbmc/platform/posix/filesystem/PosixDirectory.cpp +index 6685c0e1db..7482a73960 100644 +--- a/xbmc/platform/posix/filesystem/PosixDirectory.cpp ++++ b/xbmc/platform/posix/filesystem/PosixDirectory.cpp +@@ -79,10 +79,7 @@ bool CPosixDirectory::GetDirectory(const CURL& url, CFileItemList &items) + { + if (bStat || stat(pItem->GetPath().c_str(), &buffer) == 0) + { +- KODI::TIME::FileTime fileTime, localTime; +- KODI::TIME::TimeTToFileTime(buffer.st_mtime, &fileTime); +- KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime); +- pItem->m_dateTime = localTime; ++ pItem->m_dateTime = buffer.st_mtime; + + if (!pItem->m_bIsFolder) + pItem->m_dwSize = buffer.st_size; +diff --git a/xbmc/platform/posix/filesystem/SMBDirectory.cpp b/xbmc/platform/posix/filesystem/SMBDirectory.cpp +index e2305afb4a..f0b94c22fc 100644 +--- a/xbmc/platform/posix/filesystem/SMBDirectory.cpp ++++ b/xbmc/platform/posix/filesystem/SMBDirectory.cpp +@@ -134,10 +134,10 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items) + && strFile != "lost+found" + && aDir.type != SMBC_PRINTER_SHARE && aDir.type != SMBC_IPC_SHARE) + { +- int64_t iSize = 0; ++ int64_t iSize = 0; + bool bIsDir = true; +- int64_t lTimeDate = 0; + bool hidden = false; ++ struct stat info = {}; + + if(StringUtils::EndsWith(strFile, "$") && aDir.type == SMBC_FILE_SHARE ) + continue; +@@ -152,7 +152,6 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items) + // set this here to if the stat should fail + bIsDir = (aDir.type == SMBC_DIR); + +- struct stat info = {}; + if ((m_flags & DIR_FLAG_NO_FILE_INFO)==0 && CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_sambastatfiles) + { + // make sure we use the authenticated path which contains any default username +@@ -187,9 +186,6 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items) + CURL::GetRedacted(strFullName), errno, strerror(errno)); + + bIsDir = S_ISDIR(info.st_mode); +- lTimeDate = info.st_mtime; +- if(lTimeDate == 0) // if modification date is missing, use create date +- lTimeDate = info.st_ctime; + iSize = info.st_size; + } + else +@@ -200,10 +196,6 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items) + } + } + +- KODI::TIME::FileTime fileTime, localTime; +- KODI::TIME::TimeTToFileTime(lTimeDate, &fileTime); +- KODI::TIME::FileTimeToLocalFileTime(&fileTime, &localTime); +- + if (bIsDir) + { + CFileItemPtr pItem(new CFileItem(strFile)); +@@ -223,7 +215,7 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items) + URIUtils::AddSlashAtEnd(path); + pItem->SetPath(path); + pItem->m_bIsFolder = true; +- pItem->m_dateTime=localTime; ++ pItem->m_dateTime = info.st_mtime != 0 ? info.st_mtime : info.st_ctime; + if (hidden) + pItem->SetProperty("file:hidden", true); + items.Add(pItem); +@@ -234,7 +226,7 @@ bool CSMBDirectory::GetDirectory(const CURL& url, CFileItemList &items) + pItem->SetPath(strRoot + aDir.name); + pItem->m_bIsFolder = false; + pItem->m_dwSize = iSize; +- pItem->m_dateTime=localTime; ++ pItem->m_dateTime = info.st_mtime != 0 ? info.st_mtime : info.st_ctime; + if (hidden) + pItem->SetProperty("file:hidden", true); + items.Add(pItem); +diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp +index f7866278e7..e61761b0ab 100644 +--- a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp ++++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp +@@ -573,22 +573,16 @@ void CGUIDialogPVRTimerSettings::OnSettingAction(const std::shared_ptrGetId(); + if (settingId == SETTING_TMR_BEGIN) + { +- KODI::TIME::SystemTime timerStartTime; +- m_startLocalTime.GetAsSystemTime(timerStartTime); +- if (CGUIDialogNumeric::ShowAndGetTime(timerStartTime, g_localizeStrings.Get(14066))) ++ if (CGUIDialogNumeric::ShowAndGetTime(m_startLocalTime, g_localizeStrings.Get(14066))) + { +- SetTimeFromSystemTime(m_startLocalTime, timerStartTime); + m_timerStartTimeStr = m_startLocalTime.GetAsLocalizedTime("", false); + SetButtonLabels(); + } + } + else if (settingId == SETTING_TMR_END) + { +- KODI::TIME::SystemTime timerEndTime; +- m_endLocalTime.GetAsSystemTime(timerEndTime); +- if (CGUIDialogNumeric::ShowAndGetTime(timerEndTime, g_localizeStrings.Get(14066))) ++ if (CGUIDialogNumeric::ShowAndGetTime(m_endLocalTime, g_localizeStrings.Get(14066))) + { +- SetTimeFromSystemTime(m_endLocalTime, timerEndTime); + m_timerEndTimeStr = m_endLocalTime.GetAsLocalizedTime("", false); + SetButtonLabels(); + } +@@ -800,14 +794,6 @@ void CGUIDialogPVRTimerSettings::SetDateFromIndex(CDateTime& datetime, int date) + datetime.GetMinute(), datetime.GetSecond()); + } + +-void CGUIDialogPVRTimerSettings::SetTimeFromSystemTime(CDateTime& datetime, +- const KODI::TIME::SystemTime& time) +-{ +- const CDateTime newTime(time); +- datetime.SetDateTime(datetime.GetYear(), datetime.GetMonth(), datetime.GetDay(), +- newTime.GetHour(), newTime.GetMinute(), newTime.GetSecond()); +-} +- + void CGUIDialogPVRTimerSettings::InitializeTypesList() + { + m_typeEntries.clear(); +diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h +index d3e9e3eadf..d419e4a722 100644 +--- a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h ++++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h +@@ -59,7 +59,6 @@ private: + + static int GetDateAsIndex(const CDateTime& datetime); + static void SetDateFromIndex(CDateTime& datetime, int date); +- static void SetTimeFromSystemTime(CDateTime& datetime, const KODI::TIME::SystemTime& time); + + static int GetWeekdaysFromSetting(const std::shared_ptr& setting); + +diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp +index f4c247f910..d85e7bf493 100644 +--- a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp ++++ b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp +@@ -800,13 +800,12 @@ bool CGUIWindowPVRGuideBase::OpenDateSelectionDialog() + { + bool bReturn = false; + +- KODI::TIME::SystemTime date; + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); +- epgGridContainer->GetSelectedDate().GetAsSystemTime(date); ++ CDateTime datetime = epgGridContainer->GetSelectedDate(); + +- if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(19288))) /* Go to date */ ++ if (CGUIDialogNumeric::ShowAndGetDate(datetime, g_localizeStrings.Get(19288))) /* Go to date */ + { +- epgGridContainer->GoToDate(CDateTime(date)); ++ epgGridContainer->GoToDate(datetime); + bReturn = true; + } + +diff --git a/xbmc/settings/windows/GUIControlSettings.cpp b/xbmc/settings/windows/GUIControlSettings.cpp +index fcbf1fa908..6c9eb3ea68 100644 +--- a/xbmc/settings/windows/GUIControlSettings.cpp ++++ b/xbmc/settings/windows/GUIControlSettings.cpp +@@ -989,21 +989,19 @@ bool CGUIControlButtonSetting::OnClick() + std::shared_ptr settingDate = + std::static_pointer_cast(m_pSetting); + +- KODI::TIME::SystemTime systemdate; +- settingDate->GetDate().GetAsSystemTime(systemdate); +- if (CGUIDialogNumeric::ShowAndGetDate(systemdate, Localize(buttonControl->GetHeading()))) +- SetValid(settingDate->SetDate(CDateTime(systemdate))); ++ CDateTime datetime = settingDate->GetDate(); ++ if (CGUIDialogNumeric::ShowAndGetDate(datetime, Localize(buttonControl->GetHeading()))) ++ SetValid(settingDate->SetDate(datetime)); + } + else if (controlFormat == "time") + { + std::shared_ptr settingTime = + std::static_pointer_cast(m_pSetting); + +- KODI::TIME::SystemTime systemtime; +- settingTime->GetTime().GetAsSystemTime(systemtime); ++ CDateTime datetime = settingTime->GetTime(); + +- if (CGUIDialogNumeric::ShowAndGetTime(systemtime, Localize(buttonControl->GetHeading()))) +- SetValid(settingTime->SetTime(CDateTime(systemtime))); ++ if (CGUIDialogNumeric::ShowAndGetTime(datetime, Localize(buttonControl->GetHeading()))) ++ SetValid(settingTime->SetTime(datetime)); + } + else if (controlFormat == "action") + { +diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp +index a553bd1887..a8e61e9785 100644 +--- a/xbmc/test/TestDateTime.cpp ++++ b/xbmc/test/TestDateTime.cpp +@@ -36,17 +36,16 @@ TEST_F(TestDateTime, DateTimeOperators) + EXPECT_FALSE(dateTime1 == dateTime2); + } + +-TEST_F(TestDateTime, SystemTimeOperators) ++TEST_F(TestDateTime, TimePointOperators) + { + CDateTime dateTime1(1991, 5, 14, 12, 34, 56); + CDateTime dateTime2(1991, 5, 14, 12, 34, 57); + +- KODI::TIME::SystemTime systemTime; +- dateTime2.GetAsSystemTime(systemTime); ++ auto tp = dateTime2.GetAsTimePoint(); + +- EXPECT_TRUE(dateTime1 < systemTime); +- EXPECT_FALSE(dateTime1 > systemTime); +- EXPECT_FALSE(dateTime1 == systemTime); ++ EXPECT_TRUE(dateTime1 < tp); ++ EXPECT_FALSE(dateTime1 > tp); ++ EXPECT_FALSE(dateTime1 == tp); + } + + TEST_F(TestDateTime, TimeTOperators) +@@ -529,15 +528,14 @@ TEST_F(TestDateTime, GetAsLocalized) + EXPECT_EQ(dateTime2.GetAsLocalizedTime(TIME_FORMAT(256)), "5"); + } + +-TEST_F(TestDateTime, GetAsSystemTime) ++TEST_F(TestDateTime, GetAsTimePoint) + { + CDateTime dateTime; + dateTime.SetDateTime(1991, 05, 14, 12, 34, 56); + +- KODI::TIME::SystemTime systemTime; +- dateTime.GetAsSystemTime(systemTime); ++ auto tp = dateTime.GetAsTimePoint(); + +- EXPECT_TRUE(dateTime == systemTime); ++ EXPECT_TRUE(dateTime == tp); + } + + TEST_F(TestDateTime, GetAsTime) +diff --git a/xbmc/utils/Archive.cpp b/xbmc/utils/Archive.cpp +index 4f69929a42..0275c057f9 100644 +--- a/xbmc/utils/Archive.cpp ++++ b/xbmc/utils/Archive.cpp +@@ -152,9 +152,9 @@ CArchive& CArchive::operator<<(const std::wstring& wstr) + return streamout(wstr.data(), size * sizeof(wchar_t)); + } + +-CArchive& CArchive::operator<<(const KODI::TIME::SystemTime& time) ++CArchive& CArchive::operator<<(const std::chrono::system_clock::time_point& time) + { +- return streamout(&time, sizeof(KODI::TIME::SystemTime)); ++ return streamout(&time, sizeof(std::chrono::system_clock::time_point)); + } + + CArchive& CArchive::operator<<(IArchivable& obj) +@@ -265,9 +265,9 @@ CArchive& CArchive::operator>>(std::wstring& wstr) + return *this; + } + +-CArchive& CArchive::operator>>(KODI::TIME::SystemTime& time) ++CArchive& CArchive::operator>>(std::chrono::system_clock::time_point& time) + { +- return streamin(&time, sizeof(KODI::TIME::SystemTime)); ++ return streamin(&time, sizeof(std::chrono::system_clock::time_point)); + } + + CArchive& CArchive::operator>>(IArchivable& obj) +diff --git a/xbmc/utils/Archive.h b/xbmc/utils/Archive.h +index a1af0c3cf8..86a2bebc23 100644 +--- a/xbmc/utils/Archive.h ++++ b/xbmc/utils/Archive.h +@@ -57,7 +57,7 @@ public: + CArchive& operator<<(char c); + CArchive& operator<<(const std::string &str); + CArchive& operator<<(const std::wstring& wstr); +- CArchive& operator<<(const KODI::TIME::SystemTime& time); ++ CArchive& operator<<(const std::chrono::system_clock::time_point& time); + CArchive& operator<<(IArchivable& obj); + CArchive& operator<<(const CVariant& variant); + CArchive& operator<<(const std::vector& strArray); +@@ -126,7 +126,7 @@ public: + + CArchive& operator>>(std::string &str); + CArchive& operator>>(std::wstring& wstr); +- CArchive& operator>>(KODI::TIME::SystemTime& time); ++ CArchive& operator>>(std::chrono::system_clock::time_point& time); + CArchive& operator>>(IArchivable& obj); + CArchive& operator>>(CVariant& variant); + CArchive& operator>>(std::vector& strArray); +diff --git a/xbmc/utils/RssManager.cpp b/xbmc/utils/RssManager.cpp +index b8d350cac5..5e77dd7c13 100644 +--- a/xbmc/utils/RssManager.cpp ++++ b/xbmc/utils/RssManager.cpp +@@ -142,7 +142,7 @@ bool CRssManager::Load() + //! What about the xml encoding? + std::string strUrl = pFeed->FirstChild()->ValueStr(); + set.url.push_back(strUrl); +- set.interval.push_back(iInterval); ++ set.interval.push_back(std::chrono::minutes(iInterval)); + } + pFeed = pFeed->NextSiblingElement("feed"); + } +diff --git a/xbmc/utils/RssManager.h b/xbmc/utils/RssManager.h +index 2b807d739f..59ac506307 100644 +--- a/xbmc/utils/RssManager.h ++++ b/xbmc/utils/RssManager.h +@@ -22,7 +22,7 @@ class IRssObserver; + typedef struct + { + bool rtl; +- std::vector interval; ++ std::vector interval; + std::vector url; + } RssSet; + typedef std::map RssUrls; +diff --git a/xbmc/utils/RssReader.cpp b/xbmc/utils/RssReader.cpp +index 0b227b63d3..f9f1e4a845 100644 +--- a/xbmc/utils/RssReader.cpp ++++ b/xbmc/utils/RssReader.cpp +@@ -51,11 +51,14 @@ CRssReader::~CRssReader() + if (m_pObserver) + m_pObserver->OnFeedRelease(); + StopThread(); +- for (unsigned int i = 0; i < m_vecTimeStamps.size(); i++) +- delete m_vecTimeStamps[i]; ++ m_vecTimeStamps.clear(); + } + +-void CRssReader::Create(IRssObserver* aObserver, const std::vector& aUrls, const std::vector ×, int spacesBetweenFeeds, bool rtl) ++void CRssReader::Create(IRssObserver* aObserver, ++ const std::vector& aUrls, ++ const std::vector& times, ++ int spacesBetweenFeeds, ++ bool rtl) + { + std::unique_lock lock(m_critical); + +@@ -73,9 +76,7 @@ void CRssReader::Create(IRssObserver* aObserver, const std::vector& + for (unsigned int i = 0; i < m_vecUpdateTimes.size(); ++i) + { + AddToQueue(i); +- KODI::TIME::SystemTime* time = new KODI::TIME::SystemTime; +- KODI::TIME::GetLocalTime(time); +- m_vecTimeStamps.push_back(time); ++ m_vecTimeStamps.push_back(std::chrono::system_clock::now().time_since_epoch()); + } + } + +@@ -395,18 +396,14 @@ void CRssReader::UpdateObserver() + + void CRssReader::CheckForUpdates() + { +- KODI::TIME::SystemTime time; +- KODI::TIME::GetLocalTime(&time); +- + for (unsigned int i = 0;i < m_vecUpdateTimes.size(); ++i ) + { +- if (m_requestRefresh || ((time.day * 24 * 60) + (time.hour * 60) + time.minute) - +- ((m_vecTimeStamps[i]->day * 24 * 60) + +- (m_vecTimeStamps[i]->hour * 60) + m_vecTimeStamps[i]->minute) > +- m_vecUpdateTimes[i]) ++ if (m_requestRefresh || ++ (std::chrono::system_clock::now().time_since_epoch() - m_vecTimeStamps[i] > ++ m_vecUpdateTimes[i])) + { + CLog::Log(LOGDEBUG, "Updating RSS"); +- KODI::TIME::GetLocalTime(m_vecTimeStamps[i]); ++ m_vecTimeStamps[i] = std::chrono::system_clock::now().time_since_epoch(); + AddToQueue(i); + } + } +diff --git a/xbmc/utils/RssReader.h b/xbmc/utils/RssReader.h +index ae9d2f73ca..43c6bde609 100644 +--- a/xbmc/utils/RssReader.h ++++ b/xbmc/utils/RssReader.h +@@ -28,7 +28,11 @@ public: + CRssReader(); + ~CRssReader() override; + +- void Create(IRssObserver* aObserver, const std::vector& aUrl, const std::vector& times, int spacesBetweenFeeds, bool rtl); ++ void Create(IRssObserver* aObserver, ++ const std::vector& aUrl, ++ const std::vector& times, ++ int spacesBetweenFeeds, ++ bool rtl); + bool Parse(const std::string& data, int iFeed, const std::string& charset); + void getFeed(vecText &text); + void AddTag(const std::string &addTag); +@@ -52,8 +56,8 @@ private: + + std::vector m_strFeed; + std::vector m_strColors; +- std::vector m_vecTimeStamps; +- std::vector m_vecUpdateTimes; ++ std::vector m_vecTimeStamps; ++ std::vector m_vecUpdateTimes; + int m_spacesBetweenFeeds; + CXBMCTinyXML m_xml; + std::list m_tagSet; +diff --git a/xbmc/utils/XTimeUtils.h b/xbmc/utils/XTimeUtils.h +index 91cda40305..9d3b0e86a3 100644 +--- a/xbmc/utils/XTimeUtils.h ++++ b/xbmc/utils/XTimeUtils.h +@@ -27,42 +27,6 @@ namespace KODI + { + namespace TIME + { +-struct SystemTime +-{ +- unsigned short year; +- unsigned short month; +- unsigned short dayOfWeek; +- unsigned short day; +- unsigned short hour; +- unsigned short minute; +- unsigned short second; +- unsigned short milliseconds; +-}; +- +-struct TimeZoneInformation +-{ +- long bias; +- std::string standardName; +- SystemTime standardDate; +- long standardBias; +- std::string daylightName; +- SystemTime daylightDate; +- long daylightBias; +-}; +- +-constexpr int KODI_TIME_ZONE_ID_INVALID{-1}; +-constexpr int KODI_TIME_ZONE_ID_UNKNOWN{0}; +-constexpr int KODI_TIME_ZONE_ID_STANDARD{1}; +-constexpr int KODI_TIME_ZONE_ID_DAYLIGHT{2}; +- +-struct FileTime +-{ +- unsigned int lowDateTime; +- unsigned int highDateTime; +-}; +- +-void GetLocalTime(SystemTime* systemTime); +-uint32_t GetTimeZoneInformation(TimeZoneInformation* timeZoneInformation); + + template + void Sleep(std::chrono::duration duration) +@@ -76,13 +40,5 @@ void Sleep(std::chrono::duration duration) + std::this_thread::sleep_for(duration); + } + +-int FileTimeToLocalFileTime(const FileTime* fileTime, FileTime* localFileTime); +-int SystemTimeToFileTime(const SystemTime* systemTime, FileTime* fileTime); +-long CompareFileTime(const FileTime* fileTime1, const FileTime* fileTime2); +-int FileTimeToSystemTime(const FileTime* fileTime, SystemTime* systemTime); +-int LocalFileTimeToFileTime(const FileTime* LocalFileTime, FileTime* fileTime); +- +-int FileTimeToTimeT(const FileTime* localFileTime, time_t* pTimeT); +-int TimeTToFileTime(time_t timeT, FileTime* localFileTime); + } // namespace TIME + } // namespace KODI +diff --git a/xbmc/utils/test/TestArchive.cpp b/xbmc/utils/test/TestArchive.cpp +index 90628ea2ed..b9ab780cf9 100644 +--- a/xbmc/utils/test/TestArchive.cpp ++++ b/xbmc/utils/test/TestArchive.cpp +@@ -220,22 +220,22 @@ TEST_F(TestArchive, StringArchive) + EXPECT_STREQ(string_ref.c_str(), string_var.c_str()); + } + +-TEST_F(TestArchive, SystemTimeArchive) ++TEST_F(TestArchive, TimePointArchive) + { + ASSERT_NE(nullptr, file); +- KODI::TIME::SystemTime SystemTime_ref = {1, 2, 3, 4, 5, 6, 7, 8}; +- KODI::TIME::SystemTime SystemTime_var = {0, 0, 0, 0, 0, 0, 0, 0}; ++ std::chrono::system_clock::time_point tp_ref = std::chrono::system_clock::now(); ++ std::chrono::system_clock::time_point tp_var; + + CArchive arstore(file, CArchive::store); +- arstore << SystemTime_ref; ++ arstore << tp_ref; + arstore.Close(); + + ASSERT_EQ(0, file->Seek(0, SEEK_SET)); + CArchive arload(file, CArchive::load); +- arload >> SystemTime_var; ++ arload >> tp_var; + arload.Close(); + +- EXPECT_TRUE(!memcmp(&SystemTime_ref, &SystemTime_var, sizeof(KODI::TIME::SystemTime))); ++ EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(std::chrono::system_clock::time_point))); + } + + TEST_F(TestArchive, CVariantArchive) +@@ -335,8 +335,8 @@ TEST_F(TestArchive, MultiTypeArchive) + char char_ref = 'A', char_var = '\0'; + std::string string_ref = "test string", string_var; + std::wstring wstring_ref = L"test wstring", wstring_var; +- KODI::TIME::SystemTime SystemTime_ref = {1, 2, 3, 4, 5, 6, 7, 8}; +- KODI::TIME::SystemTime SystemTime_var = {0, 0, 0, 0, 0, 0, 0, 0}; ++ std::chrono::system_clock::time_point tp_ref = std::chrono::system_clock::now(); ++ std::chrono::system_clock::time_point tp_var; + CVariant CVariant_ref((int)1), CVariant_var; + std::vector strArray_ref, strArray_var; + strArray_ref.emplace_back("test strArray_ref 0"); +@@ -362,7 +362,7 @@ TEST_F(TestArchive, MultiTypeArchive) + arstore << char_ref; + arstore << string_ref; + arstore << wstring_ref; +- arstore << SystemTime_ref; ++ arstore << tp_ref; + arstore << CVariant_ref; + arstore << strArray_ref; + arstore << iArray_ref; +@@ -382,7 +382,7 @@ TEST_F(TestArchive, MultiTypeArchive) + arload >> char_var; + arload >> string_var; + arload >> wstring_var; +- arload >> SystemTime_var; ++ arload >> tp_var; + arload >> CVariant_var; + arload >> strArray_var; + arload >> iArray_var; +@@ -398,7 +398,7 @@ TEST_F(TestArchive, MultiTypeArchive) + EXPECT_EQ(char_ref, char_var); + EXPECT_STREQ(string_ref.c_str(), string_var.c_str()); + EXPECT_STREQ(wstring_ref.c_str(), wstring_var.c_str()); +- EXPECT_TRUE(!memcmp(&SystemTime_ref, &SystemTime_var, sizeof(KODI::TIME::SystemTime))); ++ EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(std::chrono::system_clock::time_point))); + EXPECT_TRUE(CVariant_var.isInteger()); + EXPECT_STREQ("test strArray_ref 0", strArray_var.at(0).c_str()); + EXPECT_STREQ("test strArray_ref 1", strArray_var.at(1).c_str()); +diff --git a/xbmc/video/VideoInfoScanner.cpp b/xbmc/video/VideoInfoScanner.cpp +index 92fa5c191f..8d9cf6979d 100644 +--- a/xbmc/video/VideoInfoScanner.cpp ++++ b/xbmc/video/VideoInfoScanner.cpp +@@ -2005,8 +2005,8 @@ namespace VIDEO + else + { + digest.Update(&pItem->m_dwSize, sizeof(pItem->m_dwSize)); +- KODI::TIME::FileTime time = pItem->m_dateTime; +- digest.Update(&time, sizeof(KODI::TIME::FileTime)); ++ const auto time = pItem->m_dateTime.GetAsTimePoint().time_since_epoch().count(); ++ digest.Update(&time, sizeof(std::chrono::nanoseconds)); + } + if (pItem->IsVideo() && !pItem->IsPlayList() && !pItem->IsNFO()) + count++; +-- +2.43.0 + + +From 466178a3ca4239ff1c837f28ba84283777519990 Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Wed, 21 Oct 2020 09:17:33 +0300 +Subject: [PATCH 07/19] TestDateTime: fix GetAsStringsWithBias and add Tzdata + tests + +The 'GetAsStringsWithBias' test logic assumed the timezone on +machine bullding Kodi is set to 'UTC'. This is not always the case, +plus hour value was declared with a typo. + +To address this, we compare outputs of two runs of date library, +and instead add a 'Tzdata' test case comparing local date and time +against computed values from different timezones: + +find /usr/share/zoneinfo/Etc/ -type f | sort -Vu | sed 's,^/usr/share/zoneinfo/,,' | while read TZ +do + TMSTR=$(LANG=C TZ="$TZ" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/') + echo " // LANG=C TZ=\"$TZ\" date '+%Y-%m-%dT%H:%M:%S%Ez' -d \"1991-05-14 12:34:56 UTC\" | sed 's/[0-9][0-9]$/:&/'" + echo " tps = date::floor(dateTime.GetAsTimePoint());" + echo " zone = date::make_zoned(\"$TZ\", tps);" + echo " EXPECT_EQ(date::format(\"%FT%T%Ez\", zone), \"$TMSTR\") << \"tzdata information not valid for '$TZ'\";" + echo "" +done +--- + xbmc/test/TestDateTime.cpp | 168 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 166 insertions(+), 2 deletions(-) + +diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp +index a8e61e9785..0ff2bb443b 100644 +--- a/xbmc/test/TestDateTime.cpp ++++ b/xbmc/test/TestDateTime.cpp +@@ -11,6 +11,7 @@ + #include "guilib/LocalizeStrings.h" + + #include ++#include + #include + + #define USE_OS_TZDB 0 +@@ -330,10 +331,11 @@ TEST_F(TestDateTime, GetAsStringsWithBias) + std::cout << dateTime.GetAsW3CDateTime(false) << std::endl; + std::cout << dateTime.GetAsW3CDateTime(true) << std::endl; + +- auto zone = date::make_zoned(date::current_zone(), dateTime.GetAsTimePoint()); ++ auto tps = date::floor(dateTime.GetAsTimePoint()); ++ auto zone = date::make_zoned(date::current_zone(), tps); + + EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 12:34:56 GMT"); +- EXPECT_EQ(dateTime.GetAsW3CDateTime(false), "1991-05-14T05:34:56" + date::format("%Ez", zone)); ++ EXPECT_EQ(dateTime.GetAsW3CDateTime(false), date::format("%FT%T%Ez", zone)); + EXPECT_EQ(dateTime.GetAsW3CDateTime(true), "1991-05-14T12:34:56Z"); + } + +@@ -601,3 +603,165 @@ TEST_F(TestDateTime, Reset) + EXPECT_EQ(dateTime.GetMinute(), 0); + EXPECT_EQ(dateTime.GetSecond(), 0); + } ++ ++TEST_F(TestDateTime, Tzdata) ++{ ++ CDateTime dateTime; ++ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56); ++ ++ // LANG=C TZ="Etc/GMT+1" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ auto tps = date::floor(dateTime.GetAsTimePoint()); ++ auto zone = date::make_zoned("Etc/GMT+1", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T11:34:56-01:00") ++ << "tzdata information not valid for 'Etc/GMT+1'"; ++ ++ // LANG=C TZ="Etc/GMT+2" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+2", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T10:34:56-02:00") ++ << "tzdata information not valid for 'Etc/GMT+2'"; ++ ++ // LANG=C TZ="Etc/GMT+3" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+3", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T09:34:56-03:00") ++ << "tzdata information not valid for 'Etc/GMT+3'"; ++ ++ // LANG=C TZ="Etc/GMT+4" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+4", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T08:34:56-04:00") ++ << "tzdata information not valid for 'Etc/GMT+4'"; ++ ++ // LANG=C TZ="Etc/GMT+5" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+5", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T07:34:56-05:00") ++ << "tzdata information not valid for 'Etc/GMT+5'"; ++ ++ // LANG=C TZ="Etc/GMT+6" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+6", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T06:34:56-06:00") ++ << "tzdata information not valid for 'Etc/GMT+6'"; ++ ++ // LANG=C TZ="Etc/GMT+7" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+7", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T05:34:56-07:00") ++ << "tzdata information not valid for 'Etc/GMT+7'"; ++ ++ // LANG=C TZ="Etc/GMT+8" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+8", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T04:34:56-08:00") ++ << "tzdata information not valid for 'Etc/GMT+8'"; ++ ++ // LANG=C TZ="Etc/GMT+9" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+9", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T03:34:56-09:00") ++ << "tzdata information not valid for 'Etc/GMT+9'"; ++ ++ // LANG=C TZ="Etc/GMT+10" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+10", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T02:34:56-10:00") ++ << "tzdata information not valid for 'Etc/GMT+10'"; ++ ++ // LANG=C TZ="Etc/GMT+11" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+11", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T01:34:56-11:00") ++ << "tzdata information not valid for 'Etc/GMT+11'"; ++ ++ // LANG=C TZ="Etc/GMT+12" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT+12", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T00:34:56-12:00") ++ << "tzdata information not valid for 'Etc/GMT+12'"; ++ ++ // LANG=C TZ="Etc/GMT-1" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-1", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T13:34:56+01:00") ++ << "tzdata information not valid for 'Etc/GMT-1'"; ++ ++ // LANG=C TZ="Etc/GMT-2" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-2", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T14:34:56+02:00") ++ << "tzdata information not valid for 'Etc/GMT-2'"; ++ ++ // LANG=C TZ="Etc/GMT-3" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-3", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T15:34:56+03:00") ++ << "tzdata information not valid for 'Etc/GMT-3'"; ++ ++ // LANG=C TZ="Etc/GMT-4" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-4", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T16:34:56+04:00") ++ << "tzdata information not valid for 'Etc/GMT-4'"; ++ ++ // LANG=C TZ="Etc/GMT-5" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-5", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T17:34:56+05:00") ++ << "tzdata information not valid for 'Etc/GMT-5'"; ++ ++ // LANG=C TZ="Etc/GMT-6" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-6", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T18:34:56+06:00") ++ << "tzdata information not valid for 'Etc/GMT-6'"; ++ ++ // LANG=C TZ="Etc/GMT-7" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-7", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T19:34:56+07:00") ++ << "tzdata information not valid for 'Etc/GMT-7'"; ++ ++ // LANG=C TZ="Etc/GMT-8" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-8", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T20:34:56+08:00") ++ << "tzdata information not valid for 'Etc/GMT-8'"; ++ ++ // LANG=C TZ="Etc/GMT-9" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-9", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T21:34:56+09:00") ++ << "tzdata information not valid for 'Etc/GMT-9'"; ++ ++ // LANG=C TZ="Etc/GMT-10" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-10", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T22:34:56+10:00") ++ << "tzdata information not valid for 'Etc/GMT-10'"; ++ ++ // LANG=C TZ="Etc/GMT-11" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-11", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-14T23:34:56+11:00") ++ << "tzdata information not valid for 'Etc/GMT-11'"; ++ ++ // LANG=C TZ="Etc/GMT-12" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-12", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T00:34:56+12:00") ++ << "tzdata information not valid for 'Etc/GMT-12'"; ++ ++ // LANG=C TZ="Etc/GMT-13" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-13", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T01:34:56+13:00") ++ << "tzdata information not valid for 'Etc/GMT-13'"; ++ ++ // LANG=C TZ="Etc/GMT-14" date '+%Y-%m-%dT%H:%M:%S%Ez' -d "1991-05-14 12:34:56 UTC" | sed 's/[0-9][0-9]$/:&/' ++ tps = date::floor(dateTime.GetAsTimePoint()); ++ zone = date::make_zoned("Etc/GMT-14", tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T02:34:56+14:00") ++ << "tzdata information not valid for 'Etc/GMT-14'"; ++} +-- +2.43.0 + + +From 72720c4e84bbbb1d5859cd727b1068a74a8be3af Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Tue, 3 Nov 2020 17:51:26 +0200 +Subject: [PATCH 08/19] Remove GetAsUTCDateTime and add GetAsLocalDateTime + +The 'm_time' now represents a UTC time-point, so 'GetAsUTCDateTime' +does nothing useful. Remove it fully, and add 'GetAsLocalDateTime' +function that we will use later to convert UTC (system) time to the +local time needed to get displayed in UI. + +Signed-off-by: Vasyl Gello +--- + xbmc/XBDateTime.cpp | 9 ++++++--- + xbmc/XBDateTime.h | 4 +++- + xbmc/network/WebServer.cpp | 6 +++--- + xbmc/pvr/epg/EpgContainer.cpp | 14 +++++++------- + xbmc/pvr/epg/EpgInfoTag.cpp | 4 ++-- + xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp | 1 - + xbmc/pvr/timers/PVRTimerRuleMatcher.cpp | 2 +- + xbmc/pvr/timers/PVRTimers.cpp | 1 - + xbmc/test/TestDateTime.cpp | 16 ++++++++-------- + 9 files changed, 30 insertions(+), 27 deletions(-) + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index d8187b71e6..afd2d8c6a5 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -1210,10 +1210,13 @@ std::string CDateTime::GetAsLocalizedTime(TIME_FORMAT format, bool withSeconds / + return GetAsLocalizedTime("", false); + } + +-CDateTime CDateTime::GetAsUTCDateTime() const ++CDateTime CDateTime::GetAsLocalDateTime() const + { +- CDateTime time(m_time); +- return time; ++ auto zone = date::make_zoned(date::current_zone(), m_time); ++ ++ return CDateTime( ++ std::chrono::duration_cast(zone.get_local_time().time_since_epoch()) ++ .count()); + } + + std::string CDateTime::GetAsRFC1123DateTime() const +diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h +index 16d6217c8a..a810619566 100644 +--- a/xbmc/XBDateTime.h ++++ b/xbmc/XBDateTime.h +@@ -157,7 +157,9 @@ public: + void GetAsTm(tm& time) const; + std::chrono::system_clock::time_point GetAsTimePoint() const; + +- CDateTime GetAsUTCDateTime() const; ++ /*! \brief convert UTC datetime to local datetime ++ */ ++ CDateTime GetAsLocalDateTime() const; + std::string GetAsSaveString() const; + std::string GetAsDBDateTime() const; + std::string GetAsDBDate() const; +diff --git a/xbmc/network/WebServer.cpp b/xbmc/network/WebServer.cpp +index 0ee1696033..fc3583c16d 100644 +--- a/xbmc/network/WebServer.cpp ++++ b/xbmc/network/WebServer.cpp +@@ -222,7 +222,7 @@ MHD_RESULT CWebServer::HandlePartialRequest(struct MHD_Connection* connection, + CDateTime ifUnmodifiedSinceDate; + // handle If-Modified-Since (but only if the response is cacheable) + if (cacheable && ifModifiedSinceDate.SetFromRFC1123DateTime(ifModifiedSince) && +- lastModified.GetAsUTCDateTime() <= ifModifiedSinceDate) ++ lastModified <= ifModifiedSinceDate) + { + struct MHD_Response* response = create_response(0, nullptr, MHD_NO, MHD_NO); + if (response == nullptr) +@@ -235,7 +235,7 @@ MHD_RESULT CWebServer::HandlePartialRequest(struct MHD_Connection* connection, + } + // handle If-Unmodified-Since + else if (ifUnmodifiedSinceDate.SetFromRFC1123DateTime(ifUnmodifiedSince) && +- lastModified.GetAsUTCDateTime() > ifUnmodifiedSinceDate) ++ lastModified > ifUnmodifiedSinceDate) + return SendErrorResponse(request, MHD_HTTP_PRECONDITION_FAILED, request.method); + } + +@@ -504,7 +504,7 @@ bool CWebServer::IsRequestRanged(const HTTPRequest& request, const CDateTime& la + + // check if the last modification is newer than the If-Range date + // if so we have to server the whole file instead +- if (lastModified.GetAsUTCDateTime() > ifRangeDate) ++ if (lastModified > ifRangeDate) + ranges.Clear(); + } + } +diff --git a/xbmc/pvr/epg/EpgContainer.cpp b/xbmc/pvr/epg/EpgContainer.cpp +index d6f20153ef..38aa15809e 100644 +--- a/xbmc/pvr/epg/EpgContainer.cpp ++++ b/xbmc/pvr/epg/EpgContainer.cpp +@@ -335,7 +335,7 @@ void CPVREpgContainer::Process() + time_t iLastEpgCleanup = 0; + bool bUpdateEpg = true; + +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); ++ CDateTime::GetUTCDateTime().GetAsTime(iNow); + { + std::unique_lock lock(m_critSection); + bUpdateEpg = (iNow >= m_iNextEpgUpdate) && !m_bSuspended; +@@ -593,7 +593,7 @@ std::shared_ptr CPVREpgContainer::CreateChannelEpg(int iEpgId, const st + { + std::unique_lock lock(m_critSection); + m_bPreventUpdates = false; +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate); ++ CDateTime::GetUTCDateTime().GetAsTime(m_iNextEpgUpdate); + } + + m_events.Publish(PVREvent::EpgContainer); +@@ -613,7 +613,7 @@ bool CPVREpgContainer::RemoveOldEntries() + epgEntry.second->Cleanup(cleanupTime); + + std::unique_lock lock(m_critSection); +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iLastEpgCleanup); ++ CDateTime::GetUTCDateTime().GetAsTime(m_iLastEpgCleanup); + + return true; + } +@@ -787,7 +787,7 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */) + { + /* the update has been interrupted. try again later */ + time_t iNow; +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); ++ CDateTime::GetUTCDateTime().GetAsTime(iNow); + + std::unique_lock lock(m_critSection); + m_iNextEpgUpdate = iNow + advancedSettings->m_iEpgRetryInterruptedUpdateInterval; +@@ -795,7 +795,7 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */) + else + { + std::unique_lock lock(m_critSection); +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgUpdate); ++ CDateTime::GetUTCDateTime().GetAsTime(m_iNextEpgUpdate); + m_iNextEpgUpdate += advancedSettings->m_iEpgUpdateCheckInterval; + if (m_pendingUpdates == pendingUpdates) + m_pendingUpdates = 0; +@@ -852,7 +852,7 @@ bool CPVREpgContainer::CheckPlayingEvents() + m_critSection.unlock(); + + time_t iNow; +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); ++ CDateTime::GetUTCDateTime().GetAsTime(iNow); + if (iNow >= iNextEpgActiveTagCheck) + { + bFoundChanges = std::accumulate(epgs.cbegin(), epgs.cend(), bFoundChanges, +@@ -860,7 +860,7 @@ bool CPVREpgContainer::CheckPlayingEvents() + return epgEntry.second->CheckPlayingEvent() ? true : found; + }); + +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNextEpgActiveTagCheck); ++ CDateTime::GetUTCDateTime().GetAsTime(iNextEpgActiveTagCheck); + iNextEpgActiveTagCheck += CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iEpgActiveTagCheckInterval; + + /* pvr tags always start on the full minute */ +diff --git a/xbmc/pvr/epg/EpgInfoTag.cpp b/xbmc/pvr/epg/EpgInfoTag.cpp +index 8b0930e5d2..70daa5033b 100644 +--- a/xbmc/pvr/epg/EpgInfoTag.cpp ++++ b/xbmc/pvr/epg/EpgInfoTag.cpp +@@ -233,7 +233,7 @@ float CPVREpgInfoTag::ProgressPercentage() const + float fReturn = 0.0f; + + time_t currentTime, startTime, endTime; +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime); ++ CDateTime::GetUTCDateTime().GetAsTime(currentTime); + m_startTime.GetAsTime(startTime); + m_endTime.GetAsTime(endTime); + int iDuration = endTime - startTime > 0 ? endTime - startTime : 3600; +@@ -249,7 +249,7 @@ float CPVREpgInfoTag::ProgressPercentage() const + int CPVREpgInfoTag::Progress() const + { + time_t currentTime, startTime; +- CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(currentTime); ++ CDateTime::GetUTCDateTime().GetAsTime(currentTime); + m_startTime.GetAsTime(startTime); + int iDuration = currentTime - startTime; + +diff --git a/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp b/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp +index 1a0a99a7a2..5e27ea69c5 100644 +--- a/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp ++++ b/xbmc/pvr/guilib/PVRGUIActionsPowerManagement.cpp +@@ -92,7 +92,6 @@ bool CPVRGUIActionsPowerManagement::CanSystemPowerdown(bool bAskUser /*= true*/) + CDateTime dailywakeuptime; + dailywakeuptime.SetFromDBTime( + m_settings.GetStringValue(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME)); +- dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); + + const CDateTimeSpan diff(dailywakeuptime - now); + int mins = diff.GetSecondsTotal() / 60; +diff --git a/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp b/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp +index 9178e85ec2..b34688bdce 100644 +--- a/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp ++++ b/xbmc/pvr/timers/PVRTimerRuleMatcher.cpp +@@ -66,7 +66,7 @@ CDateTime CPVRTimerRuleMatcher::GetNextTimerStart() const + } + } + +- return nextStart.GetAsUTCDateTime(); ++ return nextStart; + } + + bool CPVRTimerRuleMatcher::Matches(const std::shared_ptr& epgTag) const +diff --git a/xbmc/pvr/timers/PVRTimers.cpp b/xbmc/pvr/timers/PVRTimers.cpp +index 760bb192f7..5fe64ec7e4 100644 +--- a/xbmc/pvr/timers/PVRTimers.cpp ++++ b/xbmc/pvr/timers/PVRTimers.cpp +@@ -1274,7 +1274,6 @@ CDateTime CPVRTimers::GetNextEventTime() const + CDateTime dailywakeuptime; + dailywakeuptime.SetFromDBTime( + m_settings.GetStringValue(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME)); +- dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); + + dailywakeuptime.SetDateTime(now.GetYear(), now.GetMonth(), now.GetDay(), + dailywakeuptime.GetHour(), dailywakeuptime.GetMinute(), +diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp +index 0ff2bb443b..4c55fafe06 100644 +--- a/xbmc/test/TestDateTime.cpp ++++ b/xbmc/test/TestDateTime.cpp +@@ -573,20 +573,20 @@ TEST_F(TestDateTime, GetAsTm) + } + } + +-TEST_F(TestDateTime, GetAsUTCDateTime) ++TEST_F(TestDateTime, GetAsLocalDateTime) + { + CDateTime dateTime1; + dateTime1.SetDateTime(1991, 05, 14, 12, 34, 56); + + CDateTime dateTime2; +- dateTime2 = dateTime1.GetAsUTCDateTime(); ++ dateTime2 = dateTime1.GetAsLocalDateTime(); + +- EXPECT_EQ(dateTime2.GetYear(), 1991); +- EXPECT_EQ(dateTime2.GetMonth(), 5); +- EXPECT_EQ(dateTime2.GetDay(), 14); +- EXPECT_EQ(dateTime2.GetHour(), 12); +- EXPECT_EQ(dateTime2.GetMinute(), 34); +- EXPECT_EQ(dateTime2.GetSecond(), 56); ++ auto zoned_time = date::make_zoned(date::current_zone(), dateTime1.GetAsTimePoint()); ++ auto time = zoned_time.get_local_time().time_since_epoch(); ++ ++ CDateTime cmpTime(std::chrono::duration_cast(time).count()); ++ ++ EXPECT_TRUE(dateTime1 == cmpTime); + } + + TEST_F(TestDateTime, Reset) +-- +2.43.0 + + +From d394f4639fd434647a393b99956d22417c68401b Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Tue, 3 Nov 2020 22:49:09 +0200 +Subject: [PATCH 09/19] Compensate the representation change + +... from local time by default to UTC time by default. + +Namely, 'CDateTime::SetFromUTCDateTime' converted the UTC time +to local time in the implementation of CDateTime before +'std::chrono'. Now this conversion has gone, and to avoid +displaying PVR times in UTC, 'GetAsLocalDateTime' is added +where necessary. + +Signed-off-by: Vasyl Gello +--- + xbmc/pvr/epg/EpgInfoTag.cpp | 8 ++------ + xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp | 4 ++-- + xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp | 2 +- + xbmc/pvr/recordings/PVRRecording.cpp | 4 ++-- + 4 files changed, 7 insertions(+), 11 deletions(-) + +diff --git a/xbmc/pvr/epg/EpgInfoTag.cpp b/xbmc/pvr/epg/EpgInfoTag.cpp +index 70daa5033b..3f104aa032 100644 +--- a/xbmc/pvr/epg/EpgInfoTag.cpp ++++ b/xbmc/pvr/epg/EpgInfoTag.cpp +@@ -293,9 +293,7 @@ CDateTime CPVREpgInfoTag::StartAsUTC() const + + CDateTime CPVREpgInfoTag::StartAsLocalTime() const + { +- CDateTime retVal; +- retVal.SetFromUTCDateTime(m_startTime); +- return retVal; ++ return m_startTime.GetAsLocalDateTime(); + } + + CDateTime CPVREpgInfoTag::EndAsUTC() const +@@ -305,9 +303,7 @@ CDateTime CPVREpgInfoTag::EndAsUTC() const + + CDateTime CPVREpgInfoTag::EndAsLocalTime() const + { +- CDateTime retVal; +- retVal.SetFromUTCDateTime(m_endTime); +- return retVal; ++ return m_endTime.GetAsLocalDateTime(); + } + + void CPVREpgInfoTag::SetEndFromUTC(const CDateTime& end) +diff --git a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp +index e8fac010ca..ef7a715f70 100644 +--- a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp ++++ b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp +@@ -121,9 +121,9 @@ void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr& + //////////////////////////////////////////////////////////////////////// + // Create ruler items + CDateTime ruler; +- ruler.SetFromUTCDateTime(m_gridStart); ++ ruler.SetFromUTCDateTime(m_gridStart.GetAsLocalDateTime()); + CDateTime rulerEnd; +- rulerEnd.SetFromUTCDateTime(m_gridEnd); ++ rulerEnd.SetFromUTCDateTime(m_gridEnd.GetAsLocalDateTime()); + CFileItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedDate(true))); + rulerItem->SetProperty("DateLabel", true); + m_rulerItems.emplace_back(rulerItem); +diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp +index e5dfdb9ea0..3d2cf09b3f 100644 +--- a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp ++++ b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp +@@ -239,7 +239,7 @@ std::string CPVRGUITimesInfo::TimeToTimeString(time_t datetime, TIME_FORMAT form + { + CDateTime time; + time.SetFromUTCDateTime(datetime); +- return time.GetAsLocalizedTime(format, withSeconds); ++ return time.GetAsLocalDateTime().GetAsLocalizedTime(format, withSeconds); + } + + std::string CPVRGUITimesInfo::GetTimeshiftStartTime(TIME_FORMAT format) const +diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp +index a2e1cbd131..8e047b8d52 100644 +--- a/xbmc/pvr/recordings/PVRRecording.cpp ++++ b/xbmc/pvr/recordings/PVRRecording.cpp +@@ -524,7 +524,7 @@ void CPVRRecording::UpdatePath() + const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const + { + static CDateTime tmp; +- tmp.SetFromUTCDateTime(m_recordingTime); ++ tmp.SetFromUTCDateTime(m_recordingTime.GetAsLocalDateTime()); + + return tmp; + } +@@ -538,7 +538,7 @@ CDateTime CPVRRecording::EndTimeAsUTC() const + CDateTime CPVRRecording::EndTimeAsLocalTime() const + { + CDateTime ret; +- ret.SetFromUTCDateTime(EndTimeAsUTC()); ++ ret.SetFromUTCDateTime(EndTimeAsUTC().GetAsLocalDateTime()); + return ret; + } + +-- +2.43.0 + + +From e9cbb86184358f6eb3235a76936fedbf38ab4d97 Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Wed, 4 Nov 2020 11:13:15 +0200 +Subject: [PATCH 10/19] Get rid of {Set,}FromUTCDateTime + + * The 'const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const' + method needs 'static' CDateTime instance set explicitly. This manifested + in Debian Bug #980038 : the return value of function is explicitly nulled + out causing immediate crash when using any PVR addon manipulating PVR + recordings. + +Signed-off-by: Vasyl Gello +--- + xbmc/FileItem.cpp | 2 +- + xbmc/XBDateTime.cpp | 33 ++----------------- + xbmc/XBDateTime.h | 4 --- + .../savestates/SavestateDatabase.cpp | 2 +- + xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp | 8 ++--- + xbmc/pvr/epg/Epg.cpp | 2 +- + xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp | 4 +-- + xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp | 3 +- + xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp | 3 +- + xbmc/pvr/recordings/PVRRecording.cpp | 8 ++--- + xbmc/test/TestDateTime.cpp | 26 --------------- + 11 files changed, 14 insertions(+), 81 deletions(-) + +diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp +index 2eb5eb072c..8f14467aff 100644 +--- a/xbmc/FileItem.cpp ++++ b/xbmc/FileItem.cpp +@@ -199,7 +199,7 @@ CFileItem::CFileItem(const std::shared_ptr& filter) + + const CDateTime lastExec = filter->GetLastExecutedDateTime(); + if (lastExec.IsValid()) +- m_dateTime.SetFromUTCDateTime(lastExec); ++ m_dateTime = lastExec; + + SetArt("icon", "DefaultPVRSearch.png"); + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index afd2d8c6a5..86168ecc13 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -613,19 +613,6 @@ std::string CDateTime::GetAsSaveString() const + return date::format("%Y%m%d_%H%M%S", sp); + } + +-bool CDateTime::SetFromUTCDateTime(const CDateTime &dateTime) +-{ +- m_time = dateTime.m_time; +- m_state = valid; +- return true; +-} +- +-bool CDateTime::SetFromUTCDateTime(const time_t &dateTime) +-{ +- CDateTime tmp(dateTime); +- return SetFromUTCDateTime(tmp); +-} +- + bool CDateTime::SetFromW3CDate(const std::string &dateTime) + { + std::string date; +@@ -700,10 +687,8 @@ bool CDateTime::SetFromW3CDateTime(const std::string &dateTime, bool ignoreTimez + + if (!ignoreTimezone && !zone.empty()) + { +- // check if the timezone is UTC +- if (StringUtils::StartsWith(zone, "Z")) +- return SetFromUTCDateTime(tmpDateTime); +- else ++ // check if the timezone is not UTC ++ if (!StringUtils::StartsWith(zone, "Z")) + { + // retrieve the timezone offset (ignoring the + or -) + CDateTimeSpan zoneSpan; zoneSpan.SetFromTimeString(zone.substr(1)); +@@ -853,20 +838,6 @@ CDateTime CDateTime::FromW3CDateTime(const std::string &date, bool ignoreTimezon + return dt; + } + +-CDateTime CDateTime::FromUTCDateTime(const CDateTime &dateTime) +-{ +- CDateTime dt; +- dt.SetFromUTCDateTime(dateTime); +- return dt; +-} +- +-CDateTime CDateTime::FromUTCDateTime(const time_t &dateTime) +-{ +- CDateTime dt; +- dt.SetFromUTCDateTime(dateTime); +- return dt; +-} +- + CDateTime CDateTime::FromRFC1123DateTime(const std::string &dateTime) + { + CDateTime dt; +diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h +index a810619566..ff2d758caa 100644 +--- a/xbmc/XBDateTime.h ++++ b/xbmc/XBDateTime.h +@@ -78,8 +78,6 @@ public: + static CDateTime FromDBTime(const std::string &time); + static CDateTime FromW3CDate(const std::string &date); + static CDateTime FromW3CDateTime(const std::string &date, bool ignoreTimezone = false); +- static CDateTime FromUTCDateTime(const CDateTime &dateTime); +- static CDateTime FromUTCDateTime(const time_t &dateTime); + static CDateTime FromRFC1123DateTime(const std::string &dateTime); + + const CDateTime& operator =(const time_t& right); +@@ -144,8 +142,6 @@ public: + bool SetFromDBTime(const std::string &time); + bool SetFromW3CDate(const std::string &date); + bool SetFromW3CDateTime(const std::string &date, bool ignoreTimezone = false); +- bool SetFromUTCDateTime(const CDateTime &dateTime); +- bool SetFromUTCDateTime(const time_t &dateTime); + bool SetFromRFC1123DateTime(const std::string &dateTime); + + /*! \brief set from a database datetime format YYYY-MM-DD HH:MM:SS +diff --git a/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp b/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp +index 2611f25ad8..2a44e07596 100644 +--- a/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp ++++ b/xbmc/cores/RetroPlayer/savestates/SavestateDatabase.cpp +@@ -149,7 +149,7 @@ void CSavestateDatabase::GetSavestateItem(const ISavestate& savestate, + const std::string& savestatePath, + CFileItem& item) + { +- CDateTime dateUTC = CDateTime::FromUTCDateTime(savestate.Created()); ++ CDateTime dateUTC = savestate.Created(); + + std::string label; + std::string label2; +diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp +index 4cf7f2f0c4..13b826c1b1 100644 +--- a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp ++++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp +@@ -252,7 +252,7 @@ CDateTime CGUIDialogPVRGuideSearch::ReadDateTime(const std::string& strDate, con + sscanf(strTime.c_str(), "%d:%d", &iHours, &iMinutes); + dateTime.SetFromDBDate(strDate); + dateTime.SetDateTime(dateTime.GetYear(), dateTime.GetMonth(), dateTime.GetDay(), iHours, iMinutes, 0); +- return dateTime.GetAsUTCDateTime(); ++ return dateTime; + } + + bool CGUIDialogPVRGuideSearch::IsRadioSelected(int controlID) +@@ -358,10 +358,8 @@ void CGUIDialogPVRGuideSearch::Update() + if (!m_endDateTime.IsValid()) + m_endDateTime = m_startDateTime + CDateTimeSpan(10, 0, 0, 0); // default to start + 10 days + +- CDateTime startLocal; +- startLocal.SetFromUTCDateTime(m_startDateTime); +- CDateTime endLocal; +- endLocal.SetFromUTCDateTime(m_endDateTime); ++ CDateTime startLocal = m_startDateTime; ++ CDateTime endLocal = m_endDateTime; + + SET_CONTROL_LABEL2(CONTROL_EDIT_START_TIME, startLocal.GetAsLocalizedTime("", false)); + { +diff --git a/xbmc/pvr/epg/Epg.cpp b/xbmc/pvr/epg/Epg.cpp +index 1112b2ff89..bb0dbf967b 100644 +--- a/xbmc/pvr/epg/Epg.cpp ++++ b/xbmc/pvr/epg/Epg.cpp +@@ -264,7 +264,7 @@ bool CPVREpg::Update(time_t start, + + if (!m_lastScanTime.IsValid()) + { +- m_lastScanTime.SetFromUTCDateTime(time_t(0)); ++ m_lastScanTime = time_t(0); + m_bUpdateLastScanTime = true; + } + } +diff --git a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp +index ef7a715f70..3e89c06d6e 100644 +--- a/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp ++++ b/xbmc/pvr/guilib/GUIEPGGridContainerModel.cpp +@@ -121,9 +121,9 @@ void CGUIEPGGridContainerModel::Initialize(const std::unique_ptr& + //////////////////////////////////////////////////////////////////////// + // Create ruler items + CDateTime ruler; +- ruler.SetFromUTCDateTime(m_gridStart.GetAsLocalDateTime()); ++ ruler = m_gridStart.GetAsLocalDateTime(); + CDateTime rulerEnd; +- rulerEnd.SetFromUTCDateTime(m_gridEnd.GetAsLocalDateTime()); ++ rulerEnd = m_gridEnd.GetAsLocalDateTime(); + CFileItemPtr rulerItem(new CFileItem(ruler.GetAsLocalizedDate(true))); + rulerItem->SetProperty("DateLabel", true); + m_rulerItems.emplace_back(rulerItem); +diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp +index 9cffb9a934..80c25118b0 100644 +--- a/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp ++++ b/xbmc/pvr/guilib/guiinfo/PVRGUIInfo.cpp +@@ -568,8 +568,7 @@ bool CPVRGUIInfo::GetListItemAndPlayerLabel(const CFileItem* item, + { + case LISTITEM_DATE: + { +- CDateTime lastExecLocal; +- lastExecLocal.SetFromUTCDateTime(filter->GetLastExecutedDateTime()); ++ CDateTime lastExecLocal = filter->GetLastExecutedDateTime(); + strValue = GetAsLocalizedDateTimeString(lastExecLocal); + if (strValue.empty()) + strValue = g_localizeStrings.Get(10006); // "N/A" +diff --git a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp +index 3d2cf09b3f..c5b44b840c 100644 +--- a/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp ++++ b/xbmc/pvr/guilib/guiinfo/PVRGUITimesInfo.cpp +@@ -237,8 +237,7 @@ void CPVRGUITimesInfo::Update() + + std::string CPVRGUITimesInfo::TimeToTimeString(time_t datetime, TIME_FORMAT format, bool withSeconds) + { +- CDateTime time; +- time.SetFromUTCDateTime(datetime); ++ CDateTime time(datetime); + return time.GetAsLocalDateTime().GetAsLocalizedTime(format, withSeconds); + } + +diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp +index 8e047b8d52..5a82c7b1dc 100644 +--- a/xbmc/pvr/recordings/PVRRecording.cpp ++++ b/xbmc/pvr/recordings/PVRRecording.cpp +@@ -523,9 +523,7 @@ void CPVRRecording::UpdatePath() + + const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const + { +- static CDateTime tmp; +- tmp.SetFromUTCDateTime(m_recordingTime.GetAsLocalDateTime()); +- ++ static CDateTime tmp = m_recordingTime.GetAsLocalDateTime(); + return tmp; + } + +@@ -537,9 +535,7 @@ CDateTime CPVRRecording::EndTimeAsUTC() const + + CDateTime CPVRRecording::EndTimeAsLocalTime() const + { +- CDateTime ret; +- ret.SetFromUTCDateTime(EndTimeAsUTC().GetAsLocalDateTime()); +- return ret; ++ return EndTimeAsUTC().GetAsLocalDateTime(); + } + + bool CPVRRecording::WillBeExpiredWithNewLifetime(int iLifetime) const +diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp +index 4c55fafe06..ea910a8763 100644 +--- a/xbmc/test/TestDateTime.cpp ++++ b/xbmc/test/TestDateTime.cpp +@@ -240,32 +240,6 @@ TEST_F(TestDateTime, SetFromW3CDateTime) + EXPECT_EQ(dateTime2.GetSecond(), 30); + } + +-TEST_F(TestDateTime, SetFromUTCDateTime) +-{ +- CDateTime dateTime1; +- dateTime1.SetFromDBDateTime("1991-05-14 12:34:56"); +- +- CDateTime dateTime2; +- EXPECT_TRUE(dateTime2.SetFromUTCDateTime(dateTime1)); +- EXPECT_EQ(dateTime2.GetYear(), 1991); +- EXPECT_EQ(dateTime2.GetMonth(), 5); +- EXPECT_EQ(dateTime2.GetDay(), 14); +- EXPECT_EQ(dateTime2.GetHour(), 12); +- EXPECT_EQ(dateTime2.GetMinute(), 34); +- EXPECT_EQ(dateTime2.GetSecond(), 56); +- +- const time_t time = 674224496; +- +- CDateTime dateTime3; +- EXPECT_TRUE(dateTime3.SetFromUTCDateTime(time)); +- EXPECT_EQ(dateTime3.GetYear(), 1991); +- EXPECT_EQ(dateTime3.GetMonth(), 5); +- EXPECT_EQ(dateTime3.GetDay(), 14); +- EXPECT_EQ(dateTime3.GetHour(), 12); +- EXPECT_EQ(dateTime3.GetMinute(), 34); +- EXPECT_EQ(dateTime3.GetSecond(), 56); +-} +- + TEST_F(TestDateTime, SetFromRFC1123DateTime) + { + std::string dateTime1("Mon, 21 Oct 2018 12:16:24 GMT"); +-- +2.43.0 + + +From 9583d0b325df73f5e29b4c901715b9a6bc389295 Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Thu, 13 May 2021 11:18:31 +0000 +Subject: [PATCH 11/19] Fix one-time assignment of PVR recording times + +Fixes pvr.dvbviewer#49 +Closes: #984682 + +Signed-off-by: Vasyl Gello +--- + xbmc/pvr/recordings/PVRRecording.cpp | 5 ++--- + xbmc/pvr/recordings/PVRRecording.h | 2 +- + 2 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp +index 5a82c7b1dc..39eea88740 100644 +--- a/xbmc/pvr/recordings/PVRRecording.cpp ++++ b/xbmc/pvr/recordings/PVRRecording.cpp +@@ -521,10 +521,9 @@ void CPVRRecording::UpdatePath() + m_strChannelName, m_recordingTime, m_strRecordingId); + } + +-const CDateTime& CPVRRecording::RecordingTimeAsLocalTime() const ++CDateTime CPVRRecording::RecordingTimeAsLocalTime() const + { +- static CDateTime tmp = m_recordingTime.GetAsLocalDateTime(); +- return tmp; ++ return m_recordingTime.GetAsLocalDateTime(); + } + + CDateTime CPVRRecording::EndTimeAsUTC() const +diff --git a/xbmc/pvr/recordings/PVRRecording.h b/xbmc/pvr/recordings/PVRRecording.h +index bdd8378225..dd125ad1ea 100644 +--- a/xbmc/pvr/recordings/PVRRecording.h ++++ b/xbmc/pvr/recordings/PVRRecording.h +@@ -188,7 +188,7 @@ public: + * @brief Retrieve the recording start as local time + * @return the recording start time + */ +- const CDateTime& RecordingTimeAsLocalTime() const; ++ CDateTime RecordingTimeAsLocalTime() const; + + /*! + * @brief Retrieve the recording end as UTC time +-- +2.43.0 + + +From 732620e37163d653ec75bbefd2a58293e2303420 Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Wed, 21 Dec 2022 18:27:24 +0000 +Subject: [PATCH 12/19] Move date includes to separate header + +Signed-off-by: Vasyl Gello +--- + xbmc/ServiceManager.cpp | 5 +---- + xbmc/XBDateTime.cpp | 8 +------- + xbmc/addons/TimeZoneResource.cpp | 5 +++-- + xbmc/application/Application.cpp | 5 +---- + xbmc/platform/posix/PosixTimezone.cpp | 21 +++++++++------------ + xbmc/test/TestDateTime.cpp | 5 +---- + xbmc/utils/CMakeLists.txt | 1 + + xbmc/utils/DateLib.h | 24 ++++++++++++++++++++++++ + 8 files changed, 41 insertions(+), 33 deletions(-) + create mode 100644 xbmc/utils/DateLib.h + +diff --git a/xbmc/ServiceManager.cpp b/xbmc/ServiceManager.cpp +index 83070489e3..b95091e993 100644 +--- a/xbmc/ServiceManager.cpp ++++ b/xbmc/ServiceManager.cpp +@@ -45,15 +45,12 @@ + #include "storage/DetectDVDType.h" + #endif + #include "storage/MediaManager.h" ++#include "utils/DateLib.h" + #include "utils/FileExtensionProvider.h" + #include "utils/URIUtils.h" + #include "utils/log.h" + #include "weather/WeatherManager.h" + +-#define USE_OS_TZDB 0 +-#define HAS_REMOTE_API 0 +-#include +- + using namespace KODI; + + CServiceManager::CServiceManager() = default; +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index 86168ecc13..12ae853c43 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -11,18 +11,12 @@ + #include "LangInfo.h" + #include "guilib/LocalizeStrings.h" + #include "utils/Archive.h" ++#include "utils/DateLib.h" + #include "utils/StringUtils.h" +-#include "utils/XTimeUtils.h" + #include "utils/log.h" + + #include + +-#define USE_OS_TZDB 0 +-#define HAS_REMOTE_API 0 +-#include +-#include +-#include +- + static const char *MONTH_NAMES[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + + CDateTimeSpan::CDateTimeSpan(const CDateTimeSpan& span) +diff --git a/xbmc/addons/TimeZoneResource.cpp b/xbmc/addons/TimeZoneResource.cpp +index 94246c9e18..6dfba75e89 100644 +--- a/xbmc/addons/TimeZoneResource.cpp ++++ b/xbmc/addons/TimeZoneResource.cpp +@@ -8,11 +8,10 @@ + #include "TimeZoneResource.h" + + #include "addons/addoninfo/AddonType.h" ++#include "utils/DateLib.h" + #include "utils/StringUtils.h" + #include "utils/URIUtils.h" + +-#include +- + namespace ADDON + { + +@@ -33,7 +32,9 @@ bool CTimeZoneResource::IsInUse() const + + void CTimeZoneResource::OnPostInstall(bool update, bool modal) + { ++#if defined(DATE_INTERNAL_TZDATA) + date::reload_tzdb(); ++#endif + } + + } // namespace ADDON +diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp +index 3227933296..6aa584e5c9 100644 +--- a/xbmc/application/Application.cpp ++++ b/xbmc/application/Application.cpp +@@ -100,6 +100,7 @@ + #include "speech/ISpeechRecognition.h" + #include "threads/SingleLock.h" + #include "utils/CPUInfo.h" ++#include "utils/DateLib.h" + #include "utils/FileExtensionProvider.h" + #include "utils/RegExp.h" + #include "utils/SystemInfo.h" +@@ -196,12 +197,8 @@ + #include "pictures/GUIWindowSlideShow.h" + #include "utils/CharsetConverter.h" + +-#define USE_OS_TZDB 0 +-#define HAS_REMOTE_API 0 + #include + +-#include +- + using namespace ADDON; + using namespace XFILE; + #ifdef HAS_DVD_DRIVE +diff --git a/xbmc/platform/posix/PosixTimezone.cpp b/xbmc/platform/posix/PosixTimezone.cpp +index 7f0b8d4096..fa9ea1dde9 100644 +--- a/xbmc/platform/posix/PosixTimezone.cpp ++++ b/xbmc/platform/posix/PosixTimezone.cpp +@@ -9,25 +9,22 @@ + #ifdef TARGET_ANDROID + #include "platform/android/bionic_supplement/bionic_supplement.h" + #endif +-#include "PlatformDefs.h" + #include "PosixTimezone.h" +-#include "utils/SystemInfo.h" +- + #include "ServiceBroker.h" +-#include "utils/StringUtils.h" + #include "XBDateTime.h" +-#include "settings/lib/Setting.h" +-#include "settings/lib/SettingDefinitions.h" ++#include "filesystem/File.h" ++#include "platform/MessagePrinter.h" + #include "settings/Settings.h" + #include "settings/SettingsComponent.h" +-#include ++#include "settings/lib/Setting.h" ++#include "settings/lib/SettingDefinitions.h" ++#include "utils/DateLib.h" ++#include "utils/StringUtils.h" ++#include "utils/SystemInfo.h" + +-#define USE_OS_TZDB 0 +-#define HAS_REMOTE_API 0 +-#include "filesystem/File.h" +-#include "platform/MessagePrinter.h" ++#include + +-#include ++#include "PlatformDefs.h" + + void CPosixTimezone::Init() + { +diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp +index ea910a8763..de3f75e5e4 100644 +--- a/xbmc/test/TestDateTime.cpp ++++ b/xbmc/test/TestDateTime.cpp +@@ -9,15 +9,12 @@ + #include "LangInfo.h" + #include "XBDateTime.h" + #include "guilib/LocalizeStrings.h" ++#include "utils/DateLib.h" + + #include + #include + #include + +-#define USE_OS_TZDB 0 +-#define HAS_REMOTE_API 0 +-#include +-#include + #include + + class TestDateTime : public testing::Test +diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt +index 8e1328999f..bb4851c0ce 100644 +--- a/xbmc/utils/CMakeLists.txt ++++ b/xbmc/utils/CMakeLists.txt +@@ -97,6 +97,7 @@ set(HEADERS ActorProtocol.h + Crc32.h + CSSUtils.h + DatabaseUtils.h ++ DateLib.h + Digest.h + DiscsUtils.h + EndianSwap.h +diff --git a/xbmc/utils/DateLib.h b/xbmc/utils/DateLib.h +new file mode 100644 +index 0000000000..e39650aa03 +--- /dev/null ++++ b/xbmc/utils/DateLib.h +@@ -0,0 +1,24 @@ ++/* ++ * Copyright (C) 2005-2022 Team Kodi ++ * This file is part of Kodi - https://kodi.tv ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ * See LICENSES/README.md for more information. ++ */ ++ ++#if defined(DATE_INTERNAL_TZDATA) ++#define USE_OS_TZDB 0 ++#else ++#define USE_OS_TZDB 1 ++#endif ++ ++#if defined(DATE_HAS_STRINGVIEW) ++#define HAS_STRING_VIEW 1 ++#else ++#define HAS_STRING_VIEW 0 ++#endif ++ ++#define HAS_REMOTE_API 0 ++#include ++#include ++#include +-- +2.43.0 + + +From 46f92320cc18c3975bddb5dbe8e0d5b66a0b64c6 Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Fri, 14 May 2021 09:39:55 +0000 +Subject: [PATCH 13/19] CDateTime: Use 'long double' as underlying storage type + for time points + +This should fix the GUI date dialog issue: + +https://github.com/xbmc/xbmc/pull/18727#issuecomment-840805031 +https://github.com/xbmc/xbmc/pull/18727#issuecomment-840805031 + +Signed-off-by: Vasyl Gello +--- + xbmc/XBDateTime.cpp | 18 +++++++++--------- + xbmc/XBDateTime.h | 20 ++++++++++---------- + xbmc/dialogs/GUIDialogNumeric.cpp | 3 +-- + xbmc/utils/Archive.cpp | 8 ++++---- + xbmc/utils/Archive.h | 6 ++++-- + xbmc/utils/XTimeUtils.h | 3 +++ + xbmc/utils/test/TestArchive.cpp | 12 ++++++------ + 7 files changed, 37 insertions(+), 33 deletions(-) + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index 12ae853c43..fdcc27c9af 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -176,7 +176,7 @@ CDateTime::CDateTime(const time_t& time) + SetValid(true); + } + +-CDateTime::CDateTime(const std::chrono::system_clock::time_point& time) ++CDateTime::CDateTime(const KODI::TIME::time_point& time) + { + m_time = time; + SetValid(true); +@@ -223,7 +223,7 @@ const CDateTime& CDateTime::operator =(const tm& right) + return *this; + } + +-const CDateTime& CDateTime::operator=(const std::chrono::system_clock::time_point& right) ++const CDateTime& CDateTime::operator=(const KODI::TIME::time_point& right) + { + m_time = right; + SetValid(true); +@@ -321,32 +321,32 @@ bool CDateTime::operator !=(const tm& right) const + return !operator ==(right); + } + +-bool CDateTime::operator>(const std::chrono::system_clock::time_point& right) const ++bool CDateTime::operator>(const KODI::TIME::time_point& right) const + { + return m_time > right; + } + +-bool CDateTime::operator>=(const std::chrono::system_clock::time_point& right) const ++bool CDateTime::operator>=(const KODI::TIME::time_point& right) const + { + return operator>(right) || operator==(right); + } + +-bool CDateTime::operator<(const std::chrono::system_clock::time_point& right) const ++bool CDateTime::operator<(const KODI::TIME::time_point& right) const + { + return m_time < right; + } + +-bool CDateTime::operator<=(const std::chrono::system_clock::time_point& right) const ++bool CDateTime::operator<=(const KODI::TIME::time_point& right) const + { + return operator<(right) || operator==(right); + } + +-bool CDateTime::operator==(const std::chrono::system_clock::time_point& right) const ++bool CDateTime::operator==(const KODI::TIME::time_point& right) const + { + return m_time == right; + } + +-bool CDateTime::operator!=(const std::chrono::system_clock::time_point& right) const ++bool CDateTime::operator!=(const KODI::TIME::time_point& right) const + { + return !operator==(right); + } +@@ -577,7 +577,7 @@ void CDateTime::GetAsTm(tm& time) const + localtime_r(&t, &time); + } + +-std::chrono::system_clock::time_point CDateTime::GetAsTimePoint() const ++KODI::TIME::time_point CDateTime::GetAsTimePoint() const + { + return m_time; + } +diff --git a/xbmc/XBDateTime.h b/xbmc/XBDateTime.h +index ff2d758caa..b0a7f386d9 100644 +--- a/xbmc/XBDateTime.h ++++ b/xbmc/XBDateTime.h +@@ -65,7 +65,7 @@ public: + CDateTime& operator=(const CDateTime&) = default; + explicit CDateTime(const time_t& time); + explicit CDateTime(const tm& time); +- explicit CDateTime(const std::chrono::system_clock::time_point& time); ++ explicit CDateTime(const KODI::TIME::time_point& time); + CDateTime(int year, int month, int day, int hour, int minute, int second); + + static CDateTime GetCurrentDateTime(); +@@ -82,7 +82,7 @@ public: + + const CDateTime& operator =(const time_t& right); + const CDateTime& operator =(const tm& right); +- const CDateTime& operator=(const std::chrono::system_clock::time_point& right); ++ const CDateTime& operator=(const KODI::TIME::time_point& right); + + bool operator >(const CDateTime& right) const; + bool operator >=(const CDateTime& right) const; +@@ -105,12 +105,12 @@ public: + bool operator ==(const tm& right) const; + bool operator !=(const tm& right) const; + +- bool operator>(const std::chrono::system_clock::time_point& right) const; +- bool operator>=(const std::chrono::system_clock::time_point& right) const; +- bool operator<(const std::chrono::system_clock::time_point& right) const; +- bool operator<=(const std::chrono::system_clock::time_point& right) const; +- bool operator==(const std::chrono::system_clock::time_point& right) const; +- bool operator!=(const std::chrono::system_clock::time_point& right) const; ++ bool operator>(const KODI::TIME::time_point& right) const; ++ bool operator>=(const KODI::TIME::time_point& right) const; ++ bool operator<(const KODI::TIME::time_point& right) const; ++ bool operator<=(const KODI::TIME::time_point& right) const; ++ bool operator==(const KODI::TIME::time_point& right) const; ++ bool operator!=(const KODI::TIME::time_point& right) const; + + CDateTime operator +(const CDateTimeSpan& right) const; + CDateTime operator -(const CDateTimeSpan& right) const; +@@ -151,7 +151,7 @@ public: + + void GetAsTime(time_t& time) const; + void GetAsTm(tm& time) const; +- std::chrono::system_clock::time_point GetAsTimePoint() const; ++ KODI::TIME::time_point GetAsTimePoint() const; + + /*! \brief convert UTC datetime to local datetime + */ +@@ -173,7 +173,7 @@ public: + bool IsValid() const; + + private: +- std::chrono::system_clock::time_point m_time; ++ KODI::TIME::time_point m_time; + + typedef enum _STATE + { +diff --git a/xbmc/dialogs/GUIDialogNumeric.cpp b/xbmc/dialogs/GUIDialogNumeric.cpp +index bba219e44e..985fd05722 100644 +--- a/xbmc/dialogs/GUIDialogNumeric.cpp ++++ b/xbmc/dialogs/GUIDialogNumeric.cpp +@@ -498,8 +498,7 @@ bool CGUIDialogNumeric::ShowAndGetSeconds(std::string &timeString, const std::st + CGUIDialogNumeric *pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_NUMERIC); + if (!pDialog) return false; + +- std::chrono::system_clock::time_point time{ +- std::chrono::seconds{StringUtils::TimeStringToSeconds(timeString)}}; ++ KODI::TIME::time_point time{std::chrono::seconds{StringUtils::TimeStringToSeconds(timeString)}}; + + CDateTime datetime(time); + +diff --git a/xbmc/utils/Archive.cpp b/xbmc/utils/Archive.cpp +index 0275c057f9..bb207c6ca5 100644 +--- a/xbmc/utils/Archive.cpp ++++ b/xbmc/utils/Archive.cpp +@@ -152,9 +152,9 @@ CArchive& CArchive::operator<<(const std::wstring& wstr) + return streamout(wstr.data(), size * sizeof(wchar_t)); + } + +-CArchive& CArchive::operator<<(const std::chrono::system_clock::time_point& time) ++CArchive& CArchive::operator<<(const KODI::TIME::time_point& time) + { +- return streamout(&time, sizeof(std::chrono::system_clock::time_point)); ++ return streamout(&time, sizeof(KODI::TIME::time_point)); + } + + CArchive& CArchive::operator<<(IArchivable& obj) +@@ -265,9 +265,9 @@ CArchive& CArchive::operator>>(std::wstring& wstr) + return *this; + } + +-CArchive& CArchive::operator>>(std::chrono::system_clock::time_point& time) ++CArchive& CArchive::operator>>(KODI::TIME::time_point& time) + { +- return streamin(&time, sizeof(std::chrono::system_clock::time_point)); ++ return streamin(&time, sizeof(KODI::TIME::time_point)); + } + + CArchive& CArchive::operator>>(IArchivable& obj) +diff --git a/xbmc/utils/Archive.h b/xbmc/utils/Archive.h +index 86a2bebc23..ece5f9ac9c 100644 +--- a/xbmc/utils/Archive.h ++++ b/xbmc/utils/Archive.h +@@ -8,6 +8,8 @@ + + #pragma once + ++#include "utils/XTimeUtils.h" ++ + #include + #include + #include +@@ -57,7 +59,7 @@ public: + CArchive& operator<<(char c); + CArchive& operator<<(const std::string &str); + CArchive& operator<<(const std::wstring& wstr); +- CArchive& operator<<(const std::chrono::system_clock::time_point& time); ++ CArchive& operator<<(const KODI::TIME::time_point& time); + CArchive& operator<<(IArchivable& obj); + CArchive& operator<<(const CVariant& variant); + CArchive& operator<<(const std::vector& strArray); +@@ -126,7 +128,7 @@ public: + + CArchive& operator>>(std::string &str); + CArchive& operator>>(std::wstring& wstr); +- CArchive& operator>>(std::chrono::system_clock::time_point& time); ++ CArchive& operator>>(KODI::TIME::time_point& time); + CArchive& operator>>(IArchivable& obj); + CArchive& operator>>(CVariant& variant); + CArchive& operator>>(std::vector& strArray); +diff --git a/xbmc/utils/XTimeUtils.h b/xbmc/utils/XTimeUtils.h +index 9d3b0e86a3..ed1b25fb74 100644 +--- a/xbmc/utils/XTimeUtils.h ++++ b/xbmc/utils/XTimeUtils.h +@@ -28,6 +28,9 @@ namespace KODI + namespace TIME + { + ++using time_point = std::chrono::time_point>; ++ + template + void Sleep(std::chrono::duration duration) + { +diff --git a/xbmc/utils/test/TestArchive.cpp b/xbmc/utils/test/TestArchive.cpp +index b9ab780cf9..de912eba21 100644 +--- a/xbmc/utils/test/TestArchive.cpp ++++ b/xbmc/utils/test/TestArchive.cpp +@@ -223,8 +223,8 @@ TEST_F(TestArchive, StringArchive) + TEST_F(TestArchive, TimePointArchive) + { + ASSERT_NE(nullptr, file); +- std::chrono::system_clock::time_point tp_ref = std::chrono::system_clock::now(); +- std::chrono::system_clock::time_point tp_var; ++ KODI::TIME::time_point tp_ref = std::chrono::system_clock::now(); ++ KODI::TIME::time_point tp_var; + + CArchive arstore(file, CArchive::store); + arstore << tp_ref; +@@ -235,7 +235,7 @@ TEST_F(TestArchive, TimePointArchive) + arload >> tp_var; + arload.Close(); + +- EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(std::chrono::system_clock::time_point))); ++ EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(KODI::TIME::time_point))); + } + + TEST_F(TestArchive, CVariantArchive) +@@ -335,8 +335,8 @@ TEST_F(TestArchive, MultiTypeArchive) + char char_ref = 'A', char_var = '\0'; + std::string string_ref = "test string", string_var; + std::wstring wstring_ref = L"test wstring", wstring_var; +- std::chrono::system_clock::time_point tp_ref = std::chrono::system_clock::now(); +- std::chrono::system_clock::time_point tp_var; ++ KODI::TIME::time_point tp_ref = std::chrono::system_clock::now(); ++ KODI::TIME::time_point tp_var; + CVariant CVariant_ref((int)1), CVariant_var; + std::vector strArray_ref, strArray_var; + strArray_ref.emplace_back("test strArray_ref 0"); +@@ -398,7 +398,7 @@ TEST_F(TestArchive, MultiTypeArchive) + EXPECT_EQ(char_ref, char_var); + EXPECT_STREQ(string_ref.c_str(), string_var.c_str()); + EXPECT_STREQ(wstring_ref.c_str(), wstring_var.c_str()); +- EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(std::chrono::system_clock::time_point))); ++ EXPECT_TRUE(!memcmp(&tp_ref, &tp_var, sizeof(KODI::TIME::time_point))); + EXPECT_TRUE(CVariant_var.isInteger()); + EXPECT_STREQ("test strArray_ref 0", strArray_var.at(0).c_str()); + EXPECT_STREQ("test strArray_ref 1", strArray_var.at(1).c_str()); +-- +2.43.0 + + +From c2f4add32ebf1c3a965f1a88af0d49afcdf9e847 Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Fri, 14 May 2021 12:35:54 +0000 +Subject: [PATCH 14/19] Use "long double" type in time_t / tm conversions + +Signed-off-by: Vasyl Gello +--- + xbmc/XBDateTime.cpp | 60 +++++++++++++++++++++++++-------------------- + 1 file changed, 34 insertions(+), 26 deletions(-) + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index fdcc27c9af..c71478085b 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -93,10 +93,10 @@ const CDateTimeSpan& CDateTimeSpan::operator -=(const CDateTimeSpan& right) + + void CDateTimeSpan::SetDateTimeSpan(int day, int hour, int minute, int second) + { +- m_timeSpan = std::chrono::duration_cast(date::days(day)) + +- std::chrono::duration_cast(std::chrono::hours(hour)) + +- std::chrono::duration_cast(std::chrono::minutes(minute)) + +- std::chrono::duration_cast(std::chrono::seconds(second)); ++ m_timeSpan = date::floor(date::days(day)) + ++ date::floor(std::chrono::hours(hour)) + ++ date::floor(std::chrono::minutes(minute)) + ++ date::floor(std::chrono::seconds(second)); + } + + void CDateTimeSpan::SetFromTimeString(const std::string& time) // hh:mm +@@ -140,7 +140,7 @@ int CDateTimeSpan::GetSeconds() const + + int CDateTimeSpan::GetSecondsTotal() const + { +- return std::chrono::duration_cast(m_timeSpan).count(); ++ return date::floor(m_timeSpan).count(); + } + + void CDateTimeSpan::SetFromPeriod(const std::string &period) +@@ -172,7 +172,8 @@ CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time) + + CDateTime::CDateTime(const time_t& time) + { +- m_time = std::chrono::system_clock::from_time_t(time); ++ Reset(); ++ m_time += std::chrono::duration(time); + SetValid(true); + } + +@@ -184,7 +185,8 @@ CDateTime::CDateTime(const KODI::TIME::time_point& time) + + CDateTime::CDateTime(const tm& time) + { +- m_time = std::chrono::system_clock::from_time_t(std::mktime(const_cast(&time))); ++ Reset(); ++ m_time += std::chrono::duration(std::mktime(const_cast(&time))); + SetValid(true); + } + +@@ -198,8 +200,7 @@ CDateTime CDateTime::GetCurrentDateTime() + auto zone = date::make_zoned(date::current_zone(), std::chrono::system_clock::now()); + + return CDateTime( +- std::chrono::duration_cast(zone.get_local_time().time_since_epoch()) +- .count()); ++ date::floor(zone.get_local_time().time_since_epoch()).count()); + } + + CDateTime CDateTime::GetUTCDateTime() +@@ -209,7 +210,8 @@ CDateTime CDateTime::GetUTCDateTime() + + const CDateTime& CDateTime::operator =(const time_t& right) + { +- m_time = std::chrono::system_clock::from_time_t(right); ++ Reset(); ++ m_time += std::chrono::duration(right); + SetValid(true); + + return *this; +@@ -217,7 +219,8 @@ const CDateTime& CDateTime::operator =(const time_t& right) + + const CDateTime& CDateTime::operator =(const tm& right) + { +- m_time = std::chrono::system_clock::from_time_t(std::mktime(const_cast(&right))); ++ Reset(); ++ m_time += std::chrono::duration(std::mktime(const_cast(&right))); + SetValid(true); + + return *this; +@@ -263,7 +266,8 @@ bool CDateTime::operator !=(const CDateTime& right) const + + bool CDateTime::operator >(const time_t& right) const + { +- return m_time > std::chrono::system_clock::from_time_t(right); ++ return m_time > ++ std::chrono::system_clock::from_time_t(0) + std::chrono::duration(right); + } + + bool CDateTime::operator >=(const time_t& right) const +@@ -273,7 +277,8 @@ bool CDateTime::operator >=(const time_t& right) const + + bool CDateTime::operator <(const time_t& right) const + { +- return m_time < std::chrono::system_clock::from_time_t(right); ++ return m_time < ++ std::chrono::system_clock::from_time_t(0) + std::chrono::duration(right); + } + + bool CDateTime::operator <=(const time_t& right) const +@@ -283,7 +288,8 @@ bool CDateTime::operator <=(const time_t& right) const + + bool CDateTime::operator ==(const time_t& right) const + { +- return m_time == std::chrono::system_clock::from_time_t(right); ++ return m_time == ++ std::chrono::system_clock::from_time_t(0) + std::chrono::duration(right); + } + + bool CDateTime::operator !=(const time_t& right) const +@@ -293,7 +299,8 @@ bool CDateTime::operator !=(const time_t& right) const + + bool CDateTime::operator >(const tm& right) const + { +- return m_time > std::chrono::system_clock::from_time_t(std::mktime(const_cast(&right))); ++ return m_time > std::chrono::system_clock::from_time_t(0) + ++ std::chrono::duration(std::mktime(const_cast(&right))); + } + + bool CDateTime::operator >=(const tm& right) const +@@ -303,7 +310,8 @@ bool CDateTime::operator >=(const tm& right) const + + bool CDateTime::operator <(const tm& right) const + { +- return m_time < std::chrono::system_clock::from_time_t(std::mktime(const_cast(&right))); ++ return m_time < std::chrono::system_clock::from_time_t(0) + ++ std::chrono::duration(std::mktime(const_cast(&right))); + } + + bool CDateTime::operator <=(const tm& right) const +@@ -313,7 +321,8 @@ bool CDateTime::operator <=(const tm& right) const + + bool CDateTime::operator ==(const tm& right) const + { +- return m_time == std::chrono::system_clock::from_time_t(std::mktime(const_cast(&right))); ++ return m_time == std::chrono::system_clock::from_time_t(0) + ++ std::chrono::duration(std::mktime(const_cast(&right))); + } + + bool CDateTime::operator !=(const tm& right) const +@@ -387,7 +396,7 @@ CDateTimeSpan CDateTime::operator -(const CDateTime& right) const + { + CDateTimeSpan left; + +- left.m_timeSpan = std::chrono::duration_cast(m_time - right.m_time); ++ left.m_timeSpan = date::floor(m_time - right.m_time); + return left; + } + +@@ -496,7 +505,7 @@ int CDateTime::GetYear() const + int CDateTime::GetHour() const + { + auto dp = date::floor(m_time); +- auto time = date::make_time(m_time - dp); ++ auto time = date::make_time(date::floor(m_time - dp)); + + return time.hours().count(); + } +@@ -504,7 +513,7 @@ int CDateTime::GetHour() const + int CDateTime::GetMinute() const + { + auto dp = date::floor(m_time); +- auto time = date::make_time(m_time - dp); ++ auto time = date::make_time(date::floor(m_time - dp)); + + return time.minutes().count(); + } +@@ -512,7 +521,7 @@ int CDateTime::GetMinute() const + int CDateTime::GetSecond() const + { + auto dp = date::floor(m_time); +- auto time = date::make_time(m_time - dp); ++ auto time = date::make_time(date::floor(m_time - dp)); + + return time.seconds().count(); + } +@@ -529,7 +538,7 @@ int CDateTime::GetMinuteOfDay() const + { + auto dp = date::floor(m_time); + ; +- auto time = date::make_time(m_time - dp); ++ auto time = date::make_time(date::floor(m_time - dp)); + + return time.hours().count() * 60 + time.minutes().count(); + } +@@ -566,12 +575,12 @@ bool CDateTime::SetTime(int hour, int minute, int second) + + void CDateTime::GetAsTime(time_t& time) const + { +- time = std::chrono::system_clock::to_time_t(m_time); ++ time = date::floor(m_time.time_since_epoch()).count(); + } + + void CDateTime::GetAsTm(tm& time) const + { +- auto t = std::chrono::system_clock::to_time_t(m_time); ++ auto t = date::floor(m_time.time_since_epoch()).count(); + + time = {}; + localtime_r(&t, &time); +@@ -1180,8 +1189,7 @@ CDateTime CDateTime::GetAsLocalDateTime() const + auto zone = date::make_zoned(date::current_zone(), m_time); + + return CDateTime( +- std::chrono::duration_cast(zone.get_local_time().time_since_epoch()) +- .count()); ++ date::floor(zone.get_local_time().time_since_epoch()).count()); + } + + std::string CDateTime::GetAsRFC1123DateTime() const +-- +2.43.0 + + +From 0a476801a4d337a1e3e9c7115a03c58d302cdd75 Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Mon, 17 May 2021 09:41:32 +0000 +Subject: [PATCH 15/19] Avoid time_t in tm conversions + +This should leave the possibility of time_t truncation on 32-bit +architectures only in GetAsTime(), what we can do nothing with. + +Signed-off-by: Vasyl Gello +--- + xbmc/XBDateTime.cpp | 56 +++++++++++++++++++++++++++++++++++++-------- + 1 file changed, 46 insertions(+), 10 deletions(-) + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index c71478085b..ce5c242128 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -186,7 +186,16 @@ CDateTime::CDateTime(const KODI::TIME::time_point& time) + CDateTime::CDateTime(const tm& time) + { + Reset(); +- m_time += std::chrono::duration(std::mktime(const_cast(&time))); ++ ++ auto ymd = date::local_days(date::year(time.tm_year + 1900) / date::month(time.tm_mon + 1) / ++ time.tm_mday); ++ auto dur = ymd + std::chrono::hours(time.tm_hour) + std::chrono::minutes(time.tm_min) + ++ std::chrono::seconds(time.tm_sec); ++ ++ auto timeT = date::floor(dur.time_since_epoch()).count(); ++ ++ m_time += std::chrono::duration(timeT); ++ + SetValid(true); + } + +@@ -220,7 +229,16 @@ const CDateTime& CDateTime::operator =(const time_t& right) + const CDateTime& CDateTime::operator =(const tm& right) + { + Reset(); +- m_time += std::chrono::duration(std::mktime(const_cast(&right))); ++ ++ auto ymd = date::local_days(date::year(right.tm_year + 1900) / date::month(right.tm_mon + 1) / ++ right.tm_mday); ++ auto dur = ymd + std::chrono::hours(right.tm_hour) + std::chrono::minutes(right.tm_min) + ++ std::chrono::seconds(right.tm_sec); ++ ++ auto timeT = date::floor(dur.time_since_epoch()).count(); ++ ++ m_time += std::chrono::duration(timeT); ++ + SetValid(true); + + return *this; +@@ -299,8 +317,8 @@ bool CDateTime::operator !=(const time_t& right) const + + bool CDateTime::operator >(const tm& right) const + { +- return m_time > std::chrono::system_clock::from_time_t(0) + +- std::chrono::duration(std::mktime(const_cast(&right))); ++ CDateTime temp(right); ++ return m_time > temp.m_time; + } + + bool CDateTime::operator >=(const tm& right) const +@@ -310,8 +328,8 @@ bool CDateTime::operator >=(const tm& right) const + + bool CDateTime::operator <(const tm& right) const + { +- return m_time < std::chrono::system_clock::from_time_t(0) + +- std::chrono::duration(std::mktime(const_cast(&right))); ++ CDateTime temp(right); ++ return m_time < temp.m_time; + } + + bool CDateTime::operator <=(const tm& right) const +@@ -321,8 +339,8 @@ bool CDateTime::operator <=(const tm& right) const + + bool CDateTime::operator ==(const tm& right) const + { +- return m_time == std::chrono::system_clock::from_time_t(0) + +- std::chrono::duration(std::mktime(const_cast(&right))); ++ CDateTime temp(right); ++ return m_time == temp.m_time; + } + + bool CDateTime::operator !=(const tm& right) const +@@ -580,10 +598,28 @@ void CDateTime::GetAsTime(time_t& time) const + + void CDateTime::GetAsTm(tm& time) const + { +- auto t = date::floor(m_time.time_since_epoch()).count(); ++ auto dp = date::floor(m_time); + + time = {}; +- localtime_r(&t, &time); ++ ++ auto ymd = date::year_month_day{dp}; ++ time.tm_year = int(ymd.year()) - 1900; ++ time.tm_mon = unsigned(ymd.month()) - 1; ++ time.tm_mday = unsigned(ymd.day()); ++ ++ auto hms = date::make_time(date::floor(m_time - dp)); ++ time.tm_hour = hms.hours().count(); ++ time.tm_min = hms.minutes().count(); ++ time.tm_sec = hms.seconds().count(); ++ ++ date::weekday wd{dp}; ++ time.tm_wday = wd.c_encoding(); ++ ++ auto newyear = date::sys_days(date::year(time.tm_year + 1900) / 1 / 1); ++ auto seconds_to_newyear = date::floor(m_time - newyear); ++ time.tm_yday = seconds_to_newyear.count() / 86400; ++ ++ time.tm_isdst = date::current_zone()->get_info(m_time).save.count() != 0; + } + + KODI::TIME::time_point CDateTime::GetAsTimePoint() const +-- +2.43.0 + + +From 431a3cdbdc388bb8661d4d35fe5a42b579e3fc86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?P=C3=A4r=20Bj=C3=B6rklund?= +Date: Sun, 31 Oct 2021 10:33:30 +0100 +Subject: [PATCH 16/19] Windows patches + +--- + xbmc/application/Application.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/xbmc/application/Application.cpp b/xbmc/application/Application.cpp +index 6aa584e5c9..df55665b5e 100644 +--- a/xbmc/application/Application.cpp ++++ b/xbmc/application/Application.cpp +@@ -357,7 +357,9 @@ bool CApplication::Create() + CServiceBroker::RegisterJobManager(std::make_shared()); + + // Initialize,timezone ++#if defined(TARGET_POSIX) + g_timezone.Init(); ++#endif + + // Announcement service + m_pAnnouncementManager = std::make_shared(); +-- +2.43.0 + + +From 257a56972008869e448fd81b15c67bb46c17f338 Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Mon, 11 Jul 2022 09:24:01 +0000 +Subject: [PATCH 17/19] XBDateTime: Use timezone info specified via timezone + picker. + +Fixes broken timezone picker as reported in Debian Bug#1011294. + +Also fixes TZ-related issue spotted on reproducibility build +where TZ envvar specifying timezone as absolute pathname on Linux +and FreeBSD screwed date lib up. + +Signed-off-by: Vasyl Gello +--- + xbmc/XBDateTime.cpp | 8 +++--- + xbmc/test/TestDateTime.cpp | 20 ++++++++++++--- + xbmc/utils/CMakeLists.txt | 1 + + xbmc/utils/DateLib.cpp | 51 ++++++++++++++++++++++++++++++++++++++ + xbmc/utils/DateLib.h | 13 ++++++++++ + 5 files changed, 86 insertions(+), 7 deletions(-) + create mode 100644 xbmc/utils/DateLib.cpp + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index ce5c242128..29118e7570 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -206,7 +206,7 @@ CDateTime::CDateTime(int year, int month, int day, int hour, int minute, int sec + + CDateTime CDateTime::GetCurrentDateTime() + { +- auto zone = date::make_zoned(date::current_zone(), std::chrono::system_clock::now()); ++ auto zone = date::make_zoned(KODI::TIME::GetTimeZone(), std::chrono::system_clock::now()); + + return CDateTime( + date::floor(zone.get_local_time().time_since_epoch()).count()); +@@ -619,7 +619,7 @@ void CDateTime::GetAsTm(tm& time) const + auto seconds_to_newyear = date::floor(m_time - newyear); + time.tm_yday = seconds_to_newyear.count() / 86400; + +- time.tm_isdst = date::current_zone()->get_info(m_time).save.count() != 0; ++ time.tm_isdst = KODI::TIME::GetTimeZone()->get_info(m_time).save.count() != 0; + } + + KODI::TIME::time_point CDateTime::GetAsTimePoint() const +@@ -1222,7 +1222,7 @@ std::string CDateTime::GetAsLocalizedTime(TIME_FORMAT format, bool withSeconds / + + CDateTime CDateTime::GetAsLocalDateTime() const + { +- auto zone = date::make_zoned(date::current_zone(), m_time); ++ auto zone = date::make_zoned(KODI::TIME::GetTimeZone(), m_time); + + return CDateTime( + date::floor(zone.get_local_time().time_since_epoch()).count()); +@@ -1247,7 +1247,7 @@ std::string CDateTime::GetAsW3CDateTime(bool asUtc /* = false */) const + if (asUtc) + return date::format("%FT%TZ", time); + +- auto zt = date::make_zoned(date::current_zone(), time); ++ auto zt = date::make_zoned(KODI::TIME::GetTimeZone(), time); + + return date::format("%FT%T%Ez", zt); + } +diff --git a/xbmc/test/TestDateTime.cpp b/xbmc/test/TestDateTime.cpp +index de3f75e5e4..6604146a61 100644 +--- a/xbmc/test/TestDateTime.cpp ++++ b/xbmc/test/TestDateTime.cpp +@@ -303,7 +303,7 @@ TEST_F(TestDateTime, GetAsStringsWithBias) + std::cout << dateTime.GetAsW3CDateTime(true) << std::endl; + + auto tps = date::floor(dateTime.GetAsTimePoint()); +- auto zone = date::make_zoned(date::current_zone(), tps); ++ auto zone = date::make_zoned(KODI::TIME::GetTimeZone(), tps); + + EXPECT_EQ(dateTime.GetAsRFC1123DateTime(), "Tue, 14 May 1991 12:34:56 GMT"); + EXPECT_EQ(dateTime.GetAsW3CDateTime(false), date::format("%FT%T%Ez", zone)); +@@ -552,12 +552,12 @@ TEST_F(TestDateTime, GetAsLocalDateTime) + CDateTime dateTime2; + dateTime2 = dateTime1.GetAsLocalDateTime(); + +- auto zoned_time = date::make_zoned(date::current_zone(), dateTime1.GetAsTimePoint()); ++ auto zoned_time = date::make_zoned(KODI::TIME::GetTimeZone(), dateTime1.GetAsTimePoint()); + auto time = zoned_time.get_local_time().time_since_epoch(); + + CDateTime cmpTime(std::chrono::duration_cast(time).count()); + +- EXPECT_TRUE(dateTime1 == cmpTime); ++ EXPECT_TRUE(dateTime2 == cmpTime); + } + + TEST_F(TestDateTime, Reset) +@@ -736,3 +736,17 @@ TEST_F(TestDateTime, Tzdata) + EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T02:34:56+14:00") + << "tzdata information not valid for 'Etc/GMT-14'"; + } ++ ++TEST_F(TestDateTime, ExtractTzName) ++{ ++ CDateTime dateTime; ++ dateTime.SetDateTime(1991, 05, 14, 12, 34, 56); ++ ++ auto tps = date::floor(dateTime.GetAsTimePoint()); ++ auto zone = date::make_zoned(KODI::TIME::ExtractTzName("/usr/share/zoneinfo/Etc/GMT-14"), tps); ++ EXPECT_EQ(date::format("%FT%T%Ez", zone), "1991-05-15T02:34:56+14:00") ++ << "extractTzName failed for '/usr/share/zoneinfo/Etc/GMT-14'"; ++ ++ EXPECT_EQ(KODI::TIME::ExtractTzName("/usr/share/z0neinfo/Etc/GMT+12"), "") ++ << "extractTzName failed for '/usr/share/z0neinfo/Etc/GMT+12'"; ++} +diff --git a/xbmc/utils/CMakeLists.txt b/xbmc/utils/CMakeLists.txt +index bb4851c0ce..8f3a199a65 100644 +--- a/xbmc/utils/CMakeLists.txt ++++ b/xbmc/utils/CMakeLists.txt +@@ -17,6 +17,7 @@ set(SOURCES ActorProtocol.cpp + Crc32.cpp + CSSUtils.cpp + DatabaseUtils.cpp ++ DateLib.cpp + Digest.cpp + DiscsUtils.cpp + EndianSwap.cpp +diff --git a/xbmc/utils/DateLib.cpp b/xbmc/utils/DateLib.cpp +new file mode 100644 +index 0000000000..bd86dd5791 +--- /dev/null ++++ b/xbmc/utils/DateLib.cpp +@@ -0,0 +1,51 @@ ++/* ++ * Copyright (C) 2005-2022 Team Kodi ++ * This file is part of Kodi - https://kodi.tv ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ * See LICENSES/README.md for more information. ++ */ ++ ++#include "DateLib.h" ++ ++#include "utils/XTimeUtils.h" ++ ++#include ++ ++const std::string KODI::TIME::ExtractTzName(const char* tzname) ++{ ++ if (tzname[0] != '/') ++ return tzname; ++ ++ std::string result = tzname; ++ const char zoneinfo[] = "zoneinfo"; ++ size_t pos = result.rfind(zoneinfo); ++ if (pos == result.npos) ++ { ++ return ""; ++ } ++ ++ pos = result.find('/', pos); ++ result.erase(0, pos + 1); ++ return result; ++} ++ ++const date::time_zone* KODI::TIME::GetTimeZone() ++{ ++#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) ++ const date::time_zone* tz = nullptr; ++ const char* tzname = std::getenv("TZ"); ++ ++ if (tzname && tzname[0] != '\0') ++ { ++ const std::string tzn = ExtractTzName(tzname); ++ if (!tzn.empty()) ++ tz = date::locate_zone(tzn); ++ } ++ ++ if (tz) ++ return tz; ++ else ++#endif ++ return date::current_zone(); ++} +diff --git a/xbmc/utils/DateLib.h b/xbmc/utils/DateLib.h +index e39650aa03..9838dd94fe 100644 +--- a/xbmc/utils/DateLib.h ++++ b/xbmc/utils/DateLib.h +@@ -19,6 +19,19 @@ + #endif + + #define HAS_REMOTE_API 0 ++#include ++ + #include + #include + #include ++ ++namespace KODI ++{ ++namespace TIME ++{ ++ ++const std::string ExtractTzName(const char* tzname); ++const date::time_zone* GetTimeZone(); ++ ++} // namespace TIME ++} // namespace KODI +-- +2.43.0 + + +From b67dabf8a304565713b413d4efb30c567a0a639b Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Mon, 9 Jan 2023 23:00:17 +0200 +Subject: [PATCH 18/19] Apply formatting fixes + +Signed-off-by: Vasyl Gello +--- + xbmc/XBDateTime.cpp | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +diff --git a/xbmc/XBDateTime.cpp b/xbmc/XBDateTime.cpp +index 29118e7570..402aecfa9c 100644 +--- a/xbmc/XBDateTime.cpp ++++ b/xbmc/XBDateTime.cpp +@@ -167,7 +167,7 @@ CDateTime::CDateTime() + + CDateTime::CDateTime(const CDateTime& time) : m_time(time.m_time) + { +- m_state=time.m_state; ++ m_state = time.m_state; + } + + CDateTime::CDateTime(const time_t& time) +@@ -217,7 +217,7 @@ CDateTime CDateTime::GetUTCDateTime() + return CDateTime(std::chrono::system_clock::now()); + } + +-const CDateTime& CDateTime::operator =(const time_t& right) ++const CDateTime& CDateTime::operator=(const time_t& right) + { + Reset(); + m_time += std::chrono::duration(right); +@@ -226,7 +226,7 @@ const CDateTime& CDateTime::operator =(const time_t& right) + return *this; + } + +-const CDateTime& CDateTime::operator =(const tm& right) ++const CDateTime& CDateTime::operator=(const tm& right) + { + Reset(); + +@@ -282,68 +282,68 @@ bool CDateTime::operator !=(const CDateTime& right) const + return !operator ==(right); + } + +-bool CDateTime::operator >(const time_t& right) const ++bool CDateTime::operator>(const time_t& right) const + { + return m_time > + std::chrono::system_clock::from_time_t(0) + std::chrono::duration(right); + } + +-bool CDateTime::operator >=(const time_t& right) const ++bool CDateTime::operator>=(const time_t& right) const + { + return operator >(right) || operator ==(right); + } + +-bool CDateTime::operator <(const time_t& right) const ++bool CDateTime::operator<(const time_t& right) const + { + return m_time < + std::chrono::system_clock::from_time_t(0) + std::chrono::duration(right); + } + +-bool CDateTime::operator <=(const time_t& right) const ++bool CDateTime::operator<=(const time_t& right) const + { + return operator <(right) || operator ==(right); + } + +-bool CDateTime::operator ==(const time_t& right) const ++bool CDateTime::operator==(const time_t& right) const + { + return m_time == + std::chrono::system_clock::from_time_t(0) + std::chrono::duration(right); + } + +-bool CDateTime::operator !=(const time_t& right) const ++bool CDateTime::operator!=(const time_t& right) const + { + return !operator ==(right); + } + +-bool CDateTime::operator >(const tm& right) const ++bool CDateTime::operator>(const tm& right) const + { + CDateTime temp(right); + return m_time > temp.m_time; + } + +-bool CDateTime::operator >=(const tm& right) const ++bool CDateTime::operator>=(const tm& right) const + { + return operator >(right) || operator ==(right); + } + +-bool CDateTime::operator <(const tm& right) const ++bool CDateTime::operator<(const tm& right) const + { + CDateTime temp(right); + return m_time < temp.m_time; + } + +-bool CDateTime::operator <=(const tm& right) const ++bool CDateTime::operator<=(const tm& right) const + { + return operator <(right) || operator ==(right); + } + +-bool CDateTime::operator ==(const tm& right) const ++bool CDateTime::operator==(const tm& right) const + { + CDateTime temp(right); + return m_time == temp.m_time; + } + +-bool CDateTime::operator !=(const tm& right) const ++bool CDateTime::operator!=(const tm& right) const + { + return !operator ==(right); + } +-- +2.43.0 + + +From ae3e34c85b2c8fe476d22b7adbe9e2457a025edd Mon Sep 17 00:00:00 2001 +From: Vasyl Gello +Date: Thu, 13 Apr 2023 20:18:28 +0300 +Subject: [PATCH 19/19] Move LoadTimeZoneDatabase to xbmc/utils/DateLib.cpp + +Signed-off-by: Vasyl Gello +--- + xbmc/addons/TimeZoneResource.cpp | 4 +--- + xbmc/settings/SettingsComponent.cpp | 5 +++++ + xbmc/utils/DateLib.cpp | 23 +++++++++++++++++++++++ + xbmc/utils/DateLib.h | 1 + + 4 files changed, 30 insertions(+), 3 deletions(-) + +diff --git a/xbmc/addons/TimeZoneResource.cpp b/xbmc/addons/TimeZoneResource.cpp +index 6dfba75e89..12c4371e19 100644 +--- a/xbmc/addons/TimeZoneResource.cpp ++++ b/xbmc/addons/TimeZoneResource.cpp +@@ -32,9 +32,7 @@ bool CTimeZoneResource::IsInUse() const + + void CTimeZoneResource::OnPostInstall(bool update, bool modal) + { +-#if defined(DATE_INTERNAL_TZDATA) +- date::reload_tzdb(); +-#endif ++ KODI::TIME::LoadTimeZoneDatabase(); + } + + } // namespace ADDON +diff --git a/xbmc/settings/SettingsComponent.cpp b/xbmc/settings/SettingsComponent.cpp +index 0f5f7ae8da..8ce2730d3b 100644 +--- a/xbmc/settings/SettingsComponent.cpp ++++ b/xbmc/settings/SettingsComponent.cpp +@@ -24,6 +24,7 @@ + #include "settings/AdvancedSettings.h" + #include "settings/Settings.h" + #include "settings/SubtitlesSettings.h" ++#include "utils/DateLib.h" + #include "utils/StringUtils.h" + #include "utils/URIUtils.h" + #include "utils/log.h" +@@ -56,6 +57,10 @@ void CSettingsComponent::Initialize() + if (!inited) + inited = InitDirectoriesWin32(params->HasPlatformDirectories()); + ++ // Try loading timezone information after directories were provisioned ++ //!@todo check if the whole dirs logic should be moved to AppEnvironment ++ KODI::TIME::LoadTimeZoneDatabase(); ++ + m_settings->Initialize(); + + m_advancedSettings->Initialize(*m_settings->GetSettingsManager()); +diff --git a/xbmc/utils/DateLib.cpp b/xbmc/utils/DateLib.cpp +index bd86dd5791..d8a6d67764 100644 +--- a/xbmc/utils/DateLib.cpp ++++ b/xbmc/utils/DateLib.cpp +@@ -49,3 +49,26 @@ const date::time_zone* KODI::TIME::GetTimeZone() + #endif + return date::current_zone(); + } ++ ++void KODI::TIME::LoadTimeZoneDatabase() ++{ ++#if defined(DATE_INTERNAL_TZDATA) ++ // First check the timezone resource from userprofile ++ auto tzdataPath = ++ CSpecialProtocol::TranslatePath("special://home/addons/resource.timezone/resources/tzdata"); ++ if (!XFILE::CDirectory::Exists(tzdataPath)) ++ { ++ // Then check system-wide Kodi profile and bail out if not found ++ tzdataPath = ++ CSpecialProtocol::TranslatePath("special://xbmc/addons/resource.timezone/resources/tzdata"); ++ if (!XFILE::CDirectory::Exists(tzdataPath)) ++ { ++ CLog::LogF(LOGFATAL, "failed to find resource.timezone"); ++ return; ++ } ++ } ++ ++ CLog::LogF(LOGDEBUG, "Loading tzdata from path: {}", tzdataPath); ++ date::set_install(tzdataPath); ++#endif ++} +diff --git a/xbmc/utils/DateLib.h b/xbmc/utils/DateLib.h +index 9838dd94fe..09c4ba0b17 100644 +--- a/xbmc/utils/DateLib.h ++++ b/xbmc/utils/DateLib.h +@@ -32,6 +32,7 @@ namespace TIME + + const std::string ExtractTzName(const char* tzname); + const date::time_zone* GetTimeZone(); ++void LoadTimeZoneDatabase(); + + } // namespace TIME + } // namespace KODI +-- +2.43.0 + -- cgit v1.2.3