summaryrefslogtreecommitdiffstats
path: root/debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch')
-rw-r--r--debian/patches/cdatetime-std-chrono/0001-Replace-Kodi-date-time-implementation-with-std-c.patch7004
1 files changed, 7004 insertions, 0 deletions
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 <lorusak@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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 <andrea@pappacoda.it>
++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 <vasek.gello@gmail.com>
++Date: Wed, 11 Jan 2023 11:35:41 +0200
++Subject: [PATCH 2/4] Fix Android build
++
++Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
++---
++ 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 <unistd.h>
++-# if !USE_OS_TZDB && !defined(INSTALL)
+++# if !USE_OS_TZDB && !defined(INSTALL) && !defined(__ANDROID__)
++ # include <wordexp.h>
++ # endif
++ # include <limits.h>
++@@ -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 <vasek.gello@gmail.com>
++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 <lorusak@gmail.com>
+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 @@
+ <extension-point id="resource.language" schema="language.xsd"/>
+ <extension-point id="resource.uisounds" schema="uisounds.xsd"/>
+ <extension-point id="resource.images" schema="images.xsd"/>
++ <extension-point id="resource.timezone" schema="timezone.xsd"/>
+ </addon>
+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 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE schema PUBLIC "-//W3C//DTD XMLSCHEMA 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd">
++<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
++ <xs:element name="extension">
++ <xs:complexType>
++ <xs:attribute name="point" type="xs:string" use="required"/>
++ <xs:attribute name="id" type="simpleIdentifier"/>
++ <xs:attribute name="name" type="xs:string"/>
++ </xs:complexType>
++ </xs:element>
++ <xs:simpleType name="simpleIdentifier">
++ <xs:restriction base="xs:string">
++ <xs:pattern value="kodi\.resource\.timezone"/>
++ </xs:restriction>
++ </xs:simpleType>
++</xs:schema>
+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 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<addon id="resource.timezone" version="1.0.0" name="Kodi Timezone Database" provider-name="Team Kodi">
++ <requires>
++ <import addon="kodi.resource" version="1.0.0"/>
++ </requires>
++ <extension point="kodi.resource.timezone"/>
++ <extension point="kodi.addon.metadata">
++ <summary lang="en_GB">Kodi Timezone Database</summary>
++ <description lang="en_GB">Kodi Timezone Database</description>
++ <platform>all</platform>
++ </extension>
++</addon>
+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 @@
+ <addon optional="true">repository.xbmc.org</addon>
+ <addon>resource.images.weathericons.default</addon>
+ <addon>resource.language.en_gb</addon>
++ <addon>resource.timezone</addon>
+ <addon>resource.uisounds.kodi</addon>
+ <addon>screensaver.xbmc.builtin.black</addon>
+ <addon>screensaver.xbmc.builtin.dim</addon>
+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 <date/tz.h>
++
+ 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<CLanguageResource>(info);
+ case AddonType::RESOURCE_UISOUNDS:
+ return std::make_shared<CUISoundsResource>(info);
++ case AddonType::RESOURCE_TIMEZONE:
++ return std::make_shared<CTimeZoneResource>(info);
+ case AddonType::REPOSITORY:
+ return std::make_shared<CRepository>(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 <date/tz.h>
++
++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<TypeMapping, 40> types =
++static constexpr const std::array<TypeMapping, 41> 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<TypeMapping, 40> 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 <mutex>
+
++#include <date/tz.h>
++
+ 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 <lorusak@gmail.com>
+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 <cstdlib>
+
+-#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 <date/date.h>
++#include <date/iso_week.h>
++#include <date/tz.h>
+
+-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<long long>(day) *SECONDS_PER_DAY*SECONDS_TO_FILETIME;
+- time.QuadPart+= static_cast<long long>(hour) *SECONDS_PER_HOUR*SECONDS_TO_FILETIME;
+- time.QuadPart+= static_cast<long long>(minute) *SECONDS_PER_MINUTE*SECONDS_TO_FILETIME;
+- time.QuadPart+= static_cast<long long>(second) *SECONDS_TO_FILETIME;
+-
+- FromLargeInt(time);
++ m_timeSpan = std::chrono::duration_cast<std::chrono::seconds>(date::days(day)) +
++ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::hours(hour)) +
++ std::chrono::duration_cast<std::chrono::seconds>(std::chrono::minutes(minute)) +
++ std::chrono::duration_cast<std::chrono::seconds>(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<date::days>(m_timeSpan).count();
+ }
+
+ int CDateTimeSpan::GetHours() const
+ {
+- LARGE_INTEGER time;
+- ToLargeInt(time);
++ auto time = date::floor<date::days>(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<date::days>(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<date::days>(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<std::chrono::seconds>(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<tm*>(&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<std::chrono::seconds>(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<tm*>(&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<tm*>(&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<tm*>(&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<tm*>(&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<std::chrono::seconds>(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<date::days>(m_time);
++ auto ymd = date::year_month_day{dp};
+
+- return st.day;
++ return static_cast<unsigned int>(ymd.day());
+ }
+
+ int CDateTime::GetMonth() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto ymd = date::year_month_day{dp};
+
+- return st.month;
++ return static_cast<unsigned int>(ymd.month());
+ }
+
+ int CDateTime::GetYear() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(m_time);
++ auto ymd = date::year_month_day{dp};
+
+- return st.year;
++ return static_cast<int>(ymd.year());
+ }
+
+ int CDateTime::GetHour() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto dp = date::floor<date::days>(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<date::days>(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<date::days>(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<date::days>(m_time);
++ auto yww = iso_week::year_weeknum_weekday{dp};
+
+- return st.dayOfWeek;
++ return static_cast<unsigned int>(yww.weekday());
+ }
+
+ int CDateTime::GetMinuteOfDay() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
+- return st.hour * 60 + st.minute;
++ auto dp = date::floor<std::chrono::hours>(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<long long>(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<std::chrono::seconds>(m_time);
++ return date::format("%T", sp);
+ }
+
+ std::string CDateTime::GetAsDBDateTime() const
+ {
+- KODI::TIME::SystemTime st;
+- GetAsSystemTime(st);
++ auto sp = date::floor<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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 <chrono>
+ #include <string>
+
+ #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<int64_t> 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<const CSetting>& set
+ if (settingId == CSettings::SETTING_LOCALE_TIMEZONE)
+ {
+ SetTimezone(std::static_pointer_cast<const CSettingString>(setting)->GetValue());
+-
+- CDateTime::ResetTimezoneBias();
+ }
+ else if (settingId == CSettings::SETTING_LOCALE_TIMEZONECOUNTRY)
+ {
+@@ -157,7 +155,6 @@ void CPosixTimezone::OnSettingChanged(const std::shared_ptr<const CSetting>& set
+ void CPosixTimezone::OnSettingsLoaded()
+ {
+ SetTimezone(CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOCALE_TIMEZONE));
+- CDateTime::ResetTimezoneBias();
+ }
+
+ std::vector<std::string> 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 <lorusak@gmail.com>
+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 <array>
+ #include <iostream>
+
++#define USE_OS_TZDB 0
++#define HAS_REMOTE_API 0
++#include <date/date.h>
++#include <date/tz.h>
+ #include <gtest/gtest.h>
+
+ 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 <lorusak@gmail.com>
+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<CJobManager>());
+
++ // Initialize,timezone
++ g_timezone.Init();
++
+ // Announcement service
+ m_pAnnouncementManager = std::make_shared<ANNOUNCEMENT::CAnnouncementManager>();
+ 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 <time.h>
+ #ifdef TARGET_ANDROID
+ #include "platform/android/bionic_supplement/bionic_supplement.h"
+ #endif
+@@ -23,120 +22,215 @@
+ #include "settings/SettingsComponent.h"
+ #include <stdlib.h>
+
+-#include <algorithm>
++#define USE_OS_TZDB 0
++#define HAS_REMOTE_API 0
++#include "filesystem/File.h"
++#include "platform/MessagePrinter.h"
+
+-CPosixTimezone::CPosixTimezone()
++#include <date/tz.h>
++
++void CPosixTimezone::Init()
+ {
+- char* line = NULL;
+- size_t linelen = 0;
+- int nameonfourthfield = 0;
+- std::string s;
+- std::vector<std::string> 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<std::string> 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<std::string> timezones;
++ timezones.push_back(timezoneName);
++ m_timezonesByCountryCode[countryCode] = timezones;
++ }
++ else
++ {
++ std::vector<std::string>& 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<std::string> 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<std::string> 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<std::string> timezones;
+- timezones.push_back(timezoneName);
+- m_timezonesByCountryCode[countryCode] = timezones;
+- }
+- else
+- {
+- std::vector<std::string>& timezones = m_timezonesByCountryCode[countryCode];
+- timezones.push_back(timezoneName);
+- }
+-
+- m_countriesByTimezoneName[timezoneName] = countryCode;
++ std::vector<std::string>& 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<const CSetting>& 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<std::string> CPosixTimezone::GetCounties()
++std::vector<std::string> CPosixTimezone::GetCountries()
+ {
+- return m_counties;
++ return m_countries;
+ }
+
+ std::vector<std::string> 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<std::string> countries = g_timezone.GetCounties();
+- for (unsigned int i = 0; i < countries.size(); i++)
+- list.emplace_back(countries[i], countries[i]);
++ std::vector<std::string> countries = g_timezone.GetCountries();
++ for (const auto& country : countries)
++ list.emplace_back(country, country);
+ }
+
+ void CPosixTimezone::SettingOptionsTimezonesFiller(const std::shared_ptr<const CSetting>& setting,
+@@ -254,12 +340,12 @@ void CPosixTimezone::SettingOptionsTimezonesFiller(const std::shared_ptr<const C
+ current = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
+ bool found = false;
+ std::vector<std::string> 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<const CSetting>& setting) override;
+
+@@ -29,7 +31,7 @@ public:
+
+ std::string GetOSConfiguredTimezone();
+
+- std::vector<std::string> GetCounties();
++ std::vector<std::string> GetCountries();
+ std::vector<std::string> GetTimezonesByCountry(const std::string& country);
+ std::string GetCountryByTimezone(const std::string& timezone);
+
+@@ -46,12 +48,12 @@ public:
+ void* data);
+
+ private:
+- std::vector<std::string> m_counties;
+- std::map<std::string, std::string> m_countryByCode;
+- std::map<std::string, std::string> m_countryByName;
++ std::vector<std::string> m_countries;
++ std::map<std::string, std::string> m_countryByCode;
++ std::map<std::string, std::string> m_countryByName;
+
+- std::map<std::string, std::vector<std::string> > m_timezonesByCountryCode;
+- std::map<std::string, std::string> m_countriesByTimezoneName;
++ std::map<std::string, std::vector<std::string>> m_timezonesByCountryCode;
++ std::map<std::string, std::string> m_countriesByTimezoneName;
+ };
+
+ extern CPosixTimezone g_timezone;
+--
+2.43.0
+
+
+From a36c95155a2b8d22f69ad0809d895c11838620ed Mon Sep 17 00:00:00 2001
+From: Lukas Rusak <lorusak@gmail.com>
+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;
++ ar << GetAsTimePoint();
+ }
+ }
+ else
+@@ -541,9 +415,7 @@ void CDateTime::Archive(CArchive& ar)
+ m_state = CDateTime::STATE(state);
+ if (m_state==valid)
+ {
+- KODI::TIME::SystemTime 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<CGUIDialogNumeric>(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<std::chrono::seconds>(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<CGUIDialogNumeric>(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<CGUIDialogNumeric>(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 <cstdint>
+
+@@ -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<CCriticalSection> 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<std::string> m_vecUrls;
+- std::vector<int> m_vecIntervals;
++ std::vector<std::chrono::nanoseconds> 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<CFileItem> 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 <errno.h>
+-#include <mutex>
+-#include <time.h>
+-
+-#include <sys/times.h>
+-
+-#if defined(TARGET_ANDROID) && !defined(__LP64__)
+-#include <time64.h>
+-#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<unsigned long long>(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<std::mutex> 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_ptr<const CSe
+ const std::string& settingId = setting->GetId();
+ 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<const CSetting>& 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<CSettingDate> settingDate =
+ std::static_pointer_cast<CSettingDate>(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<CSettingTime> settingTime =
+ std::static_pointer_cast<CSettingTime>(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<std::string>& 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<std::string>& 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<int> interval;
++ std::vector<std::chrono::nanoseconds> interval;
+ std::vector<std::string> url;
+ } RssSet;
+ typedef std::map<int, RssSet> 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<std::string>& aUrls, const std::vector<int> &times, int spacesBetweenFeeds, bool rtl)
++void CRssReader::Create(IRssObserver* aObserver,
++ const std::vector<std::string>& aUrls,
++ const std::vector<std::chrono::nanoseconds>& times,
++ int spacesBetweenFeeds,
++ bool rtl)
+ {
+ std::unique_lock<CCriticalSection> lock(m_critical);
+
+@@ -73,9 +76,7 @@ void CRssReader::Create(IRssObserver* aObserver, const std::vector<std::string>&
+ 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<std::string>& aUrl, const std::vector<int>& times, int spacesBetweenFeeds, bool rtl);
++ void Create(IRssObserver* aObserver,
++ const std::vector<std::string>& aUrl,
++ const std::vector<std::chrono::nanoseconds>& 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<std::wstring> m_strFeed;
+ std::vector<std::wstring> m_strColors;
+- std::vector<KODI::TIME::SystemTime*> m_vecTimeStamps;
+- std::vector<int> m_vecUpdateTimes;
++ std::vector<std::chrono::nanoseconds> m_vecTimeStamps;
++ std::vector<std::chrono::nanoseconds> m_vecUpdateTimes;
+ int m_spacesBetweenFeeds;
+ CXBMCTinyXML m_xml;
+ std::list<std::string> 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<typename Rep, typename Period>
+ void Sleep(std::chrono::duration<Rep, Period> duration)
+@@ -76,13 +40,5 @@ void Sleep(std::chrono::duration<Rep, Period> 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<std::string> 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 <vasek.gello@gmail.com>
+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<std::chrono::seconds>(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 <array>
++#include <chrono>
+ #include <iostream>
+
+ #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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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 <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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<std::chrono::seconds>(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<CCriticalSection> lock(m_critSection);
+ bUpdateEpg = (iNow >= m_iNextEpgUpdate) && !m_bSuspended;
+@@ -593,7 +593,7 @@ std::shared_ptr<CPVREpg> CPVREpgContainer::CreateChannelEpg(int iEpgId, const st
+ {
+ std::unique_lock<CCriticalSection> 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<CCriticalSection> 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<CCriticalSection> lock(m_critSection);
+ m_iNextEpgUpdate = iNow + advancedSettings->m_iEpgRetryInterruptedUpdateInterval;
+@@ -795,7 +795,7 @@ bool CPVREpgContainer::UpdateEPG(bool bOnlyPending /* = false */)
+ else
+ {
+ std::unique_lock<CCriticalSection> 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<CPVREpgInfoTag>& 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<std::chrono::seconds>(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 <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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<CFileItemList>&
+ ////////////////////////////////////////////////////////////////////////
+ // 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 <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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<PVR::CPVREpgSearchFilter>& 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<CFileItemList>&
+ ////////////////////////////////////////////////////////////////////////
+ // 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 <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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 <vasek.gello@gmail.com>
+Date: Wed, 21 Dec 2022 18:27:24 +0000
+Subject: [PATCH 12/19] Move date includes to separate header
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ 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 <date/tz.h>
+-
+ 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 <cstdlib>
+
+-#define USE_OS_TZDB 0
+-#define HAS_REMOTE_API 0
+-#include <date/date.h>
+-#include <date/iso_week.h>
+-#include <date/tz.h>
+-
+ 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 <date/tz.h>
+-
+ 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 <mutex>
+
+-#include <date/tz.h>
+-
+ 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 <stdlib.h>
++#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 <stdlib.h>
+
+-#include <date/tz.h>
++#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 <array>
+ #include <chrono>
+ #include <iostream>
+
+-#define USE_OS_TZDB 0
+-#define HAS_REMOTE_API 0
+-#include <date/date.h>
+-#include <date/tz.h>
+ #include <gtest/gtest.h>
+
+ 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 <date/date.h>
++#include <date/iso_week.h>
++#include <date/tz.h>
+--
+2.43.0
+
+
+From 46f92320cc18c3975bddb5dbe8e0d5b66a0b64c6 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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<CGUIDialogNumeric>(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 <cstring>
+ #include <memory>
+ #include <string>
+@@ -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<std::string>& 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<std::string>& 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<std::chrono::system_clock,
++ std::chrono::duration<long double, std::nano>>;
++
+ template<typename Rep, typename Period>
+ void Sleep(std::chrono::duration<Rep, Period> 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<std::string> 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 <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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<std::chrono::seconds>(date::days(day)) +
+- std::chrono::duration_cast<std::chrono::seconds>(std::chrono::hours(hour)) +
+- std::chrono::duration_cast<std::chrono::seconds>(std::chrono::minutes(minute)) +
+- std::chrono::duration_cast<std::chrono::seconds>(std::chrono::seconds(second));
++ m_timeSpan = date::floor<std::chrono::seconds>(date::days(day)) +
++ date::floor<std::chrono::seconds>(std::chrono::hours(hour)) +
++ date::floor<std::chrono::seconds>(std::chrono::minutes(minute)) +
++ date::floor<std::chrono::seconds>(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<std::chrono::seconds>(m_timeSpan).count();
++ return date::floor<std::chrono::seconds>(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<long double>(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<tm*>(&time)));
++ Reset();
++ m_time += std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&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<std::chrono::seconds>(zone.get_local_time().time_since_epoch())
+- .count());
++ date::floor<std::chrono::seconds>(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<long double>(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<tm*>(&right)));
++ Reset();
++ m_time += std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&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<long double>(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<long double>(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<long double>(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<tm*>(&right)));
++ return m_time > std::chrono::system_clock::from_time_t(0) +
++ std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&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<tm*>(&right)));
++ return m_time < std::chrono::system_clock::from_time_t(0) +
++ std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&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<tm*>(&right)));
++ return m_time == std::chrono::system_clock::from_time_t(0) +
++ std::chrono::duration<long double>(std::mktime(const_cast<tm*>(&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<std::chrono::seconds>(m_time - right.m_time);
++ left.m_timeSpan = date::floor<std::chrono::seconds>(m_time - right.m_time);
+ return left;
+ }
+
+@@ -496,7 +505,7 @@ int CDateTime::GetYear() const
+ int CDateTime::GetHour() const
+ {
+ auto dp = date::floor<date::days>(m_time);
+- auto time = date::make_time(m_time - dp);
++ auto time = date::make_time(date::floor<std::chrono::seconds>(m_time - dp));
+
+ return time.hours().count();
+ }
+@@ -504,7 +513,7 @@ int CDateTime::GetHour() const
+ int CDateTime::GetMinute() const
+ {
+ auto dp = date::floor<date::days>(m_time);
+- auto time = date::make_time(m_time - dp);
++ auto time = date::make_time(date::floor<std::chrono::seconds>(m_time - dp));
+
+ return time.minutes().count();
+ }
+@@ -512,7 +521,7 @@ int CDateTime::GetMinute() const
+ int CDateTime::GetSecond() const
+ {
+ auto dp = date::floor<date::days>(m_time);
+- auto time = date::make_time(m_time - dp);
++ auto time = date::make_time(date::floor<std::chrono::seconds>(m_time - dp));
+
+ return time.seconds().count();
+ }
+@@ -529,7 +538,7 @@ int CDateTime::GetMinuteOfDay() const
+ {
+ auto dp = date::floor<std::chrono::hours>(m_time);
+ ;
+- auto time = date::make_time(m_time - dp);
++ auto time = date::make_time(date::floor<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(zone.get_local_time().time_since_epoch())
+- .count());
++ date::floor<std::chrono::seconds>(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 <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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<long double>(std::mktime(const_cast<tm*>(&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<std::chrono::seconds>(dur.time_since_epoch()).count();
++
++ m_time += std::chrono::duration<long double>(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<long double>(std::mktime(const_cast<tm*>(&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<std::chrono::seconds>(dur.time_since_epoch()).count();
++
++ m_time += std::chrono::duration<long double>(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<long double>(std::mktime(const_cast<tm*>(&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<long double>(std::mktime(const_cast<tm*>(&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<long double>(std::mktime(const_cast<tm*>(&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<std::chrono::seconds>(m_time.time_since_epoch()).count();
++ auto dp = date::floor<date::days>(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<std::chrono::seconds>(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<std::chrono::seconds>(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?= <per.bjorklund@gmail.com>
+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<CJobManager>());
+
+ // Initialize,timezone
++#if defined(TARGET_POSIX)
+ g_timezone.Init();
++#endif
+
+ // Announcement service
+ m_pAnnouncementManager = std::make_shared<ANNOUNCEMENT::CAnnouncementManager>();
+--
+2.43.0
+
+
+From 257a56972008869e448fd81b15c67bb46c17f338 Mon Sep 17 00:00:00 2001
+From: Vasyl Gello <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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<std::chrono::seconds>(zone.get_local_time().time_since_epoch()).count());
+@@ -619,7 +619,7 @@ void CDateTime::GetAsTm(tm& time) const
+ auto seconds_to_newyear = date::floor<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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<std::chrono::seconds>(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 <cstdlib>
++
++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 <cstring>
++
+ #include <date/date.h>
+ #include <date/iso_week.h>
+ #include <date/tz.h>
++
++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 <vasek.gello@gmail.com>
+Date: Mon, 9 Jan 2023 23:00:17 +0200
+Subject: [PATCH 18/19] Apply formatting fixes
+
+Signed-off-by: Vasyl Gello <vasek.gello@gmail.com>
+---
+ 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<long double>(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<long double>(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<long double>(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<long double>(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 <vasek.gello@gmail.com>
+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 <vasek.gello@gmail.com>
+---
+ 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
+