diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 7fd226050..23d88d747 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -461,12 +461,40 @@ add_library(core STATIC
     hle/service/spl/spl.h
     hle/service/ssl/ssl.cpp
     hle/service/ssl/ssl.h
+    hle/service/time/clock_types.h
+    hle/service/time/ephemeral_network_system_clock_context_writer.h
+    hle/service/time/ephemeral_network_system_clock_core.h
+    hle/service/time/errors.h
     hle/service/time/interface.cpp
     hle/service/time/interface.h
+    hle/service/time/local_system_clock_context_writer.h
+    hle/service/time/network_system_clock_context_writer.h
+    hle/service/time/standard_local_system_clock_core.h
+    hle/service/time/standard_network_system_clock_core.h
+    hle/service/time/standard_steady_clock_core.cpp
+    hle/service/time/standard_steady_clock_core.h
+    hle/service/time/standard_user_system_clock_core.cpp
+    hle/service/time/standard_user_system_clock_core.h
+    hle/service/time/steady_clock_core.h
+    hle/service/time/system_clock_context_update_callback.cpp
+    hle/service/time/system_clock_context_update_callback.h
+    hle/service/time/system_clock_core.cpp
+    hle/service/time/system_clock_core.h
+    hle/service/time/tick_based_steady_clock_core.cpp
+    hle/service/time/tick_based_steady_clock_core.h
     hle/service/time/time.cpp
     hle/service/time/time.h
+    hle/service/time/time_manager.cpp
+    hle/service/time/time_manager.h
     hle/service/time/time_sharedmemory.cpp
     hle/service/time/time_sharedmemory.h
+    hle/service/time/time_zone_content_manager.cpp
+    hle/service/time/time_zone_content_manager.h
+    hle/service/time/time_zone_manager.cpp
+    hle/service/time/time_zone_manager.h
+    hle/service/time/time_zone_service.cpp
+    hle/service/time/time_zone_service.h
+    hle/service/time/time_zone_types.h
     hle/service/usb/usb.cpp
     hle/service/usb/usb.h
     hle/service/vi/display/vi_display.cpp
diff --git a/src/core/hle/service/time/clock_types.h b/src/core/hle/service/time/clock_types.h
new file mode 100644
index 000000000..f2ef3ec53
--- /dev/null
+++ b/src/core/hle/service/time/clock_types.h
@@ -0,0 +1,91 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/uuid.h"
+#include "core/hle/service/time/errors.h"
+#include "core/hle/service/time/time_zone_types.h"
+
+namespace Service::Time::Clock {
+
+/// https://switchbrew.org/wiki/Glue_services#SteadyClockTimePoint
+struct SteadyClockTimePoint {
+    s64 time_point;
+    Common::UUID clock_source_id;
+
+    static SteadyClockTimePoint GetRandom() {
+        return {0, Common::UUID::Generate()};
+    }
+};
+static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
+static_assert(std::is_trivially_copyable_v<SteadyClockTimePoint>,
+              "SteadyClockTimePoint must be trivially copyable");
+
+struct SteadyClockContext {
+    u64 internal_offset;
+    Common::UUID steady_time_point;
+};
+static_assert(sizeof(SteadyClockContext) == 0x18, "SteadyClockContext is incorrect size");
+static_assert(std::is_trivially_copyable_v<SteadyClockContext>,
+              "SteadyClockContext must be trivially copyable");
+
+struct SystemClockContext {
+    s64 offset;
+    SteadyClockTimePoint steady_time_point;
+};
+static_assert(sizeof(SystemClockContext) == 0x20, "SystemClockContext is incorrect size");
+static_assert(std::is_trivially_copyable_v<SystemClockContext>,
+              "SystemClockContext must be trivially copyable");
+
+/// https://switchbrew.org/wiki/Glue_services#TimeSpanType
+struct TimeSpanType {
+    s64 nanoseconds{};
+    static constexpr s64 ns_per_second{1000000000ULL};
+
+    s64 ToSeconds() const {
+        return nanoseconds / ns_per_second;
+    }
+
+    static TimeSpanType FromSeconds(s64 seconds) {
+        return {seconds * ns_per_second};
+    }
+
+    static TimeSpanType FromTicks(u64 ticks, u64 frequency) {
+        return FromSeconds(static_cast<s64>(ticks) / static_cast<s64>(frequency));
+    }
+};
+static_assert(sizeof(TimeSpanType) == 8, "TimeSpanType is incorrect size");
+
+struct ClockSnapshot {
+    SystemClockContext user_context{};
+    SystemClockContext network_context{};
+    s64 user_time{};
+    s64 network_time{};
+    TimeZone::CalendarTime user_calendar_time{};
+    TimeZone::CalendarTime network_calendar_time{};
+    TimeZone::CalendarAdditionalInfo user_calendar_additional_time{};
+    TimeZone::CalendarAdditionalInfo network_calendar_additional_time{};
+    SteadyClockTimePoint steady_clock_time_point{};
+    TimeZone::LocationName location_name{};
+    u8 is_automatic_correction_enabled{};
+    u8 type{};
+    INSERT_PADDING_BYTES(0x2);
+
+    static ResultCode GetCurrentTime(s64& current_time,
+                                     const SteadyClockTimePoint& steady_clock_time_point,
+                                     const SystemClockContext& context) {
+        if (steady_clock_time_point.clock_source_id != context.steady_time_point.clock_source_id) {
+            current_time = 0;
+            return ERROR_TIME_MISMATCH;
+        }
+        current_time = steady_clock_time_point.time_point + context.offset;
+        return RESULT_SUCCESS;
+    }
+};
+static_assert(sizeof(ClockSnapshot) == 0xD0, "ClockSnapshot is incorrect size");
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
new file mode 100644
index 000000000..42893e3f6
--- /dev/null
+++ b/src/core/hle/service/time/ephemeral_network_system_clock_context_writer.h
@@ -0,0 +1,16 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+
+namespace Service::Time::Clock {
+
+class EphemeralNetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
+public:
+    EphemeralNetworkSystemClockContextWriter() : SystemClockContextUpdateCallback{} {}
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/ephemeral_network_system_clock_core.h b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
new file mode 100644
index 000000000..4c6cdef86
--- /dev/null
+++ b/src/core/hle/service/time/ephemeral_network_system_clock_core.h
@@ -0,0 +1,17 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+class EphemeralNetworkSystemClockCore final : public SystemClockCore {
+public:
+    explicit EphemeralNetworkSystemClockCore(SteadyClockCore& steady_clock_core)
+        : SystemClockCore{steady_clock_core} {}
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/errors.h b/src/core/hle/service/time/errors.h
new file mode 100644
index 000000000..8501a3e8c
--- /dev/null
+++ b/src/core/hle/service/time/errors.h
@@ -0,0 +1,22 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::Time {
+
+constexpr ResultCode ERROR_PERMISSION_DENIED{ErrorModule::Time, 1};
+constexpr ResultCode ERROR_TIME_MISMATCH{ErrorModule::Time, 102};
+constexpr ResultCode ERROR_UNINITIALIZED_CLOCK{ErrorModule::Time, 103};
+constexpr ResultCode ERROR_TIME_NOT_FOUND{ErrorModule::Time, 200};
+constexpr ResultCode ERROR_OVERFLOW{ErrorModule::Time, 201};
+constexpr ResultCode ERROR_LOCATION_NAME_TOO_LONG{ErrorModule::Time, 801};
+constexpr ResultCode ERROR_OUT_OF_RANGE{ErrorModule::Time, 902};
+constexpr ResultCode ERROR_TIME_ZONE_CONVERSION_FAILED{ErrorModule::Time, 903};
+constexpr ResultCode ERROR_TIME_ZONE_NOT_FOUND{ErrorModule::Time, 989};
+constexpr ResultCode ERROR_NOT_IMPLEMENTED{ErrorModule::Time, 990};
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index bc74f1e1d..b1307a434 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2019 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -6,31 +6,30 @@
 
 namespace Service::Time {
 
-Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
-           Core::System& system, const char* name)
-    : Module::Interface(std::move(time), std::move(shared_memory), system, name) {
+Time::Time(std::shared_ptr<Module> module, Core::System& system, const char* name)
+    : Module::Interface(std::move(module), system, name) {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
         {1, &Time::GetStandardNetworkSystemClock, "GetStandardNetworkSystemClock"},
         {2, &Time::GetStandardSteadyClock, "GetStandardSteadyClock"},
         {3, &Time::GetTimeZoneService, "GetTimeZoneService"},
-        {4, &Time::GetStandardLocalSystemClock, "GetStandardLocalSystemClock"},
+        {4, nullptr, "GetStandardLocalSystemClock"},
         {5, nullptr, "GetEphemeralNetworkSystemClock"},
         {20, &Time::GetSharedMemoryNativeHandle, "GetSharedMemoryNativeHandle"},
         {30, nullptr, "GetStandardNetworkClockOperationEventReadableHandle"},
         {31, nullptr, "GetEphemeralNetworkClockOperationEventReadableHandle"},
         {50, nullptr, "SetStandardSteadyClockInternalOffset"},
         {51, nullptr, "GetStandardSteadyClockRtcValue"},
-        {100, &Time::IsStandardUserSystemClockAutomaticCorrectionEnabled, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
-        {101, &Time::SetStandardUserSystemClockAutomaticCorrectionEnabled, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
+        {100, nullptr, "IsStandardUserSystemClockAutomaticCorrectionEnabled"},
+        {101, nullptr, "SetStandardUserSystemClockAutomaticCorrectionEnabled"},
         {102, nullptr, "GetStandardUserSystemClockInitialYear"},
         {200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
         {201, nullptr, "GetStandardUserSystemClockAutomaticCorrectionUpdatedTime"},
-        {300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
+        {300, &Time::CalculateMonotonicSystemClockBaseTimePoint, "CalculateMonotonicSystemClockBaseTimePoint"},
         {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
         {401, nullptr, "GetClockSnapshotFromSystemClockContext"},
-        {500, &Time::CalculateStandardUserSystemClockDifferenceByUser, "CalculateStandardUserSystemClockDifferenceByUser"},
+        {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
         {501, nullptr, "CalculateSpanBetween"},
     };
     // clang-format on
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h
index 5c63a07f4..4f49e1f07 100644
--- a/src/core/hle/service/time/interface.h
+++ b/src/core/hle/service/time/interface.h
@@ -1,4 +1,4 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2019 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
@@ -6,14 +6,15 @@
 
 #include "core/hle/service/time/time.h"
 
-namespace Service::Time {
+namespace Core {
+class System;
+}
 
-class SharedMemory;
+namespace Service::Time {
 
 class Time final : public Module::Interface {
 public:
-    explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
-                  Core::System& system, const char* name);
+    explicit Time(std::shared_ptr<Module> time, Core::System& system, const char* name);
     ~Time() override;
 };
 
diff --git a/src/core/hle/service/time/local_system_clock_context_writer.h b/src/core/hle/service/time/local_system_clock_context_writer.h
new file mode 100644
index 000000000..7050844c6
--- /dev/null
+++ b/src/core/hle/service/time/local_system_clock_context_writer.h
@@ -0,0 +1,28 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/errors.h"
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+#include "core/hle/service/time/time_sharedmemory.h"
+
+namespace Service::Time::Clock {
+
+class LocalSystemClockContextWriter final : public SystemClockContextUpdateCallback {
+public:
+    explicit LocalSystemClockContextWriter(SharedMemory& shared_memory)
+        : SystemClockContextUpdateCallback{}, shared_memory{shared_memory} {}
+
+protected:
+    ResultCode Update() override {
+        shared_memory.UpdateLocalSystemClockContext(context);
+        return RESULT_SUCCESS;
+    }
+
+private:
+    SharedMemory& shared_memory;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/network_system_clock_context_writer.h b/src/core/hle/service/time/network_system_clock_context_writer.h
new file mode 100644
index 000000000..94d8788ff
--- /dev/null
+++ b/src/core/hle/service/time/network_system_clock_context_writer.h
@@ -0,0 +1,28 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/errors.h"
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+#include "core/hle/service/time/time_sharedmemory.h"
+
+namespace Service::Time::Clock {
+
+class NetworkSystemClockContextWriter final : public SystemClockContextUpdateCallback {
+public:
+    explicit NetworkSystemClockContextWriter(SharedMemory& shared_memory)
+        : SystemClockContextUpdateCallback{}, shared_memory{shared_memory} {}
+
+protected:
+    ResultCode Update() override {
+        shared_memory.UpdateNetworkSystemClockContext(context);
+        return RESULT_SUCCESS;
+    }
+
+private:
+    SharedMemory& shared_memory;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_local_system_clock_core.h b/src/core/hle/service/time/standard_local_system_clock_core.h
new file mode 100644
index 000000000..8c1882eb1
--- /dev/null
+++ b/src/core/hle/service/time/standard_local_system_clock_core.h
@@ -0,0 +1,17 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+class StandardLocalSystemClockCore final : public SystemClockCore {
+public:
+    explicit StandardLocalSystemClockCore(SteadyClockCore& steady_clock_core)
+        : SystemClockCore{steady_clock_core} {}
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h
new file mode 100644
index 000000000..467285160
--- /dev/null
+++ b/src/core/hle/service/time/standard_network_system_clock_core.h
@@ -0,0 +1,26 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/steady_clock_core.h"
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+class StandardNetworkSystemClockCore final : public SystemClockCore {
+public:
+    explicit StandardNetworkSystemClockCore(SteadyClockCore& steady_clock_core)
+        : SystemClockCore{steady_clock_core} {}
+
+    void SetStandardNetworkClockSufficientAccuracy(TimeSpanType value) {
+        standard_network_clock_sufficient_accuracy = value;
+    }
+
+private:
+    TimeSpanType standard_network_clock_sufficient_accuracy{};
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
new file mode 100644
index 000000000..ca1a783fc
--- /dev/null
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -0,0 +1,26 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/service/time/standard_steady_clock_core.h"
+
+namespace Service::Time::Clock {
+
+TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
+    const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
+        Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
+        Core::Timing::CNTFREQ)};
+    TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
+
+    if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
+        raw_time_point.nanoseconds = cached_raw_time_point.nanoseconds;
+    }
+
+    cached_raw_time_point = raw_time_point;
+    return raw_time_point;
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_steady_clock_core.h b/src/core/hle/service/time/standard_steady_clock_core.h
new file mode 100644
index 000000000..f56f3fd95
--- /dev/null
+++ b/src/core/hle/service/time/standard_steady_clock_core.h
@@ -0,0 +1,42 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/steady_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class StandardSteadyClockCore final : public SteadyClockCore {
+public:
+    SteadyClockTimePoint GetTimePoint(Core::System& system) override {
+        return {GetCurrentRawTimePoint(system).ToSeconds(), GetClockSourceId()};
+    }
+
+    TimeSpanType GetInternalOffset() const override {
+        return internal_offset;
+    }
+
+    void SetInternalOffset(TimeSpanType value) override {
+        internal_offset = value;
+    }
+
+    TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
+
+    void SetSetupValue(TimeSpanType value) {
+        setup_value = value;
+    }
+
+private:
+    TimeSpanType setup_value{};
+    TimeSpanType internal_offset{};
+    TimeSpanType cached_raw_time_point{};
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.cpp b/src/core/hle/service/time/standard_user_system_clock_core.cpp
new file mode 100644
index 000000000..8af17091c
--- /dev/null
+++ b/src/core/hle/service/time/standard_user_system_clock_core.cpp
@@ -0,0 +1,77 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/core.h"
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/time/standard_local_system_clock_core.h"
+#include "core/hle/service/time/standard_network_system_clock_core.h"
+#include "core/hle/service/time/standard_user_system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+StandardUserSystemClockCore::StandardUserSystemClockCore(
+    StandardLocalSystemClockCore& local_system_clock_core,
+    StandardNetworkSystemClockCore& network_system_clock_core, Core::System& system)
+    : SystemClockCore(local_system_clock_core.GetSteadyClockCore()),
+      local_system_clock_core{local_system_clock_core},
+      network_system_clock_core{network_system_clock_core}, auto_correction_enabled{},
+      auto_correction_time{SteadyClockTimePoint::GetRandom()},
+      auto_correction_event{Kernel::WritableEvent::CreateEventPair(
+          system.Kernel(), "StandardUserSystemClockCore:AutoCorrectionEvent")} {}
+
+ResultCode StandardUserSystemClockCore::SetAutomaticCorrectionEnabled(Core::System& system,
+                                                                      bool value) {
+    if (const ResultCode result{ApplyAutomaticCorrection(system, value)};
+        result != RESULT_SUCCESS) {
+        return result;
+    }
+
+    auto_correction_enabled = value;
+
+    return RESULT_SUCCESS;
+}
+
+ResultCode StandardUserSystemClockCore::GetClockContext(Core::System& system,
+                                                        SystemClockContext& context) const {
+    if (const ResultCode result{ApplyAutomaticCorrection(system, false)};
+        result != RESULT_SUCCESS) {
+        return result;
+    }
+
+    return local_system_clock_core.GetClockContext(system, context);
+}
+
+ResultCode StandardUserSystemClockCore::Flush(const SystemClockContext& context) {
+    UNREACHABLE();
+    return ERROR_NOT_IMPLEMENTED;
+}
+
+ResultCode StandardUserSystemClockCore::SetClockContext(const SystemClockContext& context) {
+    UNREACHABLE();
+    return ERROR_NOT_IMPLEMENTED;
+}
+
+ResultCode StandardUserSystemClockCore::ApplyAutomaticCorrection(Core::System& system,
+                                                                 bool value) const {
+    if (auto_correction_enabled == value) {
+        return RESULT_SUCCESS;
+    }
+
+    if (!network_system_clock_core.IsClockSetup(system)) {
+        return ERROR_UNINITIALIZED_CLOCK;
+    }
+
+    SystemClockContext context{};
+    if (const ResultCode result{network_system_clock_core.GetClockContext(system, context)};
+        result != RESULT_SUCCESS) {
+        return result;
+    }
+
+    local_system_clock_core.SetClockContext(context);
+
+    return RESULT_SUCCESS;
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/standard_user_system_clock_core.h b/src/core/hle/service/time/standard_user_system_clock_core.h
new file mode 100644
index 000000000..ef3d468b7
--- /dev/null
+++ b/src/core/hle/service/time/standard_user_system_clock_core.h
@@ -0,0 +1,57 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class StandardLocalSystemClockCore;
+class StandardNetworkSystemClockCore;
+
+class StandardUserSystemClockCore final : public SystemClockCore {
+public:
+    StandardUserSystemClockCore(StandardLocalSystemClockCore& local_system_clock_core,
+                                StandardNetworkSystemClockCore& network_system_clock_core,
+                                Core::System& system);
+
+    ResultCode SetAutomaticCorrectionEnabled(Core::System& system, bool value);
+
+    ResultCode GetClockContext(Core::System& system, SystemClockContext& context) const override;
+
+    bool IsAutomaticCorrectionEnabled() const {
+        return auto_correction_enabled;
+    }
+
+    void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steady_clock_time_point) {
+        auto_correction_time = steady_clock_time_point;
+    }
+
+protected:
+    ResultCode Flush(const SystemClockContext& context) override;
+
+    ResultCode SetClockContext(const SystemClockContext&) override;
+
+    ResultCode ApplyAutomaticCorrection(Core::System& system, bool value) const;
+
+    const SteadyClockTimePoint& GetAutomaticCorrectionUpdatedTime() const {
+        return auto_correction_time;
+    }
+
+private:
+    StandardLocalSystemClockCore& local_system_clock_core;
+    StandardNetworkSystemClockCore& network_system_clock_core;
+    bool auto_correction_enabled{};
+    SteadyClockTimePoint auto_correction_time;
+    Kernel::EventPair auto_correction_event;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h
new file mode 100644
index 000000000..84af3d105
--- /dev/null
+++ b/src/core/hle/service/time/steady_clock_core.h
@@ -0,0 +1,55 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/uuid.h"
+#include "core/hle/service/time/clock_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class SteadyClockCore {
+public:
+    SteadyClockCore() = default;
+
+    const Common::UUID& GetClockSourceId() const {
+        return clock_source_id;
+    }
+
+    void SetClockSourceId(const Common::UUID& value) {
+        clock_source_id = value;
+    }
+
+    virtual TimeSpanType GetInternalOffset() const = 0;
+
+    virtual void SetInternalOffset(TimeSpanType internal_offset) = 0;
+
+    virtual SteadyClockTimePoint GetTimePoint(Core::System& system) = 0;
+
+    virtual TimeSpanType GetCurrentRawTimePoint(Core::System& system) = 0;
+
+    SteadyClockTimePoint GetCurrentTimePoint(Core::System& system) {
+        SteadyClockTimePoint result{GetTimePoint(system)};
+        result.time_point += GetInternalOffset().ToSeconds();
+        return result;
+    }
+
+    bool IsInitialized() const {
+        return is_initialized;
+    }
+
+    void MarkAsInitialized() {
+        is_initialized = true;
+    }
+
+private:
+    Common::UUID clock_source_id{Common::UUID::Generate()};
+    bool is_initialized{};
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.cpp b/src/core/hle/service/time/system_clock_context_update_callback.cpp
new file mode 100644
index 000000000..5cdb80703
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_context_update_callback.cpp
@@ -0,0 +1,55 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/time/errors.h"
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+
+namespace Service::Time::Clock {
+
+SystemClockContextUpdateCallback::SystemClockContextUpdateCallback() = default;
+SystemClockContextUpdateCallback::~SystemClockContextUpdateCallback() = default;
+
+bool SystemClockContextUpdateCallback::NeedUpdate(const SystemClockContext& value) const {
+    if (has_context) {
+        return context.offset != value.offset ||
+               context.steady_time_point.clock_source_id != value.steady_time_point.clock_source_id;
+    }
+
+    return true;
+}
+
+void SystemClockContextUpdateCallback::RegisterOperationEvent(
+    std::shared_ptr<Kernel::WritableEvent>&& writable_event) {
+    operation_event_list.emplace_back(std::move(writable_event));
+}
+
+void SystemClockContextUpdateCallback::BroadcastOperationEvent() {
+    for (const auto& writable_event : operation_event_list) {
+        writable_event->Signal();
+    }
+}
+
+ResultCode SystemClockContextUpdateCallback::Update(const SystemClockContext& value) {
+    ResultCode result{RESULT_SUCCESS};
+
+    if (NeedUpdate(value)) {
+        context = value;
+        has_context = true;
+
+        result = Update();
+
+        if (result == RESULT_SUCCESS) {
+            BroadcastOperationEvent();
+        }
+    }
+
+    return result;
+}
+
+ResultCode SystemClockContextUpdateCallback::Update() {
+    return RESULT_SUCCESS;
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
new file mode 100644
index 000000000..6260de6c3
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_context_update_callback.h
@@ -0,0 +1,43 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "core/hle/service/time/clock_types.h"
+
+namespace Kernel {
+class WritableEvent;
+}
+
+namespace Service::Time::Clock {
+
+// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
+// This code was released under public domain.
+
+class SystemClockContextUpdateCallback {
+public:
+    SystemClockContextUpdateCallback();
+    ~SystemClockContextUpdateCallback();
+
+    bool NeedUpdate(const SystemClockContext& value) const;
+
+    void RegisterOperationEvent(std::shared_ptr<Kernel::WritableEvent>&& writable_event);
+
+    void BroadcastOperationEvent();
+
+    ResultCode Update(const SystemClockContext& value);
+
+protected:
+    virtual ResultCode Update();
+
+    SystemClockContext context{};
+
+private:
+    bool has_context{};
+    std::vector<std::shared_ptr<Kernel::WritableEvent>> operation_event_list;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp
new file mode 100644
index 000000000..1a3ab8cfa
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_core.cpp
@@ -0,0 +1,72 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/service/time/steady_clock_core.h"
+#include "core/hle/service/time/system_clock_context_update_callback.h"
+#include "core/hle/service/time/system_clock_core.h"
+
+namespace Service::Time::Clock {
+
+SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core)
+    : steady_clock_core{steady_clock_core}, is_initialized{} {
+    context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
+}
+
+SystemClockCore ::~SystemClockCore() = default;
+
+ResultCode SystemClockCore::GetCurrentTime(Core::System& system, s64& posix_time) const {
+    posix_time = 0;
+
+    const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
+
+    SystemClockContext clock_context{};
+    if (const ResultCode result{GetClockContext(system, clock_context)}; result != RESULT_SUCCESS) {
+        return result;
+    }
+
+    if (current_time_point.clock_source_id != clock_context.steady_time_point.clock_source_id) {
+        return ERROR_TIME_MISMATCH;
+    }
+
+    posix_time = clock_context.offset + current_time_point.time_point;
+
+    return RESULT_SUCCESS;
+}
+
+ResultCode SystemClockCore::SetCurrentTime(Core::System& system, s64 posix_time) {
+    const SteadyClockTimePoint current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
+    const SystemClockContext clock_context{posix_time - current_time_point.time_point,
+                                           current_time_point};
+
+    if (const ResultCode result{SetClockContext(clock_context)}; result != RESULT_SUCCESS) {
+        return result;
+    }
+    return Flush(clock_context);
+}
+
+ResultCode SystemClockCore::Flush(const SystemClockContext& context) {
+    if (!system_clock_context_update_callback) {
+        return RESULT_SUCCESS;
+    }
+    return system_clock_context_update_callback->Update(context);
+}
+
+ResultCode SystemClockCore::SetSystemClockContext(const SystemClockContext& context) {
+    if (const ResultCode result{SetClockContext(context)}; result != RESULT_SUCCESS) {
+        return result;
+    }
+    return Flush(context);
+}
+
+bool SystemClockCore::IsClockSetup(Core::System& system) const {
+    SystemClockContext value{};
+    if (GetClockContext(system, value) == RESULT_SUCCESS) {
+        const SteadyClockTimePoint steady_clock_time_point{
+            steady_clock_core.GetCurrentTimePoint(system)};
+        return steady_clock_time_point.clock_source_id == value.steady_time_point.clock_source_id;
+    }
+    return {};
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h
new file mode 100644
index 000000000..54407a6c5
--- /dev/null
+++ b/src/core/hle/service/time/system_clock_core.h
@@ -0,0 +1,71 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/hle/service/time/clock_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class SteadyClockCore;
+class SystemClockContextUpdateCallback;
+
+// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
+// This code was released under public domain.
+
+class SystemClockCore {
+public:
+    explicit SystemClockCore(SteadyClockCore& steady_clock_core);
+    ~SystemClockCore();
+
+    SteadyClockCore& GetSteadyClockCore() const {
+        return steady_clock_core;
+    }
+
+    ResultCode GetCurrentTime(Core::System& system, s64& posix_time) const;
+
+    ResultCode SetCurrentTime(Core::System& system, s64 posix_time);
+
+    virtual ResultCode GetClockContext([[maybe_unused]] Core::System& system,
+                                       SystemClockContext& value) const {
+        value = context;
+        return RESULT_SUCCESS;
+    }
+
+    virtual ResultCode SetClockContext(const SystemClockContext& value) {
+        context = value;
+        return RESULT_SUCCESS;
+    }
+
+    virtual ResultCode Flush(const SystemClockContext& context);
+
+    void SetUpdateCallbackInstance(std::shared_ptr<SystemClockContextUpdateCallback> callback) {
+        system_clock_context_update_callback = std::move(callback);
+    }
+
+    ResultCode SetSystemClockContext(const SystemClockContext& context);
+
+    bool IsInitialized() const {
+        return is_initialized;
+    }
+
+    void MarkAsInitialized() {
+        is_initialized = true;
+    }
+
+    bool IsClockSetup(Core::System& system) const;
+
+private:
+    SteadyClockCore& steady_clock_core;
+    SystemClockContext context{};
+    bool is_initialized{};
+    std::shared_ptr<SystemClockContextUpdateCallback> system_clock_context_update_callback;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
new file mode 100644
index 000000000..c77b98189
--- /dev/null
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -0,0 +1,24 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/service/time/tick_based_steady_clock_core.h"
+
+namespace Service::Time::Clock {
+
+SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
+    const TimeSpanType ticks_time_span{TimeSpanType::FromTicks(
+        Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
+        Core::Timing::CNTFREQ)};
+
+    return {ticks_time_span.ToSeconds(), GetClockSourceId()};
+}
+
+TimeSpanType TickBasedSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
+    return TimeSpanType::FromSeconds(GetTimePoint(system).time_point);
+}
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.h b/src/core/hle/service/time/tick_based_steady_clock_core.h
new file mode 100644
index 000000000..1a5a53fd7
--- /dev/null
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.h
@@ -0,0 +1,29 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/steady_clock_core.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time::Clock {
+
+class TickBasedSteadyClockCore final : public SteadyClockCore {
+public:
+    TimeSpanType GetInternalOffset() const override {
+        return {};
+    }
+
+    void SetInternalOffset(TimeSpanType internal_offset) override {}
+
+    SteadyClockTimePoint GetTimePoint(Core::System& system) override;
+
+    TimeSpanType GetCurrentRawTimePoint(Core::System& system) override;
+};
+
+} // namespace Service::Time::Clock
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 6ee77c5f9..970aed0bb 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -1,9 +1,7 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2019 yuzu emulator team
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
-#include <chrono>
-#include <ctime>
 #include "common/logging/log.h"
 #include "core/core.h"
 #include "core/core_timing.h"
@@ -11,429 +9,282 @@
 #include "core/hle/ipc_helpers.h"
 #include "core/hle/kernel/client_port.h"
 #include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/scheduler.h"
 #include "core/hle/service/time/interface.h"
 #include "core/hle/service/time/time.h"
 #include "core/hle/service/time/time_sharedmemory.h"
-#include "core/settings.h"
+#include "core/hle/service/time/time_zone_service.h"
 
 namespace Service::Time {
 
-static std::chrono::seconds GetSecondsSinceEpoch() {
-    return std::chrono::duration_cast<std::chrono::seconds>(
-               std::chrono::system_clock::now().time_since_epoch()) +
-           Settings::values.custom_rtc_differential;
-}
-
-static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
-                            CalendarAdditionalInfo& additional_info,
-                            [[maybe_unused]] const TimeZoneRule& /*rule*/) {
-    const std::time_t time(posix_time);
-    const std::tm* tm = std::localtime(&time);
-    if (tm == nullptr) {
-        calendar_time = {};
-        additional_info = {};
-        return;
-    }
-    calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
-    calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
-    calendar_time.day = static_cast<u8>(tm->tm_mday);
-    calendar_time.hour = static_cast<u8>(tm->tm_hour);
-    calendar_time.minute = static_cast<u8>(tm->tm_min);
-    calendar_time.second = static_cast<u8>(tm->tm_sec);
-
-    additional_info.day_of_week = tm->tm_wday;
-    additional_info.day_of_year = tm->tm_yday;
-    std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
-    additional_info.utc_offset = 0;
-}
-
-static u64 CalendarToPosix(const CalendarTime& calendar_time,
-                           [[maybe_unused]] const TimeZoneRule& /*rule*/) {
-    std::tm time{};
-    time.tm_year = calendar_time.year - 1900;
-    time.tm_mon = calendar_time.month - 1;
-    time.tm_mday = calendar_time.day;
-
-    time.tm_hour = calendar_time.hour;
-    time.tm_min = calendar_time.minute;
-    time.tm_sec = calendar_time.second;
-
-    std::time_t epoch_time = std::mktime(&time);
-    return static_cast<u64>(epoch_time);
-}
-
-enum class ClockContextType {
-    StandardSteady,
-    StandardUserSystem,
-    StandardNetworkSystem,
-    StandardLocalSystem,
-};
-
 class ISystemClock final : public ServiceFramework<ISystemClock> {
 public:
-    ISystemClock(std::shared_ptr<Service::Time::SharedMemory> shared_memory,
-                 ClockContextType clock_type)
-        : ServiceFramework("ISystemClock"), shared_memory(shared_memory), clock_type(clock_type) {
+    ISystemClock(Clock::SystemClockCore& clock_core)
+        : ServiceFramework("ISystemClock"), clock_core{clock_core} {
         // clang-format off
         static const FunctionInfo functions[] = {
             {0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
             {1, nullptr, "SetCurrentTime"},
-            {2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
+            {2,  &ISystemClock::GetSystemClockContext, "GetSystemClockContext"},
             {3, nullptr, "SetSystemClockContext"},
             {4, nullptr, "GetOperationEventReadableHandle"},
         };
         // clang-format on
 
         RegisterHandlers(functions);
-        UpdateSharedMemoryContext(system_clock_context);
     }
 
 private:
     void GetCurrentTime(Kernel::HLERequestContext& ctx) {
-        const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
         LOG_DEBUG(Service_Time, "called");
 
+        if (!clock_core.IsInitialized()) {
+            IPC::ResponseBuilder rb{ctx, 2};
+            rb.Push(ERROR_UNINITIALIZED_CLOCK);
+            return;
+        }
+
+        s64 posix_time{};
+        if (const ResultCode result{
+                clock_core.GetCurrentTime(Core::System::GetInstance(), posix_time)};
+            result != RESULT_SUCCESS) {
+            IPC::ResponseBuilder rb{ctx, 2};
+            rb.Push(result);
+            return;
+        }
+
         IPC::ResponseBuilder rb{ctx, 4};
         rb.Push(RESULT_SUCCESS);
-        rb.Push<u64>(time_since_epoch);
+        rb.Push<s64>(posix_time);
     }
 
     void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
-        LOG_WARNING(Service_Time, "(STUBBED) called");
+        LOG_DEBUG(Service_Time, "called");
 
-        // TODO(ogniK): This should be updated periodically however since we have it stubbed we'll
-        // only update when we get a new context
-        UpdateSharedMemoryContext(system_clock_context);
+        if (!clock_core.IsInitialized()) {
+            IPC::ResponseBuilder rb{ctx, 2};
+            rb.Push(ERROR_UNINITIALIZED_CLOCK);
+            return;
+        }
 
-        IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2};
+        Clock::SystemClockContext system_clock_context{};
+        if (const ResultCode result{
+                clock_core.GetClockContext(Core::System::GetInstance(), system_clock_context)};
+            result != RESULT_SUCCESS) {
+            IPC::ResponseBuilder rb{ctx, 2};
+            rb.Push(result);
+            return;
+        }
+
+        IPC::ResponseBuilder rb{ctx, sizeof(Clock::SystemClockContext) / 4 + 2};
         rb.Push(RESULT_SUCCESS);
         rb.PushRaw(system_clock_context);
     }
 
-    void UpdateSharedMemoryContext(const SystemClockContext& clock_context) {
-        switch (clock_type) {
-        case ClockContextType::StandardLocalSystem:
-            shared_memory->SetStandardLocalSystemClockContext(clock_context);
-            break;
-        case ClockContextType::StandardNetworkSystem:
-            shared_memory->SetStandardNetworkSystemClockContext(clock_context);
-            break;
-        }
-    }
-
-    SystemClockContext system_clock_context{};
-    std::shared_ptr<Service::Time::SharedMemory> shared_memory;
-    ClockContextType clock_type;
+    Clock::SystemClockCore& clock_core;
 };
 
 class ISteadyClock final : public ServiceFramework<ISteadyClock> {
 public:
-    ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system)
-        : ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) {
+    ISteadyClock(Clock::SteadyClockCore& clock_core)
+        : ServiceFramework("ISteadyClock"), clock_core{clock_core} {
         static const FunctionInfo functions[] = {
             {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
         };
         RegisterHandlers(functions);
-
-        shared_memory->SetStandardSteadyClockTimepoint(GetCurrentTimePoint());
     }
 
 private:
     void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
         LOG_DEBUG(Service_Time, "called");
 
-        const auto time_point = GetCurrentTimePoint();
-        // TODO(ogniK): This should be updated periodically
-        shared_memory->SetStandardSteadyClockTimepoint(time_point);
+        if (!clock_core.IsInitialized()) {
+            IPC::ResponseBuilder rb{ctx, 2};
+            rb.Push(ERROR_UNINITIALIZED_CLOCK);
+            return;
+        }
 
-        IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
+        const Clock::SteadyClockTimePoint time_point{
+            clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
+        IPC::ResponseBuilder rb{ctx, (sizeof(Clock::SteadyClockTimePoint) / 4) + 2};
         rb.Push(RESULT_SUCCESS);
         rb.PushRaw(time_point);
     }
 
-    SteadyClockTimePoint GetCurrentTimePoint() const {
-        const auto& core_timing = system.CoreTiming();
-        const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
-        return {static_cast<u64_le>(ms.count() / 1000), {}};
-    }
-
-    std::shared_ptr<SharedMemory> shared_memory;
-    Core::System& system;
+    Clock::SteadyClockCore& clock_core;
 };
 
-class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
-public:
-    ITimeZoneService() : ServiceFramework("ITimeZoneService") {
-        // clang-format off
-        static const FunctionInfo functions[] = {
-            {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
-            {1, nullptr, "SetDeviceLocationName"},
-            {2, &ITimeZoneService::GetTotalLocationNameCount, "GetTotalLocationNameCount"},
-            {3, nullptr, "LoadLocationNameList"},
-            {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
-            {5, nullptr, "GetTimeZoneRuleVersion"},
-            {6, nullptr, "GetDeviceLocationNameAndUpdatedTime"},
-            {7, nullptr, "SetDeviceLocationNameWithTimeZoneRule"},
-            {8, nullptr, "ParseTimeZoneBinary"},
-            {20, nullptr, "GetDeviceLocationNameOperationEventReadableHandle"},
-            {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
-            {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
-            {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
-            {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
-        };
-        // clang-format on
+ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
+    Kernel::Thread* thread, Clock::SystemClockContext user_context,
+    Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
 
-        RegisterHandlers(functions);
+    auto& time_manager{module->GetTimeManager()};
+
+    clock_snapshot.is_automatic_correction_enabled =
+        time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
+    clock_snapshot.user_context = user_context;
+    clock_snapshot.network_context = network_context;
+
+    if (const ResultCode result{
+            time_manager.GetTimeZoneContentManager().GetTimeZoneManager().GetDeviceLocationName(
+                clock_snapshot.location_name)};
+        result != RESULT_SUCCESS) {
+        return result;
     }
 
-private:
-    LocationName location_name{"UTC"};
-    TimeZoneRule my_time_zone_rule{};
-
-    void GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
-        LOG_DEBUG(Service_Time, "called");
-
-        IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
-        rb.Push(RESULT_SUCCESS);
-        rb.PushRaw(location_name);
+    const auto current_time_point{
+        time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(Core::System::GetInstance())};
+    if (const ResultCode result{Clock::ClockSnapshot::GetCurrentTime(
+            clock_snapshot.user_time, current_time_point, clock_snapshot.user_context)};
+        result != RESULT_SUCCESS) {
+        return result;
     }
 
-    void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) {
-        LOG_WARNING(Service_Time, "(STUBBED) called");
-
-        IPC::ResponseBuilder rb{ctx, 3};
-        rb.Push(RESULT_SUCCESS);
-        rb.Push<u32>(0);
+    TimeZone::CalendarInfo userCalendarInfo{};
+    if (const ResultCode result{
+            time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
+                clock_snapshot.user_time, userCalendarInfo)};
+        result != RESULT_SUCCESS) {
+        return result;
     }
 
-    void LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
-        LOG_WARNING(Service_Time, "(STUBBED) called");
+    clock_snapshot.user_calendar_time = userCalendarInfo.time;
+    clock_snapshot.user_calendar_additional_time = userCalendarInfo.additiona_info;
 
-        ctx.WriteBuffer(&my_time_zone_rule, sizeof(TimeZoneRule));
-
-        IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(RESULT_SUCCESS);
+    if (Clock::ClockSnapshot::GetCurrentTime(clock_snapshot.network_time, current_time_point,
+                                             clock_snapshot.network_context) != RESULT_SUCCESS) {
+        clock_snapshot.network_time = 0;
     }
 
-    void ToCalendarTime(Kernel::HLERequestContext& ctx) {
-        IPC::RequestParser rp{ctx};
-        const u64 posix_time = rp.Pop<u64>();
-        LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
-
-        TimeZoneRule time_zone_rule{};
-        auto buffer = ctx.ReadBuffer();
-        std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
-
-        CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
-        CalendarAdditionalInfo additional_info{};
-
-        PosixToCalendar(posix_time, calendar_time, additional_info, time_zone_rule);
-
-        IPC::ResponseBuilder rb{ctx, 10};
-        rb.Push(RESULT_SUCCESS);
-        rb.PushRaw(calendar_time);
-        rb.PushRaw(additional_info);
+    TimeZone::CalendarInfo networkCalendarInfo{};
+    if (const ResultCode result{
+            time_manager.GetTimeZoneContentManager().GetTimeZoneManager().ToCalendarTimeWithMyRules(
+                clock_snapshot.network_time, networkCalendarInfo)};
+        result != RESULT_SUCCESS) {
+        return result;
     }
 
-    void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
-        IPC::RequestParser rp{ctx};
-        const u64 posix_time = rp.Pop<u64>();
-        LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
+    clock_snapshot.network_calendar_time = networkCalendarInfo.time;
+    clock_snapshot.network_calendar_additional_time = networkCalendarInfo.additiona_info;
+    clock_snapshot.type = type;
 
-        CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
-        CalendarAdditionalInfo additional_info{};
-
-        PosixToCalendar(posix_time, calendar_time, additional_info, my_time_zone_rule);
-
-        IPC::ResponseBuilder rb{ctx, 10};
-        rb.Push(RESULT_SUCCESS);
-        rb.PushRaw(calendar_time);
-        rb.PushRaw(additional_info);
-    }
-
-    void ToPosixTime(Kernel::HLERequestContext& ctx) {
-        // TODO(ogniK): Figure out how to handle multiple times
-        LOG_WARNING(Service_Time, "(STUBBED) called");
-
-        IPC::RequestParser rp{ctx};
-        auto calendar_time = rp.PopRaw<CalendarTime>();
-        auto posix_time = CalendarToPosix(calendar_time, {});
-
-        IPC::ResponseBuilder rb{ctx, 3};
-        rb.Push(RESULT_SUCCESS);
-        rb.PushRaw<u32>(1); // Amount of times we're returning
-        ctx.WriteBuffer(&posix_time, sizeof(u64));
-    }
-
-    void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
-        LOG_WARNING(Service_Time, "(STUBBED) called");
-
-        IPC::RequestParser rp{ctx};
-        auto calendar_time = rp.PopRaw<CalendarTime>();
-        auto posix_time = CalendarToPosix(calendar_time, {});
-
-        IPC::ResponseBuilder rb{ctx, 3};
-        rb.Push(RESULT_SUCCESS);
-        rb.PushRaw<u32>(1); // Amount of times we're returning
-        ctx.WriteBuffer(&posix_time, sizeof(u64));
-    }
-};
+    return RESULT_SUCCESS;
+}
 
 void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_Time, "called");
-
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardUserSystem);
+    rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore());
 }
 
 void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_Time, "called");
-
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardNetworkSystem);
+    rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore());
 }
 
 void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_Time, "called");
-
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushIpcInterface<ISteadyClock>(shared_memory, system);
+    rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore());
 }
 
 void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_Time, "called");
-
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushIpcInterface<ITimeZoneService>();
+    rb.PushIpcInterface<ITimeZoneService>(module->GetTimeManager().GetTimeZoneContentManager());
 }
 
-void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
+void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_Time, "called");
 
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(RESULT_SUCCESS);
-    rb.PushIpcInterface<ISystemClock>(shared_memory, ClockContextType::StandardLocalSystem);
+    auto& steady_clock_core{module->GetTimeManager().GetStandardSteadyClockCore()};
+    if (!steady_clock_core.IsInitialized()) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(ERROR_UNINITIALIZED_CLOCK);
+        return;
+    }
+
+    IPC::RequestParser rp{ctx};
+    const auto context{rp.PopRaw<Clock::SystemClockContext>()};
+    const auto current_time_point{
+        steady_clock_core.GetCurrentTimePoint(Core::System::GetInstance())};
+
+    if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
+        const auto ticks{Clock::TimeSpanType::FromTicks(
+            Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
+            Core::Timing::CNTFREQ)};
+        const s64 base_time_point{context.offset + current_time_point.time_point -
+                                  ticks.ToSeconds()};
+        IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
+        rb.Push(RESULT_SUCCESS);
+        rb.PushRaw(base_time_point);
+        return;
+    }
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(ERROR_TIME_MISMATCH);
 }
 
 void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_Time, "called");
-
     IPC::RequestParser rp{ctx};
-    const auto initial_type = rp.PopRaw<u8>();
+    const auto type = rp.PopRaw<u8>();
 
-    const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
-    const std::time_t time(time_since_epoch);
-    const std::tm* tm = std::localtime(&time);
-    if (tm == nullptr) {
-        LOG_ERROR(Service_Time, "tm is a nullptr");
+    Clock::SystemClockContext user_context{};
+    if (const ResultCode result{
+            module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
+                Core::System::GetInstance(), user_context)};
+        result != RESULT_SUCCESS) {
         IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(RESULT_UNKNOWN); // TODO(ogniK): Find appropriate error code
+        rb.Push(result);
+        return;
+    }
+    Clock::SystemClockContext network_context{};
+    if (const ResultCode result{
+            module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
+                Core::System::GetInstance(), network_context)};
+        result != RESULT_SUCCESS) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
         return;
     }
 
-    const auto& core_timing = system.CoreTiming();
-    const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
-    const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
-
-    CalendarTime calendar_time{};
-    calendar_time.year = static_cast<u16_le>(tm->tm_year + 1900);
-    calendar_time.month = static_cast<u8>(tm->tm_mon + 1);
-    calendar_time.day = static_cast<u8>(tm->tm_mday);
-    calendar_time.hour = static_cast<u8>(tm->tm_hour);
-    calendar_time.minute = static_cast<u8>(tm->tm_min);
-    calendar_time.second = static_cast<u8>(tm->tm_sec);
-
-    ClockSnapshot clock_snapshot{};
-    clock_snapshot.system_posix_time = time_since_epoch;
-    clock_snapshot.network_posix_time = time_since_epoch;
-    clock_snapshot.system_calendar_time = calendar_time;
-    clock_snapshot.network_calendar_time = calendar_time;
-
-    CalendarAdditionalInfo additional_info{};
-    PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
-
-    clock_snapshot.system_calendar_info = additional_info;
-    clock_snapshot.network_calendar_info = additional_info;
-
-    clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
-    clock_snapshot.location_name = LocationName{"UTC"};
-    clock_snapshot.clock_auto_adjustment_enabled = 1;
-    clock_snapshot.type = initial_type;
+    Clock::ClockSnapshot clock_snapshot{};
+    if (const ResultCode result{GetClockSnapshotFromSystemClockContextInternal(
+            &ctx.GetThread(), user_context, network_context, type, clock_snapshot)};
+        result != RESULT_SUCCESS) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+        return;
+    }
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(RESULT_SUCCESS);
-    ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
-}
-
-void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
-    Kernel::HLERequestContext& ctx) {
-    LOG_DEBUG(Service_Time, "called");
-
-    IPC::RequestParser rp{ctx};
-    const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
-    const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
-    const u64 difference =
-        snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
-
-    IPC::ResponseBuilder rb{ctx, 4};
-    rb.Push(RESULT_SUCCESS);
-    rb.PushRaw<u64>(difference);
+    ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
 }
 
 void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_Time, "called");
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushCopyObjects(shared_memory->GetSharedMemoryHolder());
+    rb.PushCopyObjects(module->GetTimeManager().GetSharedMemory().GetSharedMemoryHolder());
 }
 
-void Module::Interface::IsStandardUserSystemClockAutomaticCorrectionEnabled(
-    Kernel::HLERequestContext& ctx) {
-    // ogniK(TODO): When clock contexts are implemented, the value should be read from the context
-    // instead of our shared memory holder
-    LOG_DEBUG(Service_Time, "called");
-
-    IPC::ResponseBuilder rb{ctx, 3};
-    rb.Push(RESULT_SUCCESS);
-    rb.Push<u8>(shared_memory->GetStandardUserSystemClockAutomaticCorrectionEnabled());
-}
-
-void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled(
-    Kernel::HLERequestContext& ctx) {
-    IPC::RequestParser rp{ctx};
-    const auto enabled = rp.Pop<u8>();
-
-    LOG_WARNING(Service_Time, "(PARTIAL IMPLEMENTATION) called");
-
-    // TODO(ogniK): Update clock contexts and correct timespans
-
-    shared_memory->SetStandardUserSystemClockAutomaticCorrectionEnabled(enabled > 0);
-    IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(RESULT_SUCCESS);
-}
-
-Module::Interface::Interface(std::shared_ptr<Module> time,
-                             std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
-                             const char* name)
-    : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)),
-      system(system) {}
+Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
+    : ServiceFramework(name), module{std::move(module)}, system{system} {}
 
 Module::Interface::~Interface() = default;
 
 void InstallInterfaces(Core::System& system) {
-    auto time = std::make_shared<Module>();
-    auto shared_mem = std::make_shared<SharedMemory>(system);
-
-    std::make_shared<Time>(time, shared_mem, system, "time:a")
-        ->InstallAsService(system.ServiceManager());
-    std::make_shared<Time>(time, shared_mem, system, "time:s")
-        ->InstallAsService(system.ServiceManager());
-    std::make_shared<Time>(std::move(time), shared_mem, system, "time:u")
-        ->InstallAsService(system.ServiceManager());
+    auto module = std::make_shared<Module>(system);
+    std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
+    std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
+    std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());
 }
 
 } // namespace Service::Time
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index c32d32860..7b77ac7ea 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -4,102 +4,50 @@
 
 #pragma once
 
-#include <array>
-#include "common/common_funcs.h"
 #include "core/hle/service/service.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/time_manager.h"
+
+namespace Core {
+class System;
+}
 
 namespace Service::Time {
 
-class SharedMemory;
-
-struct LocationName {
-    std::array<u8, 0x24> name;
-};
-static_assert(sizeof(LocationName) == 0x24, "LocationName is incorrect size");
-
-struct CalendarTime {
-    u16_le year;
-    u8 month; // Starts at 1
-    u8 day;   // Starts at 1
-    u8 hour;
-    u8 minute;
-    u8 second;
-};
-static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
-
-struct CalendarAdditionalInfo {
-    u32_le day_of_week;
-    u32_le day_of_year;
-    std::array<u8, 8> name;
-    u8 is_dst;
-    s32_le utc_offset;
-};
-static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
-              "CalendarAdditionalInfo structure has incorrect size");
-
-// TODO(mailwl) RE this structure
-struct TimeZoneRule {
-    INSERT_PADDING_BYTES(0x4000);
-};
-
-struct SteadyClockTimePoint {
-    using SourceID = std::array<u8, 16>;
-
-    u64_le value;
-    SourceID source_id;
-};
-static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
-
-struct SystemClockContext {
-    u64_le offset;
-    SteadyClockTimePoint time_point;
-};
-static_assert(sizeof(SystemClockContext) == 0x20,
-              "SystemClockContext structure has incorrect size");
-
-struct ClockSnapshot {
-    SystemClockContext user_clock_context;
-    SystemClockContext network_clock_context;
-    s64_le system_posix_time;
-    s64_le network_posix_time;
-    CalendarTime system_calendar_time;
-    CalendarTime network_calendar_time;
-    CalendarAdditionalInfo system_calendar_info;
-    CalendarAdditionalInfo network_calendar_info;
-    SteadyClockTimePoint steady_clock_timepoint;
-    LocationName location_name;
-    u8 clock_auto_adjustment_enabled;
-    u8 type;
-    u8 version;
-    INSERT_PADDING_BYTES(1);
-};
-static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
-
 class Module final {
 public:
+    Module(Core::System& system) : time_manager{system} {}
+
     class Interface : public ServiceFramework<Interface> {
     public:
-        explicit Interface(std::shared_ptr<Module> time,
-                           std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
-                           const char* name);
+        explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
         ~Interface() override;
 
         void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
         void GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx);
         void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
         void GetTimeZoneService(Kernel::HLERequestContext& ctx);
-        void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
+        void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx);
         void GetClockSnapshot(Kernel::HLERequestContext& ctx);
-        void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
         void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
-        void IsStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
-        void SetStandardUserSystemClockAutomaticCorrectionEnabled(Kernel::HLERequestContext& ctx);
+
+    private:
+        ResultCode GetClockSnapshotFromSystemClockContextInternal(
+            Kernel::Thread* thread, Clock::SystemClockContext user_context,
+            Clock::SystemClockContext network_context, u8 type,
+            Clock::ClockSnapshot& cloc_snapshot);
 
     protected:
-        std::shared_ptr<Module> time;
-        std::shared_ptr<SharedMemory> shared_memory;
+        std::shared_ptr<Module> module;
         Core::System& system;
     };
+
+    TimeManager& GetTimeManager() {
+        return time_manager;
+    }
+
+private:
+    TimeManager time_manager;
 };
 
 /// Registers all Time services with the specified service manager.
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
new file mode 100644
index 000000000..9d6c55865
--- /dev/null
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -0,0 +1,137 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <chrono>
+#include <ctime>
+
+#include "core/hle/service/time/ephemeral_network_system_clock_context_writer.h"
+#include "core/hle/service/time/local_system_clock_context_writer.h"
+#include "core/hle/service/time/network_system_clock_context_writer.h"
+#include "core/hle/service/time/time_manager.h"
+#include "core/settings.h"
+
+namespace Service::Time {
+
+constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL};
+
+static std::chrono::seconds GetSecondsSinceEpoch() {
+    return std::chrono::duration_cast<std::chrono::seconds>(
+               std::chrono::system_clock::now().time_since_epoch()) +
+           Settings::values.custom_rtc_differential;
+}
+
+static s64 GetExternalRtcValue() {
+    return GetSecondsSinceEpoch().count();
+}
+
+TimeManager::TimeManager(Core::System& system)
+    : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
+      standard_network_system_clock_core{standard_steady_clock_core},
+      standard_user_system_clock_core{standard_local_system_clock_core,
+                                      standard_network_system_clock_core, system},
+      ephemeral_network_system_clock_core{tick_based_steady_clock_core},
+      local_system_clock_context_writer{
+          std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
+      network_system_clock_context_writer{
+          std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
+      ephemeral_network_system_clock_context_writer{
+          std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
+      time_zone_content_manager{*this, system} {
+
+    const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
+    SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
+    SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
+    SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
+    SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
+    SetupEphemeralNetworkSystemClock();
+}
+
+TimeManager::~TimeManager() = default;
+
+void TimeManager::SetupTimeZoneManager(std::string location_name,
+                                       Clock::SteadyClockTimePoint time_zone_updated_time_point,
+                                       std::size_t total_location_name_count,
+                                       u128 time_zone_rule_version,
+                                       FileSys::VirtualFile& vfs_file) {
+    if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
+            location_name, vfs_file) != RESULT_SUCCESS) {
+        UNREACHABLE();
+        return;
+    }
+
+    time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
+    time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
+        total_location_name_count);
+    time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version);
+    time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
+}
+
+void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
+                                           Clock::TimeSpanType setup_value,
+                                           Clock::TimeSpanType internal_offset,
+                                           bool is_rtc_reset_detected) {
+    standard_steady_clock_core.SetClockSourceId(clock_source_id);
+    standard_steady_clock_core.SetSetupValue(setup_value);
+    standard_steady_clock_core.SetInternalOffset(internal_offset);
+    standard_steady_clock_core.MarkAsInitialized();
+
+    const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
+    shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
+}
+
+void TimeManager::SetupStandardLocalSystemClock(Core::System& system,
+                                                Clock::SystemClockContext clock_context,
+                                                s64 posix_time) {
+    standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer);
+
+    const auto current_time_point{
+        standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
+    if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
+        standard_local_system_clock_core.SetSystemClockContext(clock_context);
+    } else {
+        if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) {
+            UNREACHABLE();
+            return;
+        }
+    }
+
+    standard_local_system_clock_core.MarkAsInitialized();
+}
+
+void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
+                                                  Clock::TimeSpanType sufficient_accuracy) {
+    standard_network_system_clock_core.SetUpdateCallbackInstance(
+        network_system_clock_context_writer);
+
+    if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) {
+        UNREACHABLE();
+        return;
+    }
+
+    standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
+        sufficient_accuracy);
+    standard_network_system_clock_core.MarkAsInitialized();
+}
+
+void TimeManager::SetupStandardUserSystemClock(
+    Core::System& system, bool is_automatic_correction_enabled,
+    Clock::SteadyClockTimePoint steady_clock_time_point) {
+    if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
+            system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
+        UNREACHABLE();
+        return;
+    }
+
+    standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
+    standard_user_system_clock_core.MarkAsInitialized();
+    shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
+}
+
+void TimeManager::SetupEphemeralNetworkSystemClock() {
+    ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
+        ephemeral_network_system_clock_context_writer);
+    ephemeral_network_system_clock_core.MarkAsInitialized();
+}
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
new file mode 100644
index 000000000..8e65f0d22
--- /dev/null
+++ b/src/core/hle/service/time/time_manager.h
@@ -0,0 +1,117 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/file_sys/vfs_types.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
+#include "core/hle/service/time/standard_local_system_clock_core.h"
+#include "core/hle/service/time/standard_network_system_clock_core.h"
+#include "core/hle/service/time/standard_steady_clock_core.h"
+#include "core/hle/service/time/standard_user_system_clock_core.h"
+#include "core/hle/service/time/tick_based_steady_clock_core.h"
+#include "core/hle/service/time/time_sharedmemory.h"
+#include "core/hle/service/time/time_zone_content_manager.h"
+
+namespace Service::Time {
+
+namespace Clock {
+class EphemeralNetworkSystemClockContextWriter;
+class LocalSystemClockContextWriter;
+class NetworkSystemClockContextWriter;
+} // namespace Clock
+
+// Parts of this implementation were based on Ryujinx (https://github.com/Ryujinx/Ryujinx/pull/783).
+// This code was released under public domain.
+
+class TimeManager final {
+public:
+    explicit TimeManager(Core::System& system);
+    ~TimeManager();
+
+    Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
+        return standard_steady_clock_core;
+    }
+
+    const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
+        return standard_steady_clock_core;
+    }
+
+    Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
+        return standard_local_system_clock_core;
+    }
+
+    const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
+        return standard_local_system_clock_core;
+    }
+
+    Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
+        return standard_network_system_clock_core;
+    }
+
+    const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
+        return standard_network_system_clock_core;
+    }
+
+    Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
+        return standard_user_system_clock_core;
+    }
+
+    const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
+        return standard_user_system_clock_core;
+    }
+
+    TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
+        return time_zone_content_manager;
+    }
+
+    const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
+        return time_zone_content_manager;
+    }
+
+    SharedMemory& GetSharedMemory() {
+        return shared_memory;
+    }
+
+    const SharedMemory& GetSharedMemory() const {
+        return shared_memory;
+    }
+
+    void SetupTimeZoneManager(std::string location_name,
+                              Clock::SteadyClockTimePoint time_zone_updated_time_point,
+                              std::size_t total_location_name_count, u128 time_zone_rule_version,
+                              FileSys::VirtualFile& vfs_file);
+
+private:
+    void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
+                                  Clock::TimeSpanType setup_value,
+                                  Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected);
+    void SetupStandardLocalSystemClock(Core::System& system,
+                                       Clock::SystemClockContext clock_context, s64 posix_time);
+    void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
+                                         Clock::TimeSpanType sufficient_accuracy);
+    void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
+                                      Clock::SteadyClockTimePoint steady_clock_time_point);
+    void SetupEphemeralNetworkSystemClock();
+
+    SharedMemory shared_memory;
+
+    Clock::StandardSteadyClockCore standard_steady_clock_core;
+    Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
+    Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
+    Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
+    Clock::StandardUserSystemClockCore standard_user_system_clock_core;
+    Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
+
+    std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
+    std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
+    std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
+        ephemeral_network_system_clock_context_writer;
+
+    TimeZone::TimeZoneContentManager time_zone_content_manager;
+};
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index 4035f5072..9b03191bf 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -3,20 +3,21 @@
 // Refer to the license.txt file included.
 
 #include "core/core.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/steady_clock_core.h"
 #include "core/hle/service/time/time_sharedmemory.h"
 
 namespace Service::Time {
-const std::size_t SHARED_MEMORY_SIZE = 0x1000;
+
+static constexpr std::size_t SHARED_MEMORY_SIZE{0x1000};
 
 SharedMemory::SharedMemory(Core::System& system) : system(system) {
     shared_memory_holder = Kernel::SharedMemory::Create(
         system.Kernel(), nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
         Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "Time:SharedMemory");
-
-    // Seems static from 1.0.0 -> 8.1.0. Specific games seem to check this value and crash
-    // if it's set to anything else
-    shared_memory_format.format_version = 14;
-    std::memcpy(shared_memory_holder->GetPointer(), &shared_memory_format, sizeof(Format));
+    std::memset(shared_memory_holder->GetPointer(), 0, SHARED_MEMORY_SIZE);
 }
 
 SharedMemory::~SharedMemory() = default;
@@ -25,44 +26,32 @@ std::shared_ptr<Kernel::SharedMemory> SharedMemory::GetSharedMemoryHolder() cons
     return shared_memory_holder;
 }
 
-void SharedMemory::SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint) {
+void SharedMemory::SetupStandardSteadyClock(Core::System& system,
+                                            const Common::UUID& clock_source_id,
+                                            Clock::TimeSpanType current_time_point) {
+    const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks(
+        Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()),
+        Core::Timing::CNTFREQ)};
+    const Clock::SteadyClockContext context{
+        static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
+        clock_source_id};
     shared_memory_format.standard_steady_clock_timepoint.StoreData(
-        shared_memory_holder->GetPointer(), timepoint);
+        shared_memory_holder->GetPointer(), context);
 }
 
-void SharedMemory::SetStandardLocalSystemClockContext(const SystemClockContext& context) {
+void SharedMemory::UpdateLocalSystemClockContext(const Clock::SystemClockContext& context) {
     shared_memory_format.standard_local_system_clock_context.StoreData(
         shared_memory_holder->GetPointer(), context);
 }
 
-void SharedMemory::SetStandardNetworkSystemClockContext(const SystemClockContext& context) {
+void SharedMemory::UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context) {
     shared_memory_format.standard_network_system_clock_context.StoreData(
         shared_memory_holder->GetPointer(), context);
 }
 
-void SharedMemory::SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled) {
+void SharedMemory::SetAutomaticCorrectionEnabled(bool is_enabled) {
     shared_memory_format.standard_user_system_clock_automatic_correction.StoreData(
-        shared_memory_holder->GetPointer(), enabled);
-}
-
-SteadyClockTimePoint SharedMemory::GetStandardSteadyClockTimepoint() {
-    return shared_memory_format.standard_steady_clock_timepoint.ReadData(
-        shared_memory_holder->GetPointer());
-}
-
-SystemClockContext SharedMemory::GetStandardLocalSystemClockContext() {
-    return shared_memory_format.standard_local_system_clock_context.ReadData(
-        shared_memory_holder->GetPointer());
-}
-
-SystemClockContext SharedMemory::GetStandardNetworkSystemClockContext() {
-    return shared_memory_format.standard_network_system_clock_context.ReadData(
-        shared_memory_holder->GetPointer());
-}
-
-bool SharedMemory::GetStandardUserSystemClockAutomaticCorrectionEnabled() {
-    return shared_memory_format.standard_user_system_clock_automatic_correction.ReadData(
-        shared_memory_holder->GetPointer());
+        shared_memory_holder->GetPointer(), is_enabled);
 }
 
 } // namespace Service::Time
diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h
index 904a96430..5976b2046 100644
--- a/src/core/hle/service/time/time_sharedmemory.h
+++ b/src/core/hle/service/time/time_sharedmemory.h
@@ -5,11 +5,14 @@
 #pragma once
 
 #include "common/common_types.h"
+#include "common/uuid.h"
 #include "core/hle/kernel/shared_memory.h"
-#include "core/hle/service/time/time.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/service/time/clock_types.h"
 
 namespace Service::Time {
-class SharedMemory {
+
+class SharedMemory final {
 public:
     explicit SharedMemory(Core::System& system);
     ~SharedMemory();
@@ -17,22 +20,10 @@ public:
     // Return the shared memory handle
     std::shared_ptr<Kernel::SharedMemory> GetSharedMemoryHolder() const;
 
-    // Set memory barriers in shared memory and update them
-    void SetStandardSteadyClockTimepoint(const SteadyClockTimePoint& timepoint);
-    void SetStandardLocalSystemClockContext(const SystemClockContext& context);
-    void SetStandardNetworkSystemClockContext(const SystemClockContext& context);
-    void SetStandardUserSystemClockAutomaticCorrectionEnabled(bool enabled);
-
-    // Pull from memory barriers in the shared memory
-    SteadyClockTimePoint GetStandardSteadyClockTimepoint();
-    SystemClockContext GetStandardLocalSystemClockContext();
-    SystemClockContext GetStandardNetworkSystemClockContext();
-    bool GetStandardUserSystemClockAutomaticCorrectionEnabled();
-
     // TODO(ogniK): We have to properly simulate memory barriers, how are we going to do this?
     template <typename T, std::size_t Offset>
     struct MemoryBarrier {
-        static_assert(std::is_trivially_constructible_v<T>, "T must be trivially constructable");
+        static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
         u32_le read_attempt{};
         std::array<T, 2> data{};
 
@@ -57,16 +48,22 @@ public:
 
     // Shared memory format
     struct Format {
-        MemoryBarrier<SteadyClockTimePoint, 0x0> standard_steady_clock_timepoint;
-        MemoryBarrier<SystemClockContext, 0x38> standard_local_system_clock_context;
-        MemoryBarrier<SystemClockContext, 0x80> standard_network_system_clock_context;
+        MemoryBarrier<Clock::SteadyClockContext, 0x0> standard_steady_clock_timepoint;
+        MemoryBarrier<Clock::SystemClockContext, 0x38> standard_local_system_clock_context;
+        MemoryBarrier<Clock::SystemClockContext, 0x80> standard_network_system_clock_context;
         MemoryBarrier<bool, 0xc8> standard_user_system_clock_automatic_correction;
         u32_le format_version;
     };
     static_assert(sizeof(Format) == 0xd8, "Format is an invalid size");
 
+    void SetupStandardSteadyClock(Core::System& system, const Common::UUID& clock_source_id,
+                                  Clock::TimeSpanType currentTimePoint);
+    void UpdateLocalSystemClockContext(const Clock::SystemClockContext& context);
+    void UpdateNetworkSystemClockContext(const Clock::SystemClockContext& context);
+    void SetAutomaticCorrectionEnabled(bool is_enabled);
+
 private:
-    std::shared_ptr<Kernel::SharedMemory> shared_memory_holder{};
+    std::shared_ptr<Kernel::SharedMemory> shared_memory_holder;
     Core::System& system;
     Format shared_memory_format{};
 };
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
new file mode 100644
index 000000000..57b1a2bca
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -0,0 +1,125 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <sstream>
+
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs.h"
+#include "core/file_sys/system_archive/system_archive.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/hle/service/time/time_manager.h"
+#include "core/hle/service/time/time_zone_content_manager.h"
+
+namespace Service::Time::TimeZone {
+
+constexpr u64 time_zone_binary_titleid{0x010000000000080E};
+
+static FileSys::VirtualDir GetTimeZoneBinary(Core::System& system) {
+    const auto* nand{system.GetFileSystemController().GetSystemNANDContents()};
+    const auto nca{nand->GetEntry(time_zone_binary_titleid, FileSys::ContentRecordType::Data)};
+
+    FileSys::VirtualFile romfs;
+    if (nca) {
+        romfs = nca->GetRomFS();
+    }
+
+    if (!romfs) {
+        romfs = FileSys::SystemArchive::SynthesizeSystemArchive(time_zone_binary_titleid);
+    }
+
+    if (!romfs) {
+        LOG_ERROR(Service_Time, "Failed to find or synthesize {:016X!}", time_zone_binary_titleid);
+        return {};
+    }
+
+    return FileSys::ExtractRomFS(romfs);
+}
+
+static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
+    const FileSys::VirtualDir extracted_romfs{GetTimeZoneBinary(system)};
+    if (!extracted_romfs) {
+        LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
+        return {};
+    }
+
+    const FileSys::VirtualFile binary_list{extracted_romfs->GetFile("binaryList.txt")};
+    if (!binary_list) {
+        LOG_ERROR(Service_Time, "{:016X} has no file binaryList.txt!", time_zone_binary_titleid);
+        return {};
+    }
+
+    std::vector<char> raw_data(binary_list->GetSize());
+    binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
+
+    std::stringstream data_stream{raw_data.data()};
+    std::string name;
+    std::vector<std::string> location_name_cache;
+    while (std::getline(data_stream, name)) {
+        name.pop_back(); // Remove carriage return
+        location_name_cache.emplace_back(std::move(name));
+    }
+    return location_name_cache;
+}
+
+TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
+    : system{system}, location_name_cache{BuildLocationNameCache(system)} {
+    if (FileSys::VirtualFile vfs_file; GetTimeZoneInfoFile("GMT", vfs_file) == RESULT_SUCCESS) {
+        const auto time_point{
+            time_manager.GetStandardSteadyClockCore().GetCurrentTimePoint(system)};
+        time_manager.SetupTimeZoneManager("GMT", time_point, location_name_cache.size(), {},
+                                          vfs_file);
+    } else {
+        time_zone_manager.MarkAsInitialized();
+    }
+}
+
+ResultCode TimeZoneContentManager::LoadTimeZoneRule(TimeZoneRule& rules,
+                                                    const std::string& location_name) const {
+    FileSys::VirtualFile vfs_file;
+    if (const ResultCode result{GetTimeZoneInfoFile(location_name, vfs_file)};
+        result != RESULT_SUCCESS) {
+        return result;
+    }
+
+    return time_zone_manager.ParseTimeZoneRuleBinary(rules, vfs_file);
+}
+
+bool TimeZoneContentManager::IsLocationNameValid(const std::string& location_name) const {
+    return std::find(location_name_cache.begin(), location_name_cache.end(), location_name) !=
+           location_name_cache.end();
+}
+
+ResultCode TimeZoneContentManager::GetTimeZoneInfoFile(const std::string& location_name,
+                                                       FileSys::VirtualFile& vfs_file) const {
+    if (!IsLocationNameValid(location_name)) {
+        return ERROR_TIME_NOT_FOUND;
+    }
+
+    const FileSys::VirtualDir extracted_romfs{GetTimeZoneBinary(system)};
+    if (!extracted_romfs) {
+        LOG_ERROR(Service_Time, "Failed to extract RomFS for {:016X}!", time_zone_binary_titleid);
+        return ERROR_TIME_NOT_FOUND;
+    }
+
+    const FileSys::VirtualDir zoneinfo_dir{extracted_romfs->GetSubdirectory("zoneinfo")};
+    if (!zoneinfo_dir) {
+        LOG_ERROR(Service_Time, "{:016X} has no directory zoneinfo!", time_zone_binary_titleid);
+        return ERROR_TIME_NOT_FOUND;
+    }
+
+    vfs_file = zoneinfo_dir->GetFile(location_name);
+    if (!vfs_file) {
+        LOG_ERROR(Service_Time, "{:016X} has no file \"{}\"!", time_zone_binary_titleid,
+                  location_name);
+        return ERROR_TIME_NOT_FOUND;
+    }
+
+    return RESULT_SUCCESS;
+}
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
new file mode 100644
index 000000000..4f302c3b9
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_content_manager.h
@@ -0,0 +1,46 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "core/hle/service/time/time_zone_manager.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Time {
+class TimeManager;
+}
+
+namespace Service::Time::TimeZone {
+
+class TimeZoneContentManager final {
+public:
+    TimeZoneContentManager(TimeManager& time_manager, Core::System& system);
+
+    TimeZoneManager& GetTimeZoneManager() {
+        return time_zone_manager;
+    }
+
+    const TimeZoneManager& GetTimeZoneManager() const {
+        return time_zone_manager;
+    }
+
+    ResultCode LoadTimeZoneRule(TimeZoneRule& rules, const std::string& location_name) const;
+
+private:
+    bool IsLocationNameValid(const std::string& location_name) const;
+    ResultCode GetTimeZoneInfoFile(const std::string& location_name,
+                                   FileSys::VirtualFile& vfs_file) const;
+
+    Core::System& system;
+    TimeZoneManager time_zone_manager;
+    const std::vector<std::string> location_name_cache;
+};
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
new file mode 100644
index 000000000..c06ca538a
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -0,0 +1,1038 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <climits>
+
+#include <boost/safe_numerics/safe_integer.hpp>
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs.h"
+#include "core/file_sys/system_archive/system_archive.h"
+#include "core/hle/service/time/time_zone_manager.h"
+
+namespace Service::Time::TimeZone {
+
+static constexpr s32 epoch_year{1970};
+static constexpr s32 year_base{1900};
+static constexpr s32 epoch_week_day{4};
+static constexpr s32 seconds_per_minute{60};
+static constexpr s32 minutes_per_hour{60};
+static constexpr s32 hours_per_day{24};
+static constexpr s32 days_per_week{7};
+static constexpr s32 days_per_normal_year{365};
+static constexpr s32 days_per_leap_year{366};
+static constexpr s32 months_per_year{12};
+static constexpr s32 seconds_per_hour{seconds_per_minute * minutes_per_hour};
+static constexpr s32 seconds_per_day{seconds_per_hour * hours_per_day};
+static constexpr s32 years_per_repeat{400};
+static constexpr s64 average_seconds_per_year{31556952};
+static constexpr s64 seconds_per_repeat{years_per_repeat * average_seconds_per_year};
+
+struct Rule {
+    enum class Type : u32 { JulianDay, DayOfYear, MonthNthDayOfWeek };
+    Type rule_type{};
+    s32 day{};
+    s32 week{};
+    s32 month{};
+    s32 transition_time{};
+};
+
+struct CalendarTimeInternal {
+    s64 year{};
+    s8 month{};
+    s8 day{};
+    s8 hour{};
+    s8 minute{};
+    s8 second{};
+    int Compare(const CalendarTimeInternal& other) const {
+        if (year != other.year) {
+            if (year < other.year) {
+                return -1;
+            }
+            return 1;
+        }
+        if (month != other.month) {
+            return month - other.month;
+        }
+        if (day != other.day) {
+            return day - other.day;
+        }
+        if (hour != other.hour) {
+            return hour - other.hour;
+        }
+        if (minute != other.minute) {
+            return minute - other.minute;
+        }
+        if (second != other.second) {
+            return second - other.second;
+        }
+        return {};
+    }
+};
+
+template <typename TResult, typename TOperand>
+static bool SafeAdd(TResult& result, TOperand op) {
+    const boost::safe_numerics::safe<TResult> safe_result{result};
+    const boost::safe_numerics::safe<TOperand> safe_op{op};
+    try {
+        result = safe_result + safe_op;
+    } catch (const std::exception&) {
+        return {}; // Failed with undefined behavior
+    }
+    return true;
+}
+
+template <typename TResult, typename TUnit, typename TBase>
+static bool SafeNormalize(TResult& result, TUnit& unit, TBase base) {
+    TUnit delta{};
+    if (unit >= 0) {
+        delta = unit / base;
+    } else {
+        delta = -1 - (-1 - unit) / base;
+    }
+    unit -= delta * base;
+    return SafeAdd(result, delta);
+}
+
+template <typename T>
+static constexpr bool IsLeapYear(T year) {
+    return ((year) % 4) == 0 && (((year) % 100) != 0 || ((year) % 400) == 0);
+}
+
+template <typename T>
+static constexpr T GetYearLengthInDays(T year) {
+    return IsLeapYear(year) ? days_per_leap_year : days_per_normal_year;
+}
+
+static constexpr s64 GetLeapDaysFromYearPositive(s64 year) {
+    return year / 4 - year / 100 + year / years_per_repeat;
+}
+
+static constexpr s64 GetLeapDaysFromYear(s64 year) {
+    if (year < 0) {
+        return -1 - GetLeapDaysFromYearPositive(-1 - year);
+    } else {
+        return GetLeapDaysFromYearPositive(year);
+    }
+}
+
+static constexpr int GetMonthLength(bool is_leap_year, int month) {
+    constexpr std::array<int, 12> month_lengths{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+    constexpr std::array<int, 12> month_lengths_leap{31, 29, 31, 30, 31, 30,
+                                                     31, 31, 30, 31, 30, 31};
+    return is_leap_year ? month_lengths_leap[month] : month_lengths[month];
+}
+
+static constexpr bool IsDigit(char value) {
+    return value >= '0' && value <= '9';
+}
+
+static constexpr int GetQZName(const char* name, int offset, char delimiter) {
+    while (name[offset] != '\0' && name[offset] != delimiter) {
+        offset++;
+    }
+    return offset;
+}
+
+static constexpr int GetTZName(const char* name, int offset) {
+    for (char value{name[offset]};
+         value != '\0' && !IsDigit(value) && value != ',' && value != '-' && value != '+';
+         offset++) {
+        value = name[offset];
+    }
+    return offset;
+}
+
+static constexpr bool GetInteger(const char* name, int& offset, int& value, int min, int max) {
+    value = 0;
+    char temp{name[offset]};
+    if (!IsDigit(temp)) {
+        return {};
+    }
+    do {
+        value = value * 10 + (temp - '0');
+        if (value > max) {
+            return {};
+        }
+        temp = name[offset];
+    } while (IsDigit(temp));
+
+    return value >= min;
+}
+
+static constexpr bool GetSeconds(const char* name, int& offset, int& seconds) {
+    seconds = 0;
+    int value{};
+    if (!GetInteger(name, offset, value, 0, hours_per_day * days_per_week - 1)) {
+        return {};
+    }
+    seconds = value * seconds_per_hour;
+
+    if (name[offset] == ':') {
+        offset++;
+        if (!GetInteger(name, offset, value, 0, minutes_per_hour - 1)) {
+            return {};
+        }
+        seconds += value * seconds_per_minute;
+        if (name[offset] == ':') {
+            offset++;
+            if (!GetInteger(name, offset, value, 0, seconds_per_minute)) {
+                return {};
+            }
+            seconds += value;
+        }
+    }
+    return true;
+}
+
+static constexpr bool GetOffset(const char* name, int& offset, int& value) {
+    bool is_negative{};
+    if (name[offset] == '-') {
+        is_negative = true;
+        offset++;
+    } else if (name[offset] == '+') {
+        offset++;
+    }
+    if (!GetSeconds(name, offset, value)) {
+        return {};
+    }
+    if (is_negative) {
+        value = -value;
+    }
+    return true;
+}
+
+static constexpr bool GetRule(const char* name, int& position, Rule& rule) {
+    bool is_valid{};
+    if (name[position] == 'J') {
+        position++;
+        rule.rule_type = Rule::Type::JulianDay;
+        is_valid = GetInteger(name, position, rule.day, 1, days_per_normal_year);
+    } else if (name[position] == 'M') {
+        position++;
+        rule.rule_type = Rule::Type::MonthNthDayOfWeek;
+        is_valid = GetInteger(name, position, rule.month, 1, months_per_year);
+        if (!is_valid) {
+            return {};
+        }
+        if (name[position++] != '.') {
+            return {};
+        }
+        is_valid = GetInteger(name, position, rule.week, 1, 5);
+        if (!is_valid) {
+            return {};
+        }
+        if (name[position++] != '.') {
+            return {};
+        }
+        is_valid = GetInteger(name, position, rule.day, 0, days_per_week - 1);
+    } else if (isdigit(name[position])) {
+        rule.rule_type = Rule::Type::DayOfYear;
+        is_valid = GetInteger(name, position, rule.day, 0, days_per_leap_year - 1);
+    } else {
+        return {};
+    }
+    if (!is_valid) {
+        return {};
+    }
+    if (name[position] == '/') {
+        position++;
+        return GetOffset(name, position, rule.transition_time);
+    } else {
+        rule.transition_time = 2 * seconds_per_hour;
+    }
+    return true;
+}
+
+static constexpr int TransitionTime(int year, Rule rule, int offset) {
+    int value{};
+    switch (rule.rule_type) {
+    case Rule::Type::JulianDay:
+        value = (rule.day - 1) * seconds_per_day;
+        if (IsLeapYear(year) && rule.day >= 60) {
+            value += seconds_per_day;
+        }
+        break;
+    case Rule::Type::DayOfYear:
+        value = rule.day * seconds_per_day;
+        break;
+    case Rule::Type::MonthNthDayOfWeek: {
+        // Use Zeller's Congruence (https://en.wikipedia.org/wiki/Zeller%27s_congruence) to
+        // calculate the day of the week for any Julian or Gregorian calendar date.
+        const int m1{(rule.month + 9) % 12 + 1};
+        const int yy0{(rule.month <= 2) ? (year - 1) : year};
+        const int yy1{yy0 / 100};
+        const int yy2{yy0 % 100};
+        int day_of_week{((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7};
+
+        if (day_of_week < 0) {
+            day_of_week += days_per_week;
+        }
+        int day{rule.day - day_of_week};
+        if (day < 0) {
+            day += days_per_week;
+        }
+        for (int i{1}; i < rule.week; i++) {
+            if (day + days_per_week >= GetMonthLength(IsLeapYear(year), rule.month - 1)) {
+                break;
+            }
+            day += days_per_week;
+        }
+
+        value = day * seconds_per_day;
+        for (int index{}; index < rule.month - 1; ++index) {
+            value += GetMonthLength(IsLeapYear(year), index) * seconds_per_day;
+        }
+        break;
+    }
+    default:
+        UNREACHABLE();
+    }
+    return value + rule.transition_time + offset;
+}
+
+static bool ParsePosixName(const char* name, TimeZoneRule& rule) {
+    constexpr char default_rule[]{",M4.1.0,M10.5.0"};
+    const char* std_name{name};
+    int std_len{};
+    int offset{};
+    int std_offset{};
+
+    if (name[offset] == '<') {
+        offset++;
+        std_name = name + offset;
+        const int std_name_offset{offset};
+        offset = GetQZName(name, offset, '>');
+        if (name[offset] != '>') {
+            return {};
+        }
+        std_len = offset - std_name_offset;
+        offset++;
+    } else {
+        offset = GetTZName(name, offset);
+        std_len = offset;
+    }
+    if (!std_len) {
+        return {};
+    }
+    if (!GetOffset(name, offset, std_offset)) {
+        return {};
+    }
+
+    int char_count{std_len + 1};
+    int dest_len{};
+    int dest_offset{};
+    const char* dest_name{name + offset};
+    if (rule.chars.size() < char_count) {
+        return {};
+    }
+
+    if (name[offset] != '\0') {
+        if (name[offset] == '<') {
+            dest_name = name + (++offset);
+            const int dest_name_offset{offset};
+            offset = GetQZName(name, offset, '>');
+            if (name[offset] != '>') {
+                return {};
+            }
+            dest_len = offset - dest_name_offset;
+            offset++;
+        } else {
+            dest_name = name + (offset);
+            offset = GetTZName(name, offset);
+            dest_len = offset;
+        }
+        if (dest_len == 0) {
+            return {};
+        }
+        char_count += dest_len + 1;
+        if (rule.chars.size() < char_count) {
+            return {};
+        }
+        if (name[offset] != '\0' && name[offset] != ',' && name[offset] != ';') {
+            if (!GetOffset(name, offset, dest_offset)) {
+                return {};
+            }
+        } else {
+            dest_offset = std_offset - seconds_per_hour;
+        }
+        if (name[offset] == '\0') {
+            name = default_rule;
+            offset = 0;
+        }
+        if (name[offset] == ',' || name[offset] == ';') {
+            offset++;
+
+            Rule start{};
+            if (!GetRule(name, offset, start)) {
+                return {};
+            }
+            if (name[offset++] != ',') {
+                return {};
+            }
+
+            Rule end{};
+            if (!GetRule(name, offset, end)) {
+                return {};
+            }
+            if (name[offset] != '\0') {
+                return {};
+            }
+
+            rule.type_count = 2;
+            rule.ttis[0].gmt_offset = -dest_offset;
+            rule.ttis[0].is_dst = true;
+            rule.ttis[0].abbreviation_list_index = std_len + 1;
+            rule.ttis[1].gmt_offset = -std_offset;
+            rule.ttis[1].is_dst = false;
+            rule.ttis[1].abbreviation_list_index = 0;
+            rule.default_type = 0;
+
+            s64 jan_first{};
+            int time_count{};
+            int jan_offset{};
+            int year_beginning{epoch_year};
+            do {
+                const int year_seconds{GetYearLengthInDays(year_beginning - 1) * seconds_per_day};
+                year_beginning--;
+                if (!SafeAdd(jan_first, -year_seconds)) {
+                    jan_offset = -year_seconds;
+                    break;
+                }
+            } while (epoch_year - years_per_repeat / 2 < year_beginning);
+
+            int year_limit{year_beginning + years_per_repeat + 1};
+            int year{};
+            for (year = year_beginning; year < year_limit; year++) {
+                int start_time{TransitionTime(year, start, std_offset)};
+                int end_time{TransitionTime(year, end, dest_offset)};
+                const int year_seconds{GetYearLengthInDays(year) * seconds_per_day};
+                const bool is_reversed{end_time < start_time};
+                if (is_reversed) {
+                    int swap{start_time};
+                    start_time = end_time;
+                    end_time = swap;
+                }
+
+                if (is_reversed ||
+                    (start_time < end_time &&
+                     (end_time - start_time < (year_seconds + (std_offset - dest_offset))))) {
+                    if (rule.ats.size() - 2 < time_count) {
+                        break;
+                    }
+
+                    rule.ats[time_count] = jan_first;
+                    if (SafeAdd(rule.ats[time_count], jan_offset + start_time)) {
+                        rule.types[time_count++] = is_reversed ? 1 : 0;
+                    } else if (jan_offset != 0) {
+                        rule.default_type = is_reversed ? 1 : 0;
+                    }
+
+                    rule.ats[time_count] = jan_first;
+                    if (SafeAdd(rule.ats[time_count], jan_offset + end_time)) {
+                        rule.types[time_count++] = is_reversed ? 0 : 1;
+                        year_limit = year + years_per_repeat + 1;
+                    } else if (jan_offset != 0) {
+                        rule.default_type = is_reversed ? 0 : 1;
+                    }
+                }
+                if (!SafeAdd(jan_first, jan_offset + year_seconds)) {
+                    break;
+                }
+                jan_offset = 0;
+            }
+            rule.time_count = time_count;
+            if (time_count == 0) {
+                rule.type_count = 1;
+            } else if (years_per_repeat < year - year_beginning) {
+                rule.go_back = true;
+                rule.go_ahead = true;
+            }
+        } else {
+            if (name[offset] == '\0') {
+                return {};
+            }
+
+            s64 their_std_offset{};
+            for (int index{}; index < rule.time_count; ++index) {
+                const s8 type{rule.types[index]};
+                if (rule.ttis[type].is_standard_time_daylight) {
+                    their_std_offset = -rule.ttis[type].gmt_offset;
+                }
+            }
+
+            s64 their_offset{their_std_offset};
+            for (int index{}; index < rule.time_count; ++index) {
+                const s8 type{rule.types[index]};
+                rule.types[index] = rule.ttis[type].is_dst ? 1 : 0;
+                if (!rule.ttis[type].is_gmt) {
+                    if (!rule.ttis[type].is_standard_time_daylight) {
+                        rule.ats[index] += dest_offset - their_std_offset;
+                    } else {
+                        rule.ats[index] += std_offset - their_std_offset;
+                    }
+                }
+                their_offset = -rule.ttis[type].gmt_offset;
+                if (!rule.ttis[type].is_dst) {
+                    their_std_offset = their_offset;
+                }
+            }
+            rule.ttis[0].gmt_offset = -std_offset;
+            rule.ttis[0].is_dst = false;
+            rule.ttis[0].abbreviation_list_index = 0;
+            rule.ttis[1].gmt_offset = -dest_offset;
+            rule.ttis[1].is_dst = true;
+            rule.ttis[1].abbreviation_list_index = std_len + 1;
+            rule.type_count = 2;
+            rule.default_type = 0;
+        }
+    } else {
+        // Default is standard time
+        rule.type_count = 1;
+        rule.time_count = 0;
+        rule.default_type = 0;
+        rule.ttis[0].gmt_offset = -std_offset;
+        rule.ttis[0].is_dst = false;
+        rule.ttis[0].abbreviation_list_index = 0;
+    }
+
+    rule.char_count = char_count;
+    for (int index{}; index < std_len; ++index) {
+        rule.chars[index] = std_name[index];
+    }
+
+    rule.chars[std_len++] = '\0';
+    if (dest_len != 0) {
+        for (int index{}; index < dest_len; ++index) {
+            rule.chars[std_len + index] = dest_name[index];
+        }
+        rule.chars[std_len + dest_len] = '\0';
+    }
+
+    return true;
+}
+
+static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFile& vfs_file) {
+    TzifHeader header{};
+    if (vfs_file->ReadObject<TzifHeader>(&header) != sizeof(TzifHeader)) {
+        return {};
+    }
+
+    constexpr s32 time_zone_max_leaps{50};
+    constexpr s32 time_zone_max_chars{50};
+    if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
+          0 < header.type_count && header.type_count < time_zone_rule.ttis.size() &&
+          0 <= header.time_count && header.time_count < time_zone_rule.ats.size() &&
+          0 <= header.char_count && header.char_count < time_zone_max_chars &&
+          (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) &&
+          (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) {
+        return {};
+    }
+    time_zone_rule.time_count = header.time_count;
+    time_zone_rule.type_count = header.type_count;
+    time_zone_rule.char_count = header.char_count;
+
+    int time_count{};
+    u64 read_offset = sizeof(TzifHeader);
+    for (int index{}; index < time_zone_rule.time_count; ++index) {
+        s64_be at{};
+        vfs_file->ReadObject<s64_be>(&at, read_offset);
+        time_zone_rule.types[index] = 1;
+        if (time_count != 0 && at <= time_zone_rule.ats[time_count - 1]) {
+            if (at < time_zone_rule.ats[time_count - 1]) {
+                return {};
+            }
+            time_zone_rule.types[index - 1] = 0;
+            time_count--;
+        }
+        time_zone_rule.ats[time_count++] = at;
+        read_offset += sizeof(s64_be);
+    }
+    time_count = 0;
+    for (int index{}; index < time_zone_rule.time_count; ++index) {
+        const u8 type{*vfs_file->ReadByte(read_offset)};
+        read_offset += sizeof(u8);
+        if (time_zone_rule.time_count <= type) {
+            return {};
+        }
+        if (time_zone_rule.types[index] != 0) {
+            time_zone_rule.types[time_count++] = type;
+        }
+    }
+    time_zone_rule.time_count = time_count;
+    for (int index{}; index < time_zone_rule.type_count; ++index) {
+        TimeTypeInfo& ttis{time_zone_rule.ttis[index]};
+        u32_be gmt_offset{};
+        vfs_file->ReadObject<u32_be>(&gmt_offset, read_offset);
+        read_offset += sizeof(u32_be);
+        ttis.gmt_offset = gmt_offset;
+
+        const u8 dst{*vfs_file->ReadByte(read_offset)};
+        read_offset += sizeof(u8);
+        if (dst >= 2) {
+            return {};
+        }
+        ttis.is_dst = dst != 0;
+
+        const s32 abbreviation_list_index{*vfs_file->ReadByte(read_offset)};
+        read_offset += sizeof(u8);
+        if (abbreviation_list_index >= time_zone_rule.char_count) {
+            return {};
+        }
+        ttis.abbreviation_list_index = abbreviation_list_index;
+    }
+
+    vfs_file->ReadArray(time_zone_rule.chars.data(), time_zone_rule.char_count, read_offset);
+    time_zone_rule.chars[time_zone_rule.char_count] = '\0';
+    read_offset += time_zone_rule.char_count;
+    for (int index{}; index < time_zone_rule.type_count; ++index) {
+        if (header.ttis_std_count == 0) {
+            time_zone_rule.ttis[index].is_standard_time_daylight = false;
+        } else {
+            const u8 dst{*vfs_file->ReadByte(read_offset)};
+            read_offset += sizeof(u8);
+            if (dst >= 2) {
+                return {};
+            }
+            time_zone_rule.ttis[index].is_standard_time_daylight = dst != 0;
+        }
+    }
+
+    for (int index{}; index < time_zone_rule.type_count; ++index) {
+        if (header.ttis_std_count == 0) {
+            time_zone_rule.ttis[index].is_gmt = false;
+        } else {
+            const u8 dst{*vfs_file->ReadByte(read_offset)};
+            read_offset += sizeof(u8);
+            if (dst >= 2) {
+                return {};
+            }
+            time_zone_rule.ttis[index].is_gmt = dst != 0;
+        }
+    }
+
+    const u64 position{(read_offset - sizeof(TzifHeader))};
+    const std::size_t bytes_read{vfs_file->GetSize() - sizeof(TzifHeader) - position};
+    if (bytes_read < 0) {
+        return {};
+    }
+    constexpr s32 time_zone_name_max{255};
+    if (bytes_read > (time_zone_name_max + 1)) {
+        return {};
+    }
+
+    std::array<char, time_zone_name_max + 1> temp_name{};
+    vfs_file->ReadArray(temp_name.data(), bytes_read, read_offset);
+    if (bytes_read > 2 && temp_name[0] == '\n' && temp_name[bytes_read - 1] == '\n' &&
+        time_zone_rule.type_count + 2 <= time_zone_rule.ttis.size()) {
+        temp_name[bytes_read - 1] = '\0';
+
+        std::array<char, time_zone_name_max> name{};
+        std::memcpy(name.data(), temp_name.data() + 1, bytes_read - 1);
+
+        TimeZoneRule temp_rule;
+        if (ParsePosixName(name.data(), temp_rule)) {
+            UNIMPLEMENTED();
+        }
+    }
+    if (time_zone_rule.type_count == 0) {
+        return {};
+    }
+    if (time_zone_rule.time_count > 1) {
+        UNIMPLEMENTED();
+    }
+
+    s32 default_type{};
+
+    for (default_type = 0; default_type < time_zone_rule.time_count; default_type++) {
+        if (time_zone_rule.types[default_type] == 0) {
+            break;
+        }
+    }
+
+    default_type = default_type < time_zone_rule.time_count ? -1 : 0;
+    if (default_type < 0 && time_zone_rule.time_count > 0 &&
+        time_zone_rule.ttis[time_zone_rule.types[0]].is_dst) {
+        default_type = time_zone_rule.types[0];
+        while (--default_type >= 0) {
+            if (!time_zone_rule.ttis[default_type].is_dst) {
+                break;
+            }
+        }
+    }
+    if (default_type < 0) {
+        default_type = 0;
+        while (time_zone_rule.ttis[default_type].is_dst) {
+            if (++default_type >= time_zone_rule.type_count) {
+                default_type = 0;
+                break;
+            }
+        }
+    }
+    time_zone_rule.default_type = default_type;
+    return true;
+}
+
+static ResultCode CreateCalendarTime(s64 time, int gmt_offset, CalendarTimeInternal& calendar_time,
+                                     CalendarAdditionalInfo& calendar_additional_info) {
+    s64 year{epoch_year};
+    s64 time_days{time / seconds_per_day};
+    s64 remaining_seconds{time % seconds_per_day};
+    while (time_days < 0 || time_days >= GetYearLengthInDays(year)) {
+        s64 delta = time_days / days_per_leap_year;
+        if (!delta) {
+            delta = time_days < 0 ? -1 : 1;
+        }
+        s64 new_year{year};
+        if (!SafeAdd(new_year, delta)) {
+            return ERROR_OUT_OF_RANGE;
+        }
+        time_days -= (new_year - year) * days_per_normal_year;
+        time_days -= GetLeapDaysFromYear(new_year - 1) - GetLeapDaysFromYear(year - 1);
+        year = new_year;
+    }
+
+    s64 day_of_year{time_days};
+    remaining_seconds += gmt_offset;
+    while (remaining_seconds < 0) {
+        remaining_seconds += seconds_per_day;
+        day_of_year--;
+    }
+
+    while (remaining_seconds >= seconds_per_day) {
+        remaining_seconds -= seconds_per_day;
+        day_of_year++;
+    }
+
+    while (day_of_year < 0) {
+        if (!SafeAdd(year, -1)) {
+            return ERROR_OUT_OF_RANGE;
+        }
+        day_of_year += GetYearLengthInDays(year);
+    }
+
+    while (day_of_year >= GetYearLengthInDays(year)) {
+        day_of_year -= GetYearLengthInDays(year);
+        if (!SafeAdd(year, 1)) {
+            return ERROR_OUT_OF_RANGE;
+        }
+    }
+
+    calendar_time.year = year;
+    calendar_additional_info.day_of_year = static_cast<u32>(day_of_year);
+    s64 day_of_week{
+        (epoch_week_day +
+         ((year - epoch_year) % days_per_week) * (days_per_normal_year % days_per_week) +
+         GetLeapDaysFromYear(year - 1) - GetLeapDaysFromYear(epoch_year - 1) + day_of_year) %
+        days_per_week};
+    if (day_of_week < 0) {
+        day_of_week += days_per_week;
+    }
+
+    calendar_additional_info.day_of_week = static_cast<u32>(day_of_week);
+    calendar_time.hour = static_cast<s8>((remaining_seconds / seconds_per_hour) % seconds_per_hour);
+    remaining_seconds %= seconds_per_hour;
+    calendar_time.minute = static_cast<s8>(remaining_seconds / seconds_per_minute);
+    calendar_time.second = static_cast<s8>(remaining_seconds % seconds_per_minute);
+
+    for (calendar_time.month = 0;
+         day_of_year >= GetMonthLength(IsLeapYear(year), calendar_time.month);
+         ++calendar_time.month) {
+        day_of_year -= GetMonthLength(IsLeapYear(year), calendar_time.month);
+    }
+
+    calendar_time.day = static_cast<s8>(day_of_year + 1);
+    calendar_additional_info.is_dst = false;
+    calendar_additional_info.gmt_offset = gmt_offset;
+
+    return RESULT_SUCCESS;
+}
+
+static ResultCode ToCalendarTimeInternal(const TimeZoneRule& rules, s64 time,
+                                         CalendarTimeInternal& calendar_time,
+                                         CalendarAdditionalInfo& calendar_additional_info) {
+    if ((rules.go_ahead && time < rules.ats[0]) ||
+        (rules.go_back && time > rules.ats[rules.time_count - 1])) {
+        s64 seconds{};
+        if (time < rules.ats[0]) {
+            seconds = rules.ats[0] - time;
+        } else {
+            seconds = time - rules.ats[rules.time_count - 1];
+        }
+        seconds--;
+
+        const s64 years{(seconds / seconds_per_repeat + 1) * years_per_repeat};
+        seconds = years * average_seconds_per_year;
+
+        s64 new_time{time};
+        if (time < rules.ats[0]) {
+            new_time += seconds;
+        } else {
+            new_time -= seconds;
+        }
+        if (new_time < rules.ats[0] && new_time > rules.ats[rules.time_count - 1]) {
+            return ERROR_TIME_NOT_FOUND;
+        }
+        if (const ResultCode result{
+                ToCalendarTimeInternal(rules, new_time, calendar_time, calendar_additional_info)};
+            result != RESULT_SUCCESS) {
+            return result;
+        }
+        if (time < rules.ats[0]) {
+            calendar_time.year -= years;
+        } else {
+            calendar_time.year += years;
+        }
+
+        return RESULT_SUCCESS;
+    }
+
+    s32 tti_index{};
+    if (rules.time_count == 0 || time < rules.ats[0]) {
+        tti_index = rules.default_type;
+    } else {
+        s32 low{1};
+        s32 high{rules.time_count};
+        while (low < high) {
+            s32 mid{(low + high) >> 1};
+            if (time < rules.ats[mid]) {
+                high = mid;
+            } else {
+                low = mid + 1;
+            }
+        }
+        tti_index = rules.types[low - 1];
+    }
+
+    if (const ResultCode result{CreateCalendarTime(time, rules.ttis[tti_index].gmt_offset,
+                                                   calendar_time, calendar_additional_info)};
+        result != RESULT_SUCCESS) {
+        return result;
+    }
+
+    calendar_additional_info.is_dst = rules.ttis[tti_index].is_dst;
+    const char* time_zone{&rules.chars[rules.ttis[tti_index].abbreviation_list_index]};
+    for (int index{}; time_zone[index] != '\0'; ++index) {
+        calendar_additional_info.timezone_name[index] = time_zone[index];
+    }
+    return RESULT_SUCCESS;
+}
+
+static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) {
+    CalendarTimeInternal calendar_time{};
+    const ResultCode result{
+        ToCalendarTimeInternal(rules, time, calendar_time, calendar.additiona_info)};
+    calendar.time.year = static_cast<s16>(calendar_time.year);
+    calendar.time.month = calendar_time.month;
+    calendar.time.day = calendar_time.day;
+    calendar.time.hour = calendar_time.hour;
+    calendar.time.minute = calendar_time.minute;
+    calendar.time.second = calendar_time.second;
+    return result;
+}
+
+TimeZoneManager::TimeZoneManager() = default;
+TimeZoneManager::~TimeZoneManager() = default;
+
+ResultCode TimeZoneManager::ToCalendarTime(const TimeZoneRule& rules, s64 time,
+                                           CalendarInfo& calendar) const {
+    return ToCalendarTimeImpl(rules, time, calendar);
+}
+
+ResultCode TimeZoneManager::SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
+                                                                  FileSys::VirtualFile& vfs_file) {
+    TimeZoneRule rule{};
+    if (ParseTimeZoneBinary(rule, vfs_file)) {
+        device_location_name = location_name;
+        time_zone_rule = rule;
+        return RESULT_SUCCESS;
+    }
+    return ERROR_TIME_ZONE_CONVERSION_FAILED;
+}
+
+ResultCode TimeZoneManager::SetUpdatedTime(const Clock::SteadyClockTimePoint& value) {
+    time_zone_update_time_point = value;
+    return RESULT_SUCCESS;
+}
+
+ResultCode TimeZoneManager::ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const {
+    if (is_initialized) {
+        return ToCalendarTime(time_zone_rule, time, calendar);
+    } else {
+        return ERROR_UNINITIALIZED_CLOCK;
+    }
+}
+
+ResultCode TimeZoneManager::ParseTimeZoneRuleBinary(TimeZoneRule& rules,
+                                                    FileSys::VirtualFile& vfs_file) const {
+    if (!ParseTimeZoneBinary(rules, vfs_file)) {
+        return ERROR_TIME_ZONE_CONVERSION_FAILED;
+    }
+    return RESULT_SUCCESS;
+}
+
+ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
+                                        const CalendarTime& calendar_time, s64& posix_time) const {
+    posix_time = 0;
+
+    CalendarTimeInternal internal_time{};
+    internal_time.year = calendar_time.year;
+    internal_time.month = calendar_time.month;
+    internal_time.day = calendar_time.day;
+    internal_time.hour = calendar_time.hour;
+    internal_time.minute = calendar_time.minute;
+    internal_time.second = calendar_time.second;
+
+    s32 hour{internal_time.hour};
+    s32 minute{internal_time.minute};
+    if (!SafeNormalize(hour, minute, minutes_per_hour)) {
+        return ERROR_OVERFLOW;
+    }
+    internal_time.minute = static_cast<s8>(minute);
+
+    s32 day{internal_time.day};
+    if (!SafeNormalize(day, hour, hours_per_day)) {
+        return ERROR_OVERFLOW;
+    }
+    internal_time.day = static_cast<s8>(day);
+    internal_time.hour = static_cast<s8>(hour);
+
+    s64 year{internal_time.year};
+    s64 month{internal_time.month};
+    if (!SafeNormalize(year, month, months_per_year)) {
+        return ERROR_OVERFLOW;
+    }
+    internal_time.month = static_cast<s8>(month);
+
+    if (!SafeAdd(year, year_base)) {
+        return ERROR_OVERFLOW;
+    }
+
+    while (day <= 0) {
+        if (!SafeAdd(year, -1)) {
+            return ERROR_OVERFLOW;
+        }
+        s64 temp_year{year};
+        if (1 < internal_time.month) {
+            ++temp_year;
+        }
+        day += static_cast<s32>(GetYearLengthInDays(temp_year));
+    }
+
+    while (day > days_per_leap_year) {
+        s64 temp_year{year};
+        if (1 < internal_time.month) {
+            temp_year++;
+        }
+        day -= static_cast<s32>(GetYearLengthInDays(temp_year));
+        if (!SafeAdd(year, 1)) {
+            return ERROR_OVERFLOW;
+        }
+    }
+
+    while (true) {
+        const s32 month_length{GetMonthLength(IsLeapYear(year), internal_time.month)};
+        if (day <= month_length) {
+            break;
+        }
+        day -= month_length;
+        internal_time.month++;
+        if (internal_time.month >= months_per_year) {
+            internal_time.month = 0;
+            if (!SafeAdd(year, 1)) {
+                return ERROR_OVERFLOW;
+            }
+        }
+    }
+    internal_time.day = static_cast<s8>(day);
+
+    if (!SafeAdd(year, -year_base)) {
+        return ERROR_OVERFLOW;
+    }
+    internal_time.year = year;
+
+    s32 saved_seconds{};
+    if (internal_time.second >= 0 && internal_time.second < seconds_per_minute) {
+        saved_seconds = 0;
+    } else if (year + year_base < epoch_year) {
+        s32 second{internal_time.second};
+        if (!SafeAdd(second, 1 - seconds_per_minute)) {
+            return ERROR_OVERFLOW;
+        }
+        saved_seconds = second;
+        internal_time.second = 1 - seconds_per_minute;
+    } else {
+        saved_seconds = internal_time.second;
+        internal_time.second = 0;
+    }
+
+    s64 low{LLONG_MIN};
+    s64 high{LLONG_MAX};
+    while (true) {
+        s64 pivot{low / 2 + high / 2};
+        if (pivot < low) {
+            pivot = low;
+        } else if (pivot > high) {
+            pivot = high;
+        }
+        s32 direction{};
+        CalendarTimeInternal candidate_calendar_time{};
+        CalendarAdditionalInfo unused{};
+        if (ToCalendarTimeInternal(rules, pivot, candidate_calendar_time, unused) !=
+            RESULT_SUCCESS) {
+            if (pivot > 0) {
+                direction = 1;
+            } else {
+                direction = -1;
+            }
+        } else {
+            direction = candidate_calendar_time.Compare(internal_time);
+        }
+        if (!direction) {
+            const s64 time_result{pivot + saved_seconds};
+            if ((time_result < pivot) != (saved_seconds < 0)) {
+                return ERROR_OVERFLOW;
+            }
+            posix_time = time_result;
+            break;
+        } else {
+            if (pivot == low) {
+                if (pivot == LLONG_MAX) {
+                    return ERROR_TIME_NOT_FOUND;
+                }
+                pivot++;
+                low++;
+            } else if (pivot == high) {
+                if (pivot == LLONG_MIN) {
+                    return ERROR_TIME_NOT_FOUND;
+                }
+                pivot--;
+                high--;
+            }
+            if (low > high) {
+                return ERROR_TIME_NOT_FOUND;
+            }
+            if (direction > 0) {
+                high = pivot;
+            } else {
+                low = pivot;
+            }
+        }
+    }
+    return RESULT_SUCCESS;
+}
+
+ResultCode TimeZoneManager::GetDeviceLocationName(LocationName& value) const {
+    if (!is_initialized) {
+        return ERROR_UNINITIALIZED_CLOCK;
+    }
+    std::memcpy(value.data(), device_location_name.c_str(), device_location_name.size());
+    return RESULT_SUCCESS;
+}
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_manager.h b/src/core/hle/service/time/time_zone_manager.h
new file mode 100644
index 000000000..7c6f975ae
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_manager.h
@@ -0,0 +1,53 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+
+#include "common/common_types.h"
+#include "core/file_sys/vfs_types.h"
+#include "core/hle/service/time/clock_types.h"
+#include "core/hle/service/time/time_zone_types.h"
+
+namespace Service::Time::TimeZone {
+
+class TimeZoneManager final {
+public:
+    TimeZoneManager();
+    ~TimeZoneManager();
+
+    void SetTotalLocationNameCount(std::size_t value) {
+        total_location_name_count = value;
+    }
+
+    void SetTimeZoneRuleVersion(const u128& value) {
+        time_zone_rule_version = value;
+    }
+
+    void MarkAsInitialized() {
+        is_initialized = true;
+    }
+
+    ResultCode SetDeviceLocationNameWithTimeZoneRule(const std::string& location_name,
+                                                     FileSys::VirtualFile& vfs_file);
+    ResultCode SetUpdatedTime(const Clock::SteadyClockTimePoint& value);
+    ResultCode GetDeviceLocationName(TimeZone::LocationName& value) const;
+    ResultCode ToCalendarTime(const TimeZoneRule& rules, s64 time, CalendarInfo& calendar) const;
+    ResultCode ToCalendarTimeWithMyRules(s64 time, CalendarInfo& calendar) const;
+    ResultCode ParseTimeZoneRuleBinary(TimeZoneRule& rules, FileSys::VirtualFile& vfs_file) const;
+    ResultCode ToPosixTime(const TimeZoneRule& rules, const CalendarTime& calendar_time,
+                           s64& posix_time) const;
+
+private:
+    bool is_initialized{};
+    TimeZoneRule time_zone_rule{};
+    std::string device_location_name{"GMT"};
+    u128 time_zone_rule_version{};
+    std::size_t total_location_name_count{};
+    Clock::SteadyClockTimePoint time_zone_update_time_point{
+        Clock::SteadyClockTimePoint::GetRandom()};
+};
+
+} // namespace Service::Time::TimeZone
diff --git a/src/core/hle/service/time/time_zone_service.cpp b/src/core/hle/service/time/time_zone_service.cpp
new file mode 100644
index 000000000..1566e778e
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_service.cpp
@@ -0,0 +1,148 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/time/time_zone_content_manager.h"
+#include "core/hle/service/time/time_zone_service.h"
+#include "core/hle/service/time/time_zone_types.h"
+
+namespace Service::Time {
+
+ITimeZoneService ::ITimeZoneService(TimeZone::TimeZoneContentManager& time_zone_content_manager)
+    : ServiceFramework("ITimeZoneService"), time_zone_content_manager{time_zone_content_manager} {
+    static const FunctionInfo functions[] = {
+        {0, &ITimeZoneService::GetDeviceLocationName, "GetDeviceLocationName"},
+        {1, nullptr, "SetDeviceLocationName"},
+        {2, nullptr, "GetTotalLocationNameCount"},
+        {3, nullptr, "LoadLocationNameList"},
+        {4, &ITimeZoneService::LoadTimeZoneRule, "LoadTimeZoneRule"},
+        {5, nullptr, "GetTimeZoneRuleVersion"},
+        {100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
+        {101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
+        {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
+        {202, nullptr, "ToPosixTimeWithMyRule"},
+    };
+    RegisterHandlers(functions);
+}
+
+void ITimeZoneService::GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
+    LOG_DEBUG(Service_Time, "called");
+
+    TimeZone::LocationName location_name{};
+    if (const ResultCode result{
+            time_zone_content_manager.GetTimeZoneManager().GetDeviceLocationName(location_name)};
+        result != RESULT_SUCCESS) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+        return;
+    }
+
+    IPC::ResponseBuilder rb{ctx, (sizeof(location_name) / 4) + 2};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushRaw(location_name);
+}
+
+void ITimeZoneService::LoadTimeZoneRule(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    const auto raw_location_name{rp.PopRaw<std::array<u8, 0x24>>()};
+
+    std::string location_name;
+    for (const auto& byte : raw_location_name) {
+        // Strip extra bytes
+        if (byte == '\0') {
+            break;
+        }
+        location_name.push_back(byte);
+    }
+
+    LOG_DEBUG(Service_Time, "called, location_name={}", location_name);
+
+    TimeZone::TimeZoneRule time_zone_rule{};
+    if (const ResultCode result{
+            time_zone_content_manager.LoadTimeZoneRule(time_zone_rule, location_name)};
+        result != RESULT_SUCCESS) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+        return;
+    }
+
+    std::vector<u8> time_zone_rule_outbuffer(sizeof(TimeZone::TimeZoneRule));
+    std::memcpy(time_zone_rule_outbuffer.data(), &time_zone_rule, sizeof(TimeZone::TimeZoneRule));
+    ctx.WriteBuffer(time_zone_rule_outbuffer);
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(RESULT_SUCCESS);
+}
+
+void ITimeZoneService::ToCalendarTime(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    const auto posix_time{rp.Pop<s64>()};
+
+    LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
+
+    TimeZone::TimeZoneRule time_zone_rule{};
+    const auto buffer{ctx.ReadBuffer()};
+    std::memcpy(&time_zone_rule, buffer.data(), buffer.size());
+
+    TimeZone::CalendarInfo calendar_info{};
+    if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToCalendarTime(
+            time_zone_rule, posix_time, calendar_info)};
+        result != RESULT_SUCCESS) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+        return;
+    }
+
+    IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushRaw(calendar_info);
+}
+
+void ITimeZoneService::ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    const auto posix_time{rp.Pop<s64>()};
+
+    LOG_DEBUG(Service_Time, "called, posix_time=0x{:016X}", posix_time);
+
+    TimeZone::CalendarInfo calendar_info{};
+    if (const ResultCode result{
+            time_zone_content_manager.GetTimeZoneManager().ToCalendarTimeWithMyRules(
+                posix_time, calendar_info)};
+        result != RESULT_SUCCESS) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+        return;
+    }
+
+    IPC::ResponseBuilder rb{ctx, 2 + (sizeof(TimeZone::CalendarInfo) / 4)};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushRaw(calendar_info);
+}
+
+void ITimeZoneService::ToPosixTime(Kernel::HLERequestContext& ctx) {
+    LOG_DEBUG(Service_Time, "called");
+
+    IPC::RequestParser rp{ctx};
+    const auto calendar_time{rp.PopRaw<TimeZone::CalendarTime>()};
+    TimeZone::TimeZoneRule time_zone_rule{};
+    std::memcpy(&time_zone_rule, ctx.ReadBuffer().data(), sizeof(TimeZone::TimeZoneRule));
+
+    s64 posix_time{};
+    if (const ResultCode result{time_zone_content_manager.GetTimeZoneManager().ToPosixTime(
+            time_zone_rule, calendar_time, posix_time)};
+        result != RESULT_SUCCESS) {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(result);
+        return;
+    }
+
+    // TODO(bunnei): Handle multiple times
+    IPC::ResponseBuilder rb{ctx, 3};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushRaw<u32>(1); // Number of times we're returning
+    ctx.WriteBuffer(&posix_time, sizeof(s64));
+}
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_service.h b/src/core/hle/service/time/time_zone_service.h
new file mode 100644
index 000000000..a92b4312b
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_service.h
@@ -0,0 +1,30 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Service::Time {
+
+namespace TimeZone {
+class TimeZoneContentManager;
+}
+
+class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
+public:
+    explicit ITimeZoneService(TimeZone::TimeZoneContentManager& time_zone_manager);
+
+private:
+    void GetDeviceLocationName(Kernel::HLERequestContext& ctx);
+    void LoadTimeZoneRule(Kernel::HLERequestContext& ctx);
+    void ToCalendarTime(Kernel::HLERequestContext& ctx);
+    void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx);
+    void ToPosixTime(Kernel::HLERequestContext& ctx);
+
+private:
+    TimeZone::TimeZoneContentManager& time_zone_content_manager;
+};
+
+} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_types.h b/src/core/hle/service/time/time_zone_types.h
new file mode 100644
index 000000000..9be15b53e
--- /dev/null
+++ b/src/core/hle/service/time/time_zone_types.h
@@ -0,0 +1,87 @@
+// Copyright 2019 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace Service::Time::TimeZone {
+
+using LocationName = std::array<char, 0x24>;
+
+/// https://switchbrew.org/wiki/Glue_services#ttinfo
+struct TimeTypeInfo {
+    s32 gmt_offset{};
+    u8 is_dst{};
+    INSERT_PADDING_BYTES(3);
+    s32 abbreviation_list_index{};
+    u8 is_standard_time_daylight{};
+    u8 is_gmt{};
+    INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(TimeTypeInfo) == 0x10, "TimeTypeInfo is incorrect size");
+
+/// https://switchbrew.org/wiki/Glue_services#TimeZoneRule
+struct TimeZoneRule {
+    s32 time_count{};
+    s32 type_count{};
+    s32 char_count{};
+    u8 go_back{};
+    u8 go_ahead{};
+    INSERT_PADDING_BYTES(2);
+    std::array<s64, 1000> ats{};
+    std::array<s8, 1000> types{};
+    std::array<TimeTypeInfo, 128> ttis{};
+    std::array<char, 512> chars{};
+    s32 default_type{};
+    INSERT_PADDING_BYTES(0x12C4);
+};
+static_assert(sizeof(TimeZoneRule) == 0x4000, "TimeZoneRule is incorrect size");
+
+/// https://switchbrew.org/wiki/Glue_services#CalendarAdditionalInfo
+struct CalendarAdditionalInfo {
+    u32 day_of_week{};
+    u32 day_of_year{};
+    std::array<char, 8> timezone_name;
+    u32 is_dst{};
+    s32 gmt_offset{};
+};
+static_assert(sizeof(CalendarAdditionalInfo) == 0x18, "CalendarAdditionalInfo is incorrect size");
+
+/// https://switchbrew.org/wiki/Glue_services#CalendarTime
+struct CalendarTime {
+    s16 year{};
+    s8 month{};
+    s8 day{};
+    s8 hour{};
+    s8 minute{};
+    s8 second{};
+    INSERT_PADDING_BYTES(1);
+};
+static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime is incorrect size");
+
+struct CalendarInfo {
+    CalendarTime time{};
+    CalendarAdditionalInfo additiona_info{};
+};
+static_assert(sizeof(CalendarInfo) == 0x20, "CalendarInfo is incorrect size");
+
+struct TzifHeader {
+    u32_be magic{};
+    u8 version{};
+    INSERT_PADDING_BYTES(15);
+    s32_be ttis_gmt_count{};
+    s32_be ttis_std_count{};
+    s32_be leap_count{};
+    s32_be time_count{};
+    s32_be type_count{};
+    s32_be char_count{};
+};
+static_assert(sizeof(TzifHeader) == 0x2C, "TzifHeader is incorrect size");
+
+} // namespace Service::Time::TimeZone