diff --git a/README.md b/README.md
index 426ee40a..be3d1fea 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
    
  - **System Titles**  
  
-   Some of our System Modules implementation require [System Data Archives](https://switchbrew.org/wiki/Title_list#System_Data_Archives).  
+   Some of our System Modules implementation (like time) require [System Data Archives](https://switchbrew.org/wiki/Title_list#System_Data_Archives).  
    You can install them by mounting your nand partition using [HacDiskMount](https://switchtools.sshnuke.net/) and copy the content in `RyuFs/nand/system`.
    
  - **Executables**
diff --git a/Ryujinx.HLE/DeviceMemory.cs b/Ryujinx.HLE/DeviceMemory.cs
index 0ead1747..38864bc2 100644
--- a/Ryujinx.HLE/DeviceMemory.cs
+++ b/Ryujinx.HLE/DeviceMemory.cs
@@ -59,6 +59,11 @@ namespace Ryujinx.HLE
             return *((ulong*)(_ramPtr + position));
         }
 
+        public unsafe T ReadStruct<T>(long position)
+        {
+            return Marshal.PtrToStructure<T>((IntPtr)(_ramPtr + position));
+        }
+
         public void WriteSByte(long position, sbyte value)
         {
             WriteByte(position, (byte)value);
diff --git a/Ryujinx.HLE/Exceptions/InternalServiceException.cs b/Ryujinx.HLE/Exceptions/InternalServiceException.cs
new file mode 100644
index 00000000..b940c51c
--- /dev/null
+++ b/Ryujinx.HLE/Exceptions/InternalServiceException.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Ryujinx.HLE.Exceptions
+{
+    class InternalServiceException: Exception
+    {
+        public InternalServiceException(string message) : base(message) { }
+    }
+}
diff --git a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs
index fe6642c3..9ed2e142 100644
--- a/Ryujinx.HLE/FileSystem/Content/ContentManager.cs
+++ b/Ryujinx.HLE/FileSystem/Content/ContentManager.cs
@@ -1,6 +1,6 @@
 using LibHac.Fs;
 using LibHac.Fs.NcaUtils;
-using Ryujinx.HLE.HOS.Services.Time.TimeZone;
+using Ryujinx.HLE.HOS.Services.Time;
 using Ryujinx.HLE.Utilities;
 using System;
 using System.Collections.Generic;
@@ -143,7 +143,7 @@ namespace Ryujinx.HLE.FileSystem.Content
                 }
             }
 
-            TimeZoneManager.Instance.Initialize(_device);
+            TimeManager.Instance.InitializeTimeZone(_device);
         }
 
         public void ClearEntry(long titleId, ContentType contentType, StorageId storageId)
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 86b28320..80c9ef0c 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -8,12 +8,14 @@ using Ryujinx.HLE.HOS.Kernel.Common;
 using Ryujinx.HLE.HOS.Kernel.Memory;
 using Ryujinx.HLE.HOS.Kernel.Process;
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
 using Ryujinx.HLE.HOS.Services.Settings;
 using Ryujinx.HLE.HOS.Services.Sm;
 using Ryujinx.HLE.HOS.Services.Time.Clock;
 using Ryujinx.HLE.HOS.SystemState;
 using Ryujinx.HLE.Loaders.Executables;
 using Ryujinx.HLE.Loaders.Npdm;
+using Ryujinx.HLE.Utilities;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
@@ -22,7 +24,8 @@ using System.Linq;
 using System.Reflection;
 using System.Threading;
 
-using NxStaticObject = Ryujinx.HLE.Loaders.Executables.NxStaticObject;
+using TimeServiceManager = Ryujinx.HLE.HOS.Services.Time.TimeManager;
+using NxStaticObject     = Ryujinx.HLE.Loaders.Executables.NxStaticObject;
 
 namespace Ryujinx.HLE.HOS
 {
@@ -87,8 +90,6 @@ namespace Ryujinx.HLE.HOS
         internal KSharedMemory HidSharedMem  { get; private set; }
         internal KSharedMemory FontSharedMem { get; private set; }
         internal KSharedMemory IirsSharedMem { get; private set; }
-        internal KSharedMemory TimeSharedMem { get; private set; }
-
         internal SharedFontManager Font { get; private set; }
 
         internal ContentManager ContentManager { get; private set; }
@@ -184,7 +185,10 @@ namespace Ryujinx.HLE.HOS
             HidSharedMem  = new KSharedMemory(this, hidPageList,  0, 0, MemoryPermission.Read);
             FontSharedMem = new KSharedMemory(this, fontPageList, 0, 0, MemoryPermission.Read);
             IirsSharedMem = new KSharedMemory(this, iirsPageList, 0, 0, MemoryPermission.Read);
-            TimeSharedMem = new KSharedMemory(this, timePageList, 0, 0, MemoryPermission.Read);
+
+            KSharedMemory timeSharedMemory = new KSharedMemory(this, timePageList, 0, 0, MemoryPermission.Read);
+
+            TimeServiceManager.Instance.Initialize(device, this, timeSharedMemory, (long)(timePa - DramMemoryMap.DramBase), TimeSize);
 
             AppletState = new AppletStateMgr(this);
 
@@ -200,17 +204,30 @@ namespace Ryujinx.HLE.HOS
 
             ContentManager = new ContentManager(device);
 
-            // TODO: use set:sys (and set external clock source id from settings)
+            // TODO: use set:sys (and get external clock source id from settings)
             // TODO: use "time!standard_steady_clock_rtc_update_interval_minutes" and implement a worker thread to be accurate.
-            StandardSteadyClockCore.Instance.ConfigureSetupValue();
+            UInt128 clockSourceId = new UInt128(Guid.NewGuid().ToByteArray());
+            IRtcManager.GetExternalRtcValue(out ulong rtcValue);
+
+            // We assume the rtc is system time.
+            TimeSpanType systemTime = TimeSpanType.FromSeconds((long)rtcValue);
+
+            // First init the standard steady clock
+            TimeServiceManager.Instance.SetupStandardSteadyClock(null, clockSourceId, systemTime, TimeSpanType.Zero, TimeSpanType.Zero, false);
+            TimeServiceManager.Instance.SetupStandardLocalSystemClock(null, new SystemClockContext(), systemTime.ToSeconds());
 
             if (NxSettings.Settings.TryGetValue("time!standard_network_clock_sufficient_accuracy_minutes", out object standardNetworkClockSufficientAccuracyMinutes))
             {
                 TimeSpanType standardNetworkClockSufficientAccuracy = new TimeSpanType((int)standardNetworkClockSufficientAccuracyMinutes * 60000000000);
 
-                StandardNetworkSystemClockCore.Instance.SetStandardNetworkClockSufficientAccuracy(standardNetworkClockSufficientAccuracy);
+                TimeServiceManager.Instance.SetupStandardNetworkSystemClock(new SystemClockContext(), standardNetworkClockSufficientAccuracy);
             }
 
+            TimeServiceManager.Instance.SetupStandardUserSystemClock(null, false, SteadyClockTimePoint.GetRandom());
+
+            // FIXME: TimeZone shoud be init here but it's actually done in ContentManager
+
+            TimeServiceManager.Instance.SetupEphemeralNetworkSystemClock();
         }
 
         public void LoadCart(string exeFsDir, string romFsFile = null)
diff --git a/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs b/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs
index b679005e..ca3853e8 100644
--- a/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs
+++ b/Ryujinx.HLE/HOS/Services/Settings/NxSettings.cs
@@ -1704,6 +1704,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
 			{ "time!standard_steady_clock_test_offset_minutes", 0 },
 			{ "time!standard_steady_clock_rtc_update_interval_minutes", 5 },
 			{ "time!standard_network_clock_sufficient_accuracy_minutes", 43200 },
+			{ "time!standard_user_clock_initial_year", 2019 },
 			{ "usb!usb30_force_enabled", false },
 			{ "wlan_debug!skip_wlan_boot", false }
 		};
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockContextWriter.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockContextWriter.cs
new file mode 100644
index 00000000..14d3cb24
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockContextWriter.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Services.Time.Clock
+{
+    class EphemeralNetworkSystemClockContextWriter : SystemClockContextUpdateCallback
+    {
+        protected override ResultCode Update()
+        {
+            return ResultCode.Success;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs
index 8e9073a4..003863e4 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/EphemeralNetworkSystemClockCore.cs
@@ -2,26 +2,6 @@
 {
     class EphemeralNetworkSystemClockCore : SystemClockCore
     {
-        private static EphemeralNetworkSystemClockCore _instance;
-
-        public static EphemeralNetworkSystemClockCore Instance
-        {
-            get
-            {
-                if (_instance == null)
-                {
-                    _instance = new EphemeralNetworkSystemClockCore(TickBasedSteadyClockCore.Instance);
-                }
-
-                return _instance;
-            }
-        }
-
         public EphemeralNetworkSystemClockCore(SteadyClockCore steadyClockCore) : base(steadyClockCore) { }
-
-        public override ResultCode Flush(SystemClockContext context)
-        {
-            return ResultCode.Success;
-        }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs
new file mode 100644
index 00000000..fb7ebdc5
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/LocalSystemClockContextWriter.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.HLE.HOS.Services.Time.Clock
+{
+    class LocalSystemClockContextWriter : SystemClockContextUpdateCallback
+    {
+        private TimeSharedMemory _sharedMemory;
+
+        public LocalSystemClockContextWriter(TimeSharedMemory sharedMemory)
+        {
+            _sharedMemory = sharedMemory;
+        }
+
+        protected override ResultCode Update()
+        {
+            _sharedMemory.UpdateLocalSystemClockContext(_context);
+
+            return ResultCode.Success;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs
new file mode 100644
index 00000000..36468ec1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/NetworkSystemClockContextWriter.cs
@@ -0,0 +1,19 @@
+namespace Ryujinx.HLE.HOS.Services.Time.Clock
+{
+    class NetworkSystemClockContextWriter : SystemClockContextUpdateCallback
+    {
+        private TimeSharedMemory _sharedMemory;
+
+        public NetworkSystemClockContextWriter(TimeSharedMemory sharedMemory)
+        {
+            _sharedMemory = sharedMemory;
+        }
+
+        protected override ResultCode Update()
+        {
+            _sharedMemory.UpdateNetworkSystemClockContext(_context);
+
+            return ResultCode.Success;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs
index a1727976..20c334e8 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardLocalSystemClockCore.cs
@@ -2,28 +2,6 @@
 {
     class StandardLocalSystemClockCore : SystemClockCore
     {
-        private static StandardLocalSystemClockCore _instance;
-
-        public static StandardLocalSystemClockCore Instance
-        {
-            get
-            {
-                if (_instance == null)
-                {
-                    _instance = new StandardLocalSystemClockCore(StandardSteadyClockCore.Instance);
-                }
-
-                return _instance;
-            }
-        }
-
         public StandardLocalSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore) {}
-
-        public override ResultCode Flush(SystemClockContext context)
-        {
-            // TODO: set:sys SetUserSystemClockContext
-
-            return ResultCode.Success;
-        }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs
index cc21dd9a..b86f703d 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardNetworkSystemClockCore.cs
@@ -6,33 +6,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
     {
         private TimeSpanType _standardNetworkClockSufficientAccuracy;
 
-        private static StandardNetworkSystemClockCore _instance;
-
-        public static StandardNetworkSystemClockCore Instance
-        {
-            get
-            {
-                if (_instance == null)
-                {
-                    _instance = new StandardNetworkSystemClockCore(StandardSteadyClockCore.Instance);
-                }
-
-                return _instance;
-            }
-        }
-
         public StandardNetworkSystemClockCore(StandardSteadyClockCore steadyClockCore) : base(steadyClockCore)
         {
             _standardNetworkClockSufficientAccuracy = new TimeSpanType(0);
         }
 
-        public override ResultCode Flush(SystemClockContext context)
-        {
-            // TODO: set:sys SetNetworkSystemClockContext
-
-            return ResultCode.Success;
-        }
-
         public bool IsStandardNetworkSystemClockAccuracySufficient(KThread thread)
         {
             SteadyClockCore      steadyClockCore  = GetSteadyClockCore();
@@ -40,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
 
             bool isStandardNetworkClockSufficientAccuracy = false;
 
-            ResultCode result = GetSystemClockContext(thread, out SystemClockContext context);
+            ResultCode result = GetClockContext(thread, out SystemClockContext context);
 
             if (result == ResultCode.Success && context.SteadyTimePoint.GetSpanBetween(currentTimePoint, out long outSpan) == ResultCode.Success)
             {
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs
index 1bc5bee7..370e7d73 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardSteadyClockCore.cs
@@ -1,49 +1,30 @@
 using Ryujinx.HLE.HOS.Kernel.Threading;
-using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
 
 namespace Ryujinx.HLE.HOS.Services.Time.Clock
 {
     class StandardSteadyClockCore : SteadyClockCore
     {
-        private long         _setupValue;
-        private ResultCode   _setupResultCode;
-        private bool         _isRtcResetDetected;
+        private TimeSpanType _setupValue;
         private TimeSpanType _testOffset;
         private TimeSpanType _internalOffset;
+        private TimeSpanType _cachedRawTimePoint;
 
-        private static StandardSteadyClockCore _instance;
-
-        public static StandardSteadyClockCore Instance
+        public StandardSteadyClockCore()
         {
-            get
-            {
-                if (_instance == null)
-                {
-                    _instance = new StandardSteadyClockCore();
-                }
-
-                return _instance;
-            }
-        }
-
-        private StandardSteadyClockCore()
-        {
-            _testOffset     = new TimeSpanType(0);
-            _internalOffset = new TimeSpanType(0);
+            _setupValue         = TimeSpanType.Zero;
+            _testOffset         = TimeSpanType.Zero;
+            _internalOffset     = TimeSpanType.Zero;
+            _cachedRawTimePoint = TimeSpanType.Zero;
         }
 
         public override SteadyClockTimePoint GetTimePoint(KThread thread)
         {
             SteadyClockTimePoint result = new SteadyClockTimePoint
             {
-                TimePoint     = 0,
+                TimePoint     = GetCurrentRawTimePoint(thread).ToSeconds(),
                 ClockSourceId = GetClockSourceId()
             };
 
-            TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
-
-            result.TimePoint = _setupValue + ticksTimeSpan.ToSeconds();
-
             return result;
         }
 
@@ -57,16 +38,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
             _testOffset = testOffset;
         }
 
-        public override ResultCode GetRtcValue(out ulong rtcValue)
-        {
-            return (ResultCode)IRtcManager.GetExternalRtcValue(out rtcValue);
-        }
-
-        public bool IsRtcResetDetected()
-        {
-            return _isRtcResetDetected;
-        }
-
         public override TimeSpanType GetInternalOffset()
         {
             return _internalOffset;
@@ -77,31 +48,35 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
             _internalOffset = internalOffset;
         }
 
-        public override ResultCode GetSetupResultValue()
+        public override TimeSpanType GetCurrentRawTimePoint(KThread thread)
         {
-            return _setupResultCode;
-        }
+            TimeSpanType ticksTimeSpan;
 
-        public void ConfigureSetupValue()
-        {
-            int retry = 0;
-
-            ResultCode result = ResultCode.Success;
-
-            while (retry < 20)
+            // As this may be called before the guest code, we support passing a null thread to make this api usable.
+            if (thread == null)
             {
-                result = (ResultCode)IRtcManager.GetExternalRtcValue(out ulong rtcValue);
-
-                if (result == ResultCode.Success)
-                {
-                    _setupValue = (long)rtcValue;
-                    break;
-                }
-
-                retry++;
+                ticksTimeSpan = TimeSpanType.FromSeconds(0);
+            }
+            else
+            {
+                ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
             }
 
-            _setupResultCode = result;
+            TimeSpanType rawTimePoint = new TimeSpanType(_setupValue.NanoSeconds + ticksTimeSpan.NanoSeconds);
+
+            if (rawTimePoint.NanoSeconds < _cachedRawTimePoint.NanoSeconds)
+            {
+                rawTimePoint.NanoSeconds = _cachedRawTimePoint.NanoSeconds;
+            }
+
+            _cachedRawTimePoint = rawTimePoint;
+
+            return rawTimePoint;
+        }
+
+        public void SetSetupValue(TimeSpanType setupValue)
+        {
+            _setupValue = setupValue;
         }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs
index c98b0064..42bc05fa 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/StandardUserSystemClockCore.cs
@@ -1,4 +1,5 @@
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
 
 namespace Ryujinx.HLE.HOS.Services.Time.Clock
 {
@@ -7,35 +8,25 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
         private StandardLocalSystemClockCore   _localSystemClockCore;
         private StandardNetworkSystemClockCore _networkSystemClockCore;
         private bool                           _autoCorrectionEnabled;
-
-        private static StandardUserSystemClockCore _instance;
-
-        public static StandardUserSystemClockCore Instance
-        {
-            get
-            {
-                if (_instance == null)
-                {
-                    _instance = new StandardUserSystemClockCore(StandardLocalSystemClockCore.Instance, StandardNetworkSystemClockCore.Instance);
-                }
-
-                return _instance;
-            }
-        }
+        private SteadyClockTimePoint           _autoCorrectionTime;
+        private KEvent                         _autoCorrectionEvent;
 
         public StandardUserSystemClockCore(StandardLocalSystemClockCore localSystemClockCore, StandardNetworkSystemClockCore networkSystemClockCore) : base(localSystemClockCore.GetSteadyClockCore())
         {
             _localSystemClockCore   = localSystemClockCore;
             _networkSystemClockCore = networkSystemClockCore;
             _autoCorrectionEnabled  = false;
+            _autoCorrectionTime     = SteadyClockTimePoint.GetRandom();
+            _autoCorrectionEvent    = null;
         }
 
-        public override ResultCode Flush(SystemClockContext context)
+        protected override ResultCode Flush(SystemClockContext context)
         {
-            return ResultCode.NotImplemented;
+            // As UserSystemClock isn't a real system clock, this shouldn't happens.
+            throw new NotImplementedException();
         }
 
-        public override ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
+        public override ResultCode GetClockContext(KThread thread, out SystemClockContext context)
         {
             ResultCode result = ApplyAutomaticCorrection(thread, false);
 
@@ -43,13 +34,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
 
             if (result == ResultCode.Success)
             {
-                return _localSystemClockCore.GetSystemClockContext(thread, out context);
+                return _localSystemClockCore.GetClockContext(thread, out context);
             }
 
             return result;
         }
 
-        public override ResultCode SetSystemClockContext(SystemClockContext context)
+        public override ResultCode SetClockContext(SystemClockContext context)
         {
             return ResultCode.NotImplemented;
         }
@@ -60,17 +51,22 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
 
             if (_autoCorrectionEnabled != autoCorrectionEnabled && _networkSystemClockCore.IsClockSetup(thread))
             {
-                result = _networkSystemClockCore.GetSystemClockContext(thread, out SystemClockContext context);
+                result = _networkSystemClockCore.GetClockContext(thread, out SystemClockContext context);
 
                 if (result == ResultCode.Success)
                 {
-                    _localSystemClockCore.SetSystemClockContext(context);
+                    _localSystemClockCore.SetClockContext(context);
                 }
             }
 
             return result;
         }
 
+        internal void CreateAutomaticCorrectionEvent(Horizon system)
+        {
+            _autoCorrectionEvent = new KEvent(system);
+        }
+
         public ResultCode SetAutomaticCorrectionEnabled(KThread thread, bool autoCorrectionEnabled)
         {
             ResultCode result = ApplyAutomaticCorrection(thread, autoCorrectionEnabled);
@@ -87,5 +83,25 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
         {
             return _autoCorrectionEnabled;
         }
+
+        public KReadableEvent GetAutomaticCorrectionReadableEvent()
+        {
+            return _autoCorrectionEvent.ReadableEvent;
+        }
+
+        public void SetAutomaticCorrectionUpdatedTime(SteadyClockTimePoint steadyClockTimePoint)
+        {
+            _autoCorrectionTime = steadyClockTimePoint;
+        }
+
+        public SteadyClockTimePoint GetAutomaticCorrectionUpdatedTime()
+        {
+            return _autoCorrectionTime;
+        }
+
+        public void SignalAutomaticCorrectionEvent()
+        {
+            _autoCorrectionEvent.WritableEvent.Signal();
+        }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
index 54d9accf..83ace981 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SteadyClockCore.cs
@@ -7,10 +7,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
     abstract class SteadyClockCore
     {
         private UInt128 _clockSourceId;
+        private bool    _isRtcResetDetected;
+        private bool    _isInitialized;
 
         public SteadyClockCore()
         {
-            _clockSourceId = new UInt128(Guid.NewGuid().ToByteArray());
+            _clockSourceId      = new UInt128(Guid.NewGuid().ToByteArray());
+            _isRtcResetDetected = false;
+            _isInitialized      = false;
         }
 
         public UInt128 GetClockSourceId()
@@ -18,6 +22,16 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
             return _clockSourceId;
         }
 
+        public void SetClockSourceId(UInt128 clockSourceId)
+        {
+            _clockSourceId = clockSourceId;
+        }
+
+        public void SetRtcReset()
+        {
+            _isRtcResetDetected = true;
+        }
+
         public virtual TimeSpanType GetTestOffset()
         {
             return new TimeSpanType(0);
@@ -25,16 +39,21 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
 
         public virtual void SetTestOffset(TimeSpanType testOffset) {}
 
-        public virtual ResultCode GetRtcValue(out ulong rtcValue)
+        public ResultCode GetRtcValue(out ulong rtcValue)
         {
             rtcValue = 0;
 
             return ResultCode.NotImplemented;
         }
 
-        public virtual ResultCode GetSetupResultValue()
+        public bool IsRtcResetDetected()
         {
-            return ResultCode.NotImplemented;
+            return _isRtcResetDetected;
+        }
+
+        public ResultCode GetSetupResultValue()
+        {
+            return ResultCode.Success;
         }
 
         public virtual TimeSpanType GetInternalOffset()
@@ -49,6 +68,13 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
             throw new NotImplementedException();
         }
 
+        public virtual TimeSpanType GetCurrentRawTimePoint(KThread thread)
+        {
+            SteadyClockTimePoint timePoint = GetTimePoint(thread);
+
+            return TimeSpanType.FromSeconds(timePoint.TimePoint);
+        }
+
         public SteadyClockTimePoint GetCurrentTimePoint(KThread thread)
         {
             SteadyClockTimePoint result = GetTimePoint(thread);
@@ -58,5 +84,15 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
 
             return result;
         }
+
+        public bool IsInitialized()
+        {
+            return _isInitialized;
+        }
+
+        public void MarkInitialized()
+        {
+            _isInitialized = true;
+        }
     }
 }
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs
new file mode 100644
index 00000000..629d8ee1
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockContextUpdateCallback.cs
@@ -0,0 +1,72 @@
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Services.Time.Clock
+{
+    abstract class SystemClockContextUpdateCallback
+    {
+        private   List<KWritableEvent> _operationEventList;
+        protected SystemClockContext   _context;
+        private   bool                 _hasContext;
+
+        public SystemClockContextUpdateCallback()
+        {
+            _operationEventList = new List<KWritableEvent>();
+            _context            = new SystemClockContext();
+            _hasContext         = false;
+        }
+
+        private bool NeedUpdate(SystemClockContext context)
+        {
+            if (_hasContext)
+            {
+                return _context.Offset != context.Offset || _context.SteadyTimePoint.ClockSourceId != context.SteadyTimePoint.ClockSourceId;
+            }
+
+            return true;
+        }
+
+        public void RegisterOperationEvent(KWritableEvent writableEvent)
+        {
+            Monitor.Enter(_operationEventList);
+            _operationEventList.Add(writableEvent);
+            Monitor.Exit(_operationEventList);
+        }
+
+        private void BroadcastOperationEvent()
+        {
+            Monitor.Enter(_operationEventList);
+
+            foreach (KWritableEvent e in _operationEventList)
+            {
+                e.Signal();
+            }
+
+            Monitor.Exit(_operationEventList);
+        }
+
+        protected abstract ResultCode Update();
+
+        public ResultCode Update(SystemClockContext context)
+        {
+            ResultCode result = ResultCode.Success;
+
+            if (NeedUpdate(context))
+            {
+                _context    = context;
+                _hasContext = true;
+
+                result = Update();
+
+                if (result == ResultCode.Success)
+                {
+                    BroadcastOperationEvent();
+                }
+            }
+
+            return result;
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs
index 52f3c908..865b1c09 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/SystemClockCore.cs
@@ -1,18 +1,23 @@
-using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+using Ryujinx.HLE.HOS.Kernel.Threading;
 
 namespace Ryujinx.HLE.HOS.Services.Time.Clock
 {
     abstract class SystemClockCore
     {
-        private SteadyClockCore    _steadyClockCore;
-        private SystemClockContext _context;
+        private SteadyClockCore                  _steadyClockCore;
+        private SystemClockContext               _context;
+        private bool                             _isInitialized;
+        private SystemClockContextUpdateCallback _systemClockContextUpdateCallback;
 
         public SystemClockCore(SteadyClockCore steadyClockCore)
         {
             _steadyClockCore = steadyClockCore;
             _context         = new SystemClockContext();
+            _isInitialized   = false;
 
             _context.SteadyTimePoint.ClockSourceId = steadyClockCore.GetClockSourceId();
+            _systemClockContextUpdateCallback      = null;
         }
 
         public virtual SteadyClockCore GetSteadyClockCore()
@@ -20,31 +25,115 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
             return _steadyClockCore;
         }
 
-        public virtual ResultCode GetSystemClockContext(KThread thread, out SystemClockContext context)
+        public ResultCode GetCurrentTime(KThread thread, out long posixTime)
+        {
+            posixTime = 0;
+
+            SteadyClockTimePoint currentTimePoint = _steadyClockCore.GetCurrentTimePoint(thread);
+
+            ResultCode result = GetClockContext(thread, out SystemClockContext clockContext);
+
+            if (result == ResultCode.Success)
+            {
+                result = ResultCode.TimeMismatch;
+
+                if (currentTimePoint.ClockSourceId == clockContext.SteadyTimePoint.ClockSourceId)
+                {
+                    posixTime = clockContext.Offset + currentTimePoint.TimePoint;
+
+                    result = 0;
+                }
+            }
+
+            return result;
+        }
+
+        public ResultCode SetCurrentTime(KThread thread, long posixTime)
+        {
+            SteadyClockTimePoint currentTimePoint = _steadyClockCore.GetCurrentTimePoint(thread);
+
+            SystemClockContext clockContext = new SystemClockContext()
+            {
+                Offset          = posixTime - currentTimePoint.TimePoint,
+                SteadyTimePoint = currentTimePoint
+            };
+
+            ResultCode result = SetClockContext(clockContext);
+
+            if (result == ResultCode.Success)
+            {
+                result = Flush(clockContext);
+            }
+
+            return result;
+        }
+
+        public virtual ResultCode GetClockContext(KThread thread, out SystemClockContext context)
         {
             context = _context;
 
             return ResultCode.Success;
         }
 
-        public virtual ResultCode SetSystemClockContext(SystemClockContext context)
+        public virtual ResultCode SetClockContext(SystemClockContext context)
         {
             _context = context;
 
             return ResultCode.Success;
         }
 
-        public abstract ResultCode Flush(SystemClockContext context);
-
-        public bool IsClockSetup(KThread thread)
+        protected virtual ResultCode Flush(SystemClockContext context)
         {
-            ResultCode result = GetSystemClockContext(thread, out SystemClockContext context);
+            if (_systemClockContextUpdateCallback == null)
+            {
+                return ResultCode.Success;
+            }
+
+            return _systemClockContextUpdateCallback.Update(context);
+        }
+
+        public void SetUpdateCallbackInstance(SystemClockContextUpdateCallback systemClockContextUpdateCallback)
+        {
+            _systemClockContextUpdateCallback = systemClockContextUpdateCallback;
+        }
+
+        public void RegisterOperationEvent(KWritableEvent writableEvent)
+        {
+            if (_systemClockContextUpdateCallback != null)
+            {
+                _systemClockContextUpdateCallback.RegisterOperationEvent(writableEvent);
+            }
+        }
+
+        public ResultCode SetSystemClockContext(SystemClockContext context)
+        {
+            ResultCode result = SetClockContext(context);
 
             if (result == ResultCode.Success)
             {
-                SteadyClockCore steadyClockCore = GetSteadyClockCore();
+                result = Flush(context);
+            }
 
-                SteadyClockTimePoint steadyClockTimePoint = steadyClockCore.GetCurrentTimePoint(thread);
+            return result;
+        }
+
+        public bool IsInitialized()
+        {
+            return _isInitialized;
+        }
+
+        public void MarkInitialized()
+        {
+            _isInitialized = true;
+        }
+
+        public bool IsClockSetup(KThread thread)
+        {
+            ResultCode result = GetClockContext(thread, out SystemClockContext context);
+
+            if (result == ResultCode.Success)
+            {
+                SteadyClockTimePoint steadyClockTimePoint = _steadyClockCore.GetCurrentTimePoint(thread);
 
                 return steadyClockTimePoint.ClockSourceId == context.SteadyTimePoint.ClockSourceId;
             }
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs
index e5baba25..06502082 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/TickBasedSteadyClockCore.cs
@@ -4,22 +4,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
 {
     class TickBasedSteadyClockCore : SteadyClockCore
     {
-        private static TickBasedSteadyClockCore _instance;
-
-        public static TickBasedSteadyClockCore Instance
-        {
-            get
-            {
-                if (_instance == null)
-                {
-                    _instance = new TickBasedSteadyClockCore();
-                }
-
-                return _instance;
-            }
-        }
-
-        private TickBasedSteadyClockCore() {}
+        public TickBasedSteadyClockCore() {}
 
         public override SteadyClockTimePoint GetTimePoint(KThread thread)
         {
@@ -29,7 +14,17 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
                 ClockSourceId = GetClockSourceId()
             };
 
-            TimeSpanType ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
+            TimeSpanType ticksTimeSpan;
+
+            // As this may be called before the guest code, we support passing a null thread to make this api usable.
+            if (thread == null)
+            {
+                ticksTimeSpan = TimeSpanType.FromSeconds(0);
+            }
+            else
+            {
+                ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
+            }
 
             result.TimePoint = ticksTimeSpan.ToSeconds();
 
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs
index 0055b5ea..71fb4521 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/SteadyClockTimePoint.cs
@@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
     [StructLayout(LayoutKind.Sequential)]
     struct SteadyClockTimePoint
     {
-        public long TimePoint;
+        public long    TimePoint;
         public UInt128 ClockSourceId;
 
         public ResultCode GetSpanBetween(SteadyClockTimePoint other, out long outSpan)
@@ -30,5 +30,14 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
 
             return ResultCode.Overflow;
         }
+
+        public static SteadyClockTimePoint GetRandom()
+        {
+            return new SteadyClockTimePoint
+            {
+                TimePoint     = 0,
+                ClockSourceId = new UInt128(Guid.NewGuid().ToByteArray())
+            };
+        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs
index 93579709..c336ad41 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs
@@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
     {
         private const long NanoSecondsPerSecond = 1000000000;
 
+        public static readonly TimeSpanType Zero = new TimeSpanType(0);
+
         public long NanoSeconds;
 
         public TimeSpanType(long nanoSeconds)
diff --git a/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs b/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs
index cb10da47..8ec55c15 100644
--- a/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/IPowerStateRequestHandler.cs
@@ -1,6 +1,6 @@
 namespace Ryujinx.HLE.HOS.Services.Time
 {
-    [Service("time:m")] // 9.0.0+
+    [Service("time:p")] // 9.0.0+
     class IPowerStateRequestHandler : IpcService
     {
         public IPowerStateRequestHandler(ServiceCtx context) { }
diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs
new file mode 100644
index 00000000..605cbbbd
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForGlue.cs
@@ -0,0 +1,182 @@
+using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Services.Pcv.Bpc;
+using Ryujinx.HLE.HOS.Services.Settings;
+using Ryujinx.HLE.HOS.Services.Time.Clock;
+using Ryujinx.HLE.HOS.Services.Time.StaticService;
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Time
+{
+    [Service("time:a", TimePermissions.Admin)]
+    [Service("time:r", TimePermissions.Repair)]
+    [Service("time:u", TimePermissions.User)]
+    class IStaticServiceForGlue : IpcService
+    {
+        private IStaticServiceForPsc _inner;
+        private TimePermissions      _permissions;
+
+        public IStaticServiceForGlue(ServiceCtx context, TimePermissions permissions)
+        {
+            _permissions = permissions;
+            _inner       = new IStaticServiceForPsc(context, permissions);
+        }
+
+        [Command(0)]
+        // GetStandardUserSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
+        public ResultCode GetStandardUserSystemClock(ServiceCtx context)
+        {
+            return _inner.GetStandardUserSystemClock(context);
+        }
+
+        [Command(1)]
+        // GetStandardNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
+        public ResultCode GetStandardNetworkSystemClock(ServiceCtx context)
+        {
+            return _inner.GetStandardNetworkSystemClock(context);
+        }
+
+        [Command(2)]
+        // GetStandardSteadyClock() -> object<nn::timesrv::detail::service::ISteadyClock>
+        public ResultCode GetStandardSteadyClock(ServiceCtx context)
+        {
+            return _inner.GetStandardSteadyClock(context);
+        }
+
+        [Command(3)]
+        // GetTimeZoneService() -> object<nn::timesrv::detail::service::ITimeZoneService>
+        public ResultCode GetTimeZoneService(ServiceCtx context)
+        {
+            MakeObject(context, new ITimeZoneServiceForGlue(TimeManager.Instance.TimeZone, (_permissions & TimePermissions.TimeZoneWritableMask) != 0));
+
+            return ResultCode.Success;
+        }
+
+        [Command(4)]
+        // GetStandardLocalSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
+        public ResultCode GetStandardLocalSystemClock(ServiceCtx context)
+        {
+            return _inner.GetStandardLocalSystemClock(context);
+        }
+
+        [Command(5)] // 4.0.0+
+        // GetEphemeralNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
+        public ResultCode GetEphemeralNetworkSystemClock(ServiceCtx context)
+        {
+            return _inner.GetEphemeralNetworkSystemClock(context);
+        }
+
+        [Command(20)] // 6.0.0+
+        // GetSharedMemoryNativeHandle() -> handle<copy>
+        public ResultCode GetSharedMemoryNativeHandle(ServiceCtx context)
+        {
+            return _inner.GetSharedMemoryNativeHandle(context);
+        }
+
+        [Command(50)] // 4.0.0+
+        // SetStandardSteadyClockInternalOffset(nn::TimeSpanType internal_offset)
+        public ResultCode SetStandardSteadyClockInternalOffset(ServiceCtx context)
+        {
+            if ((_permissions & TimePermissions.BypassUninitialized) == 0)
+            {
+                return ResultCode.PermissionDenied;
+            }
+
+            TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>();
+
+            // TODO: set:sys SetExternalSteadyClockInternalOffset(internalOffset.ToSeconds())
+
+            return ResultCode.Success;
+        }
+
+        [Command(51)] // 9.0.0+
+        // GetStandardSteadyClockRtcValue() -> u64
+        public ResultCode GetStandardSteadyClockRtcValue(ServiceCtx context)
+        {
+            ResultCode result = (ResultCode)IRtcManager.GetExternalRtcValue(out ulong rtcValue);
+
+            if (result == ResultCode.Success)
+            {
+                context.ResponseData.Write(rtcValue);
+            }
+
+            return result;
+        }
+
+        [Command(100)]
+        // IsStandardUserSystemClockAutomaticCorrectionEnabled() -> bool
+        public ResultCode IsStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context)
+        {
+            return _inner.IsStandardUserSystemClockAutomaticCorrectionEnabled(context);
+        }
+
+        [Command(101)]
+        // SetStandardUserSystemClockAutomaticCorrectionEnabled(b8)
+        public ResultCode SetStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context)
+        {
+            return _inner.SetStandardUserSystemClockAutomaticCorrectionEnabled(context);
+        }
+
+        [Command(102)] // 5.0.0+
+        // GetStandardUserSystemClockInitialYear() -> u32
+        public ResultCode GetStandardUserSystemClockInitialYear(ServiceCtx context)
+        {
+            if (!NxSettings.Settings.TryGetValue("time!standard_user_clock_initial_year", out object standardUserSystemClockInitialYear))
+            {
+                throw new InvalidOperationException("standard_user_clock_initial_year isn't defined in system settings!");
+            }
+
+            context.ResponseData.Write((int)standardUserSystemClockInitialYear);
+
+            return ResultCode.Success;
+        }
+
+        [Command(200)] // 3.0.0+
+        // IsStandardNetworkSystemClockAccuracySufficient() -> bool
+        public ResultCode IsStandardNetworkSystemClockAccuracySufficient(ServiceCtx context)
+        {
+            return _inner.IsStandardNetworkSystemClockAccuracySufficient(context);
+        }
+
+        [Command(201)] // 6.0.0+
+        // GetStandardUserSystemClockAutomaticCorrectionUpdatedTime() -> nn::time::SteadyClockTimePoint
+        public ResultCode GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(ServiceCtx context)
+        {
+            return _inner.GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(context);
+        }
+
+        [Command(300)] // 4.0.0+
+        // CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> s64
+        public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context)
+        {
+            return _inner.CalculateMonotonicSystemClockBaseTimePoint(context);
+        }
+
+        [Command(400)] // 4.0.0+
+        // GetClockSnapshot(u8) -> buffer<nn::time::sf::ClockSnapshot, 0x1a>
+        public ResultCode GetClockSnapshot(ServiceCtx context)
+        {
+            return _inner.GetClockSnapshot(context);
+        }
+
+        [Command(401)] // 4.0.0+
+        // GetClockSnapshotFromSystemClockContext(u8, nn::time::SystemClockContext, nn::time::SystemClockContext) -> buffer<nn::time::sf::ClockSnapshot, 0x1a>
+        public ResultCode GetClockSnapshotFromSystemClockContext(ServiceCtx context)
+        {
+            return _inner.GetClockSnapshotFromSystemClockContext(context);
+        }
+
+        [Command(500)] // 4.0.0+
+        // CalculateStandardUserSystemClockDifferenceByUser(buffer<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> nn::TimeSpanType
+        public ResultCode CalculateStandardUserSystemClockDifferenceByUser(ServiceCtx context)
+        {
+            return _inner.CalculateStandardUserSystemClockDifferenceByUser(context);
+        }
+
+        [Command(501)] // 4.0.0+
+        // CalculateSpanBetween(buffer<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> nn::TimeSpanType
+        public ResultCode CalculateSpanBetween(ServiceCtx context)
+        {
+            return _inner.CalculateSpanBetween(context);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs
similarity index 64%
rename from Ryujinx.HLE/HOS/Services/Time/IStaticService.cs
rename to Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs
index 0cfdebcf..5ea3910f 100644
--- a/Ryujinx.HLE/HOS/Services/Time/IStaticService.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/IStaticServiceForPsc.cs
@@ -12,28 +12,30 @@ using System.Runtime.InteropServices;
 
 namespace Ryujinx.HLE.HOS.Services.Time
 {
-    [Service("time:a", TimePermissions.Applet)]
     [Service("time:s", TimePermissions.System)]
-    [Service("time:u", TimePermissions.User)]
-    [Service("time:p", TimePermissions.System)] // 9.0.0+ - TODO: Fix the permission.
-    class IStaticService : IpcService
+    [Service("time:su", TimePermissions.SystemUpdate)]
+    class IStaticServiceForPsc : IpcService
     {
+        private TimeManager     _timeManager;
         private TimePermissions _permissions;
 
         private int _timeSharedMemoryNativeHandle = 0;
 
-        private static readonly DateTime StartupDate = DateTime.UtcNow;
+        public IStaticServiceForPsc(ServiceCtx context, TimePermissions permissions) : this(TimeManager.Instance, permissions) {}
 
-        public IStaticService(ServiceCtx context, TimePermissions permissions)
+        public IStaticServiceForPsc(TimeManager manager, TimePermissions permissions)
         {
             _permissions = permissions;
+            _timeManager = manager;
         }
 
         [Command(0)]
         // GetStandardUserSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
         public ResultCode GetStandardUserSystemClock(ServiceCtx context)
         {
-            MakeObject(context, new ISystemClock(StandardUserSystemClockCore.Instance, (_permissions & TimePermissions.UserSystemClockWritableMask) != 0));
+            MakeObject(context, new ISystemClock(_timeManager.StandardUserSystemClock,
+                (_permissions & TimePermissions.UserSystemClockWritableMask) != 0,
+                (_permissions & TimePermissions.BypassUninitialized) != 0));
 
             return ResultCode.Success;
         }
@@ -42,7 +44,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
         // GetStandardNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
         public ResultCode GetStandardNetworkSystemClock(ServiceCtx context)
         {
-            MakeObject(context, new ISystemClock(StandardNetworkSystemClockCore.Instance, (_permissions & TimePermissions.NetworkSystemClockWritableMask) != 0));
+            MakeObject(context, new ISystemClock(_timeManager.StandardNetworkSystemClock,
+                (_permissions & TimePermissions.NetworkSystemClockWritableMask) != 0,
+                (_permissions & TimePermissions.BypassUninitialized) != 0));
 
             return ResultCode.Success;
         }
@@ -51,7 +55,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
         // GetStandardSteadyClock() -> object<nn::timesrv::detail::service::ISteadyClock>
         public ResultCode GetStandardSteadyClock(ServiceCtx context)
         {
-            MakeObject(context, new ISteadyClock());
+            MakeObject(context, new ISteadyClock(_timeManager.StandardSteadyClock,
+                (_permissions & TimePermissions.SteadyClockWritableMask) != 0,
+                (_permissions & TimePermissions.BypassUninitialized) != 0));
 
             return ResultCode.Success;
         }
@@ -60,7 +66,8 @@ namespace Ryujinx.HLE.HOS.Services.Time
         // GetTimeZoneService() -> object<nn::timesrv::detail::service::ITimeZoneService>
         public ResultCode GetTimeZoneService(ServiceCtx context)
         {
-            MakeObject(context, new ITimeZoneService());
+            MakeObject(context, new ITimeZoneServiceForPsc(_timeManager.TimeZone.Manager,
+                (_permissions & TimePermissions.TimeZoneWritableMask) != 0));
 
             return ResultCode.Success;
         }
@@ -69,7 +76,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
         // GetStandardLocalSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
         public ResultCode GetStandardLocalSystemClock(ServiceCtx context)
         {
-            MakeObject(context, new ISystemClock(StandardLocalSystemClockCore.Instance, (_permissions & TimePermissions.LocalSystemClockWritableMask) != 0));
+            MakeObject(context, new ISystemClock(_timeManager.StandardLocalSystemClock,
+                (_permissions & TimePermissions.LocalSystemClockWritableMask) != 0,
+                (_permissions & TimePermissions.BypassUninitialized) != 0));
 
             return ResultCode.Success;
         }
@@ -78,7 +87,9 @@ namespace Ryujinx.HLE.HOS.Services.Time
         // GetEphemeralNetworkSystemClock() -> object<nn::timesrv::detail::service::ISystemClock>
         public ResultCode GetEphemeralNetworkSystemClock(ServiceCtx context)
         {
-            MakeObject(context, new ISystemClock(StandardNetworkSystemClockCore.Instance, false));
+            MakeObject(context, new ISystemClock(_timeManager.StandardNetworkSystemClock,
+                (_permissions & TimePermissions.NetworkSystemClockWritableMask) != 0,
+                (_permissions & TimePermissions.BypassUninitialized) != 0));
 
             return ResultCode.Success;
         }
@@ -89,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
         {
             if (_timeSharedMemoryNativeHandle == 0)
             {
-                if (context.Process.HandleTable.GenerateHandle(context.Device.System.TimeSharedMem, out _timeSharedMemoryNativeHandle) != KernelResult.Success)
+                if (context.Process.HandleTable.GenerateHandle(_timeManager.SharedMemory.GetSharedMemory(), out _timeSharedMemoryNativeHandle) != KernelResult.Success)
                 {
                     throw new InvalidOperationException("Out of handles!");
                 }
@@ -100,11 +111,34 @@ namespace Ryujinx.HLE.HOS.Services.Time
             return ResultCode.Success;
         }
 
+        [Command(50)] // 4.0.0+
+        // SetStandardSteadyClockInternalOffset(nn::TimeSpanType internal_offset)
+        public ResultCode SetStandardSteadyClockInternalOffset(ServiceCtx context)
+        {
+            // This is only implemented in glue's StaticService.
+            return ResultCode.NotImplemented;
+        }
+
+        [Command(51)] // 9.0.0+
+        // GetStandardSteadyClockRtcValue() -> u64
+        public ResultCode GetStandardSteadyClockRtcValue(ServiceCtx context)
+        {
+            // This is only implemented in glue's StaticService.
+            return ResultCode.NotImplemented;
+        }
+
         [Command(100)]
         // IsStandardUserSystemClockAutomaticCorrectionEnabled() -> bool
         public ResultCode IsStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context)
         {
-            context.ResponseData.Write(StandardUserSystemClockCore.Instance.IsAutomaticCorrectionEnabled());
+            StandardUserSystemClockCore userClock = _timeManager.StandardUserSystemClock;
+
+            if (!userClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            context.ResponseData.Write(userClock.IsAutomaticCorrectionEnabled());
 
             return ResultCode.Success;
         }
@@ -113,6 +147,14 @@ namespace Ryujinx.HLE.HOS.Services.Time
         // SetStandardUserSystemClockAutomaticCorrectionEnabled(b8)
         public ResultCode SetStandardUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context)
         {
+            SteadyClockCore             steadyClock = _timeManager.StandardSteadyClock;
+            StandardUserSystemClockCore userClock   = _timeManager.StandardUserSystemClock;
+
+            if (!userClock.IsInitialized() || !steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
             if ((_permissions & TimePermissions.UserSystemClockWritableMask) == 0)
             {
                 return ResultCode.PermissionDenied;
@@ -120,14 +162,50 @@ namespace Ryujinx.HLE.HOS.Services.Time
 
             bool autoCorrectionEnabled = context.RequestData.ReadBoolean();
 
-            return StandardUserSystemClockCore.Instance.SetAutomaticCorrectionEnabled(context.Thread, autoCorrectionEnabled);
+            ResultCode result = userClock.SetAutomaticCorrectionEnabled(context.Thread, autoCorrectionEnabled);
+
+            if (result == ResultCode.Success)
+            {
+                _timeManager.SharedMemory.SetAutomaticCorrectionEnabled(autoCorrectionEnabled);
+
+                SteadyClockTimePoint currentTimePoint = userClock.GetSteadyClockCore().GetCurrentTimePoint(context.Thread);
+
+                userClock.SetAutomaticCorrectionUpdatedTime(currentTimePoint);
+                userClock.SignalAutomaticCorrectionEvent();
+            }
+
+            return result;
+        }
+
+        [Command(102)] // 5.0.0+
+        // GetStandardUserSystemClockInitialYear() -> u32
+        public ResultCode GetStandardUserSystemClockInitialYear(ServiceCtx context)
+        {
+            // This is only implemented in glue's StaticService.
+            return ResultCode.NotImplemented;
         }
 
         [Command(200)] // 3.0.0+
         // IsStandardNetworkSystemClockAccuracySufficient() -> bool
         public ResultCode IsStandardNetworkSystemClockAccuracySufficient(ServiceCtx context)
         {
-            context.ResponseData.Write(StandardNetworkSystemClockCore.Instance.IsStandardNetworkSystemClockAccuracySufficient(context.Thread));
+            context.ResponseData.Write(_timeManager.StandardNetworkSystemClock.IsStandardNetworkSystemClockAccuracySufficient(context.Thread));
+
+            return ResultCode.Success;
+        }
+
+        [Command(201)] // 6.0.0+
+        // GetStandardUserSystemClockAutomaticCorrectionUpdatedTime() -> nn::time::SteadyClockTimePoint
+        public ResultCode GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(ServiceCtx context)
+        {
+            StandardUserSystemClockCore userClock = _timeManager.StandardUserSystemClock;
+
+            if (!userClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            context.ResponseData.WriteStruct(userClock.GetAutomaticCorrectionUpdatedTime());
 
             return ResultCode.Success;
         }
@@ -136,8 +214,15 @@ namespace Ryujinx.HLE.HOS.Services.Time
         // CalculateMonotonicSystemClockBaseTimePoint(nn::time::SystemClockContext) -> s64
         public ResultCode CalculateMonotonicSystemClockBaseTimePoint(ServiceCtx context)
         {
+            SteadyClockCore steadyClock = _timeManager.StandardSteadyClock;
+
+            if (!steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
             SystemClockContext   otherContext     = context.RequestData.ReadStruct<SystemClockContext>();
-            SteadyClockTimePoint currentTimePoint = StandardSteadyClockCore.Instance.GetCurrentTimePoint(context.Thread);
+            SteadyClockTimePoint currentTimePoint = steadyClock.GetCurrentTimePoint(context.Thread);
 
             ResultCode result = ResultCode.TimeMismatch;
 
@@ -148,7 +233,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
 
                 context.ResponseData.Write(baseTimePoint);
 
-                result = 0;
+                result = ResultCode.Success;
             }
 
             return result;
@@ -160,11 +245,11 @@ namespace Ryujinx.HLE.HOS.Services.Time
         {
             byte type = context.RequestData.ReadByte();
 
-            ResultCode result = StandardUserSystemClockCore.Instance.GetSystemClockContext(context.Thread, out SystemClockContext userContext);
+            ResultCode result = _timeManager.StandardUserSystemClock.GetClockContext(context.Thread, out SystemClockContext userContext);
 
             if (result == ResultCode.Success)
             {
-                result = StandardNetworkSystemClockCore.Instance.GetSystemClockContext(context.Thread, out SystemClockContext networkContext);
+                result = _timeManager.StandardNetworkSystemClock.GetClockContext(context.Thread, out SystemClockContext networkContext);
 
                 if (result == ResultCode.Success)
                 {
@@ -205,7 +290,6 @@ namespace Ryujinx.HLE.HOS.Services.Time
         // CalculateStandardUserSystemClockDifferenceByUser(buffer<nn::time::sf::ClockSnapshot, 0x19>, buffer<nn::time::sf::ClockSnapshot, 0x19>) -> nn::TimeSpanType
         public ResultCode CalculateStandardUserSystemClockDifferenceByUser(ServiceCtx context)
         {
-
             ClockSnapshot clockSnapshotA = ReadClockSnapshotFromBuffer(context, context.Request.ExchangeBuff[0]);
             ClockSnapshot clockSnapshotB = ReadClockSnapshotFromBuffer(context, context.Request.ExchangeBuff[1]);
             TimeSpanType  difference     = TimeSpanType.FromSeconds(clockSnapshotB.UserContext.Offset - clockSnapshotA.UserContext.Offset);
@@ -259,25 +343,32 @@ namespace Ryujinx.HLE.HOS.Services.Time
         {
             clockSnapshot = new ClockSnapshot();
 
-            SteadyClockCore      steadyClockCore  = StandardSteadyClockCore.Instance;
+            SteadyClockCore      steadyClockCore  = _timeManager.StandardSteadyClock;
             SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(thread);
 
-            clockSnapshot.IsAutomaticCorrectionEnabled = StandardUserSystemClockCore.Instance.IsAutomaticCorrectionEnabled();
+            clockSnapshot.IsAutomaticCorrectionEnabled = _timeManager.StandardUserSystemClock.IsAutomaticCorrectionEnabled();
             clockSnapshot.UserContext                  = userContext;
             clockSnapshot.NetworkContext               = networkContext;
 
-            char[] tzName       = TimeZoneManager.Instance.GetDeviceLocationName().ToCharArray();
+            ResultCode result = _timeManager.TimeZone.Manager.GetDeviceLocationName(out string deviceLocationName);
+
+            if (result != ResultCode.Success)
+            {
+                return result;
+            }
+
+            char[] tzName       = deviceLocationName.ToCharArray();
             char[] locationName = new char[0x24];
 
             Array.Copy(tzName, locationName, tzName.Length);
 
             clockSnapshot.LocationName = locationName;
 
-            ResultCode result = ClockSnapshot.GetCurrentTime(out clockSnapshot.UserTime, currentTimePoint, clockSnapshot.UserContext);
+            result = ClockSnapshot.GetCurrentTime(out clockSnapshot.UserTime, currentTimePoint, clockSnapshot.UserContext);
 
             if (result == ResultCode.Success)
             {
-                result = TimeZoneManager.Instance.ToCalendarTimeWithMyRules(clockSnapshot.UserTime, out CalendarInfo userCalendarInfo);
+                result = _timeManager.TimeZone.Manager.ToCalendarTimeWithMyRules(clockSnapshot.UserTime, out CalendarInfo userCalendarInfo);
 
                 if (result == ResultCode.Success)
                 {
@@ -289,7 +380,7 @@ namespace Ryujinx.HLE.HOS.Services.Time
                         clockSnapshot.NetworkTime = 0;
                     }
 
-                    result = TimeZoneManager.Instance.ToCalendarTimeWithMyRules(clockSnapshot.NetworkTime, out CalendarInfo networkCalendarInfo);
+                    result = _timeManager.TimeZone.Manager.ToCalendarTimeWithMyRules(clockSnapshot.NetworkTime, out CalendarInfo networkCalendarInfo);
 
                     if (result == ResultCode.Success)
                     {
diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
index 514e901e..a897b3f7 100644
--- a/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/ITimeServiceManager.cs
@@ -1,8 +1,219 @@
-namespace Ryujinx.HLE.HOS.Services.Time
+using Ryujinx.Common;
+using Ryujinx.HLE.Exceptions;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Services.Time.Clock;
+using Ryujinx.HLE.Utilities;
+using System;
+using System.IO;
+using System.Text;
+
+namespace Ryujinx.HLE.HOS.Services.Time
 {
-    [Service("time:su")] // 9.0.0+
+    [Service("time:m")] // 9.0.0+
     class ITimeServiceManager : IpcService
     {
-        public ITimeServiceManager(ServiceCtx context) { }
+        private TimeManager _timeManager;
+        private int         _automaticCorrectionEvent;
+
+        public ITimeServiceManager(ServiceCtx context)
+        {
+            _timeManager              = TimeManager.Instance;
+            _automaticCorrectionEvent = 0;
+        }
+
+        [Command(0)]
+        // GetUserStaticService() -> object<nn::timesrv::detail::service::IStaticService>
+        public ResultCode GetUserStaticService(ServiceCtx context)
+        {
+            MakeObject(context, new IStaticServiceForPsc(_timeManager, TimePermissions.User));
+
+            return ResultCode.Success;
+        }
+
+        [Command(5)]
+        // GetAdminStaticService() -> object<nn::timesrv::detail::service::IStaticService>
+        public ResultCode GetAdminStaticService(ServiceCtx context)
+        {
+            MakeObject(context, new IStaticServiceForPsc(_timeManager, TimePermissions.Admin));
+
+            return ResultCode.Success;
+        }
+
+        [Command(6)]
+        // GetRepairStaticService() -> object<nn::timesrv::detail::service::IStaticService>
+        public ResultCode GetRepairStaticService(ServiceCtx context)
+        {
+            MakeObject(context, new IStaticServiceForPsc(_timeManager, TimePermissions.Repair));
+
+            return ResultCode.Success;
+        }
+
+        [Command(9)]
+        // GetManufactureStaticService() -> object<nn::timesrv::detail::service::IStaticService>
+        public ResultCode GetManufactureStaticService(ServiceCtx context)
+        {
+            MakeObject(context, new IStaticServiceForPsc(_timeManager, TimePermissions.Manufacture));
+
+            return ResultCode.Success;
+        }
+
+        [Command(10)]
+        // SetupStandardSteadyClock(nn::util::Uuid clock_source_id, nn::TimeSpanType setup_value,  nn::TimeSpanType internal_offset,  nn::TimeSpanType test_offset, bool is_rtc_reset_detected)
+        public ResultCode SetupStandardSteadyClock(ServiceCtx context)
+        {
+            UInt128      clockSourceId      = context.RequestData.ReadStruct<UInt128>();
+            TimeSpanType setupValue         = context.RequestData.ReadStruct<TimeSpanType>();
+            TimeSpanType internalOffset     = context.RequestData.ReadStruct<TimeSpanType>();
+            TimeSpanType testOffset         = context.RequestData.ReadStruct<TimeSpanType>();
+            bool         isRtcResetDetected = context.RequestData.ReadBoolean();
+
+            _timeManager.SetupStandardSteadyClock(context.Thread, clockSourceId, setupValue, internalOffset, testOffset, isRtcResetDetected);
+
+            return ResultCode.Success;
+        }
+
+        [Command(11)]
+        // SetupStandardLocalSystemClock(nn::time::SystemClockContext context, nn::time::PosixTime posix_time)
+        public ResultCode SetupStandardLocalSystemClock(ServiceCtx context)
+        {
+            SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>();
+            long               posixTime    = context.RequestData.ReadInt64();
+
+            _timeManager.SetupStandardLocalSystemClock(context.Thread, clockContext, posixTime);
+
+            return ResultCode.Success;
+        }
+
+        [Command(12)]
+        // SetupStandardNetworkSystemClock(nn::time::SystemClockContext context, nn::TimeSpanType sufficient_accuracy)
+        public ResultCode SetupStandardNetworkSystemClock(ServiceCtx context)
+        {
+            SystemClockContext clockContext       = context.RequestData.ReadStruct<SystemClockContext>();
+            TimeSpanType       sufficientAccuracy = context.RequestData.ReadStruct<TimeSpanType>();
+
+            _timeManager.SetupStandardNetworkSystemClock(clockContext, sufficientAccuracy);
+
+            return ResultCode.Success;
+        }
+
+        [Command(13)]
+        // SetupStandardUserSystemClock(bool automatic_correction_enabled, nn::time::SteadyClockTimePoint steady_clock_timepoint)
+        public ResultCode SetupStandardUserSystemClock(ServiceCtx context)
+        {
+            bool isAutomaticCorrectionEnabled = context.RequestData.ReadBoolean();
+
+            context.RequestData.BaseStream.Position += 7;
+
+            SteadyClockTimePoint steadyClockTimePoint = context.RequestData.ReadStruct<SteadyClockTimePoint>();
+
+            _timeManager.SetupStandardUserSystemClock(context.Thread, isAutomaticCorrectionEnabled, steadyClockTimePoint);
+
+            return ResultCode.Success;
+        }
+
+        [Command(14)]
+        // SetupTimeZoneManager(nn::time::LocationName location_name, nn::time::SteadyClockTimePoint timezone_update_timepoint, u32 total_location_name_count, nn::time::TimeZoneRuleVersion timezone_rule_version, buffer<nn::time::TimeZoneBinary, 0x21> timezone_binary)
+        public ResultCode SetupTimeZoneManager(ServiceCtx context)
+        {
+            string               locationName            = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
+            SteadyClockTimePoint timeZoneUpdateTimePoint = context.RequestData.ReadStruct<SteadyClockTimePoint>();
+            uint                 totalLocationNameCount  = context.RequestData.ReadUInt32();
+            UInt128              timeZoneRuleVersion     = context.RequestData.ReadStruct<UInt128>();
+
+            (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
+
+            using (MemoryStream timeZoneBinaryStream = new MemoryStream(context.Memory.ReadBytes(bufferPosition, bufferSize)))
+            {
+                _timeManager.SetupTimeZoneManager(locationName, timeZoneUpdateTimePoint, totalLocationNameCount, timeZoneRuleVersion, timeZoneBinaryStream);
+            }
+
+            return ResultCode.Success;
+        }
+
+        [Command(15)]
+        // SetupEphemeralNetworkSystemClock()
+        public ResultCode SetupEphemeralNetworkSystemClock(ServiceCtx context)
+        {
+            _timeManager.SetupEphemeralNetworkSystemClock();
+
+            return ResultCode.Success;
+        }
+
+        [Command(50)]
+        // Unknown50() -> handle<copy>
+        public ResultCode Unknown50(ServiceCtx context)
+        {
+            // TODO: figure out the usage of this event
+            throw new ServiceNotImplementedException(context);
+        }
+
+        [Command(51)]
+        // Unknown51() -> handle<copy>
+        public ResultCode Unknown51(ServiceCtx context)
+        {
+            // TODO: figure out the usage of this event
+            throw new ServiceNotImplementedException(context);
+        }
+
+        [Command(52)]
+        // Unknown52() -> handle<copy>
+        public ResultCode Unknown52(ServiceCtx context)
+        {
+            // TODO: figure out the usage of this event
+            throw new ServiceNotImplementedException(context);
+        }
+
+        [Command(60)]
+        // GetStandardUserSystemClockAutomaticCorrectionEvent() -> handle<copy>
+        public ResultCode GetStandardUserSystemClockAutomaticCorrectionEvent(ServiceCtx context)
+        {
+            if (_automaticCorrectionEvent == 0)
+            {
+                if (context.Process.HandleTable.GenerateHandle(_timeManager.StandardUserSystemClock.GetAutomaticCorrectionReadableEvent(), out _automaticCorrectionEvent) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
+            }
+
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_automaticCorrectionEvent);
+
+            return ResultCode.Success;
+        }
+
+        [Command(100)]
+        // SetStandardSteadyClockRtcOffset(nn::TimeSpanType rtc_offset)
+        public ResultCode SetStandardSteadyClockRtcOffset(ServiceCtx context)
+        {
+            TimeSpanType rtcOffset = context.RequestData.ReadStruct<TimeSpanType>();
+
+            _timeManager.SetStandardSteadyClockRtcOffset(context.Thread, rtcOffset);
+
+            return ResultCode.Success;
+        }
+
+        [Command(200)]
+        // GetAlarmRegistrationEvent() -> handle<copy>
+        public ResultCode GetAlarmRegistrationEvent(ServiceCtx context)
+        {
+            // TODO
+            throw new ServiceNotImplementedException(context);
+        }
+
+        [Command(201)]
+        // UpdateSteadyAlarms()
+        public ResultCode UpdateSteadyAlarms(ServiceCtx context)
+        {
+            // TODO
+            throw new ServiceNotImplementedException(context);
+        }
+
+        [Command(202)]
+        // TryGetNextSteadyClockAlarmSnapshot() -> (bool, nn::time::SteadyClockAlarmSnapshot)
+        public ResultCode TryGetNextSteadyClockAlarmSnapshot(ServiceCtx context)
+        {
+            // TODO
+            throw new ServiceNotImplementedException(context);
+        }
     }
-}
\ No newline at end of file
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs
index ed7130f3..1ef895c2 100644
--- a/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/ResultCode.cs
@@ -9,6 +9,7 @@
 
         PermissionDenied         = (1   << ErrorCodeShift) | ModuleId,
         TimeMismatch             = (102 << ErrorCodeShift) | ModuleId,
+        UninitializedClock       = (103 << ErrorCodeShift) | ModuleId,
         TimeNotFound             = (200 << ErrorCodeShift) | ModuleId,
         Overflow                 = (201 << ErrorCodeShift) | ModuleId,
         LocationNameTooLong      = (801 << ErrorCodeShift) | ModuleId,
diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs
index 31f119df..bf6a4fd1 100644
--- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISteadyClock.cs
@@ -5,42 +5,78 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
 {
     class ISteadyClock : IpcService
     {
+        private SteadyClockCore _steadyClock;
+        private bool            _writePermission;
+        private bool            _bypassUninitializedClock;
+
+        public ISteadyClock(SteadyClockCore steadyClock, bool writePermission, bool bypassUninitializedClock)
+        {
+            _steadyClock              = steadyClock;
+            _writePermission          = writePermission;
+            _bypassUninitializedClock = bypassUninitializedClock;
+        }
+
         [Command(0)]
         // GetCurrentTimePoint() -> nn::time::SteadyClockTimePoint
         public ResultCode GetCurrentTimePoint(ServiceCtx context)
         {
-            SteadyClockTimePoint currentTimePoint = StandardSteadyClockCore.Instance.GetCurrentTimePoint(context.Thread);
+            if (!_bypassUninitializedClock && !_steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            SteadyClockTimePoint currentTimePoint = _steadyClock.GetCurrentTimePoint(context.Thread);
 
             context.ResponseData.WriteStruct(currentTimePoint);
 
             return ResultCode.Success;
         }
 
-        [Command(1)]
+        [Command(2)]
         // GetTestOffset() -> nn::TimeSpanType
         public ResultCode GetTestOffset(ServiceCtx context)
         {
-            context.ResponseData.WriteStruct(StandardSteadyClockCore.Instance.GetTestOffset());
+            if (!_bypassUninitializedClock && !_steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            context.ResponseData.WriteStruct(_steadyClock.GetTestOffset());
 
             return ResultCode.Success;
         }
 
-        [Command(2)]
+        [Command(3)]
         // SetTestOffset(nn::TimeSpanType)
         public ResultCode SetTestOffset(ServiceCtx context)
         {
+            if (!_writePermission)
+            {
+                return ResultCode.PermissionDenied;
+            }
+
+            if (!_bypassUninitializedClock && !_steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
             TimeSpanType testOffset = context.RequestData.ReadStruct<TimeSpanType>();
 
-            StandardSteadyClockCore.Instance.SetTestOffset(testOffset);
+            _steadyClock.SetTestOffset(testOffset);
 
-            return 0;
+            return ResultCode.Success;
         }
 
         [Command(100)] // 2.0.0+
         // GetRtcValue() -> u64
         public ResultCode GetRtcValue(ServiceCtx context)
         {
-            ResultCode result = StandardSteadyClockCore.Instance.GetRtcValue(out ulong rtcValue);
+            if (!_bypassUninitializedClock && !_steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            ResultCode result = _steadyClock.GetRtcValue(out ulong rtcValue);
 
             if (result == ResultCode.Success)
             {
@@ -54,7 +90,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
         // IsRtcResetDetected() -> bool
         public ResultCode IsRtcResetDetected(ServiceCtx context)
         {
-            context.ResponseData.Write(StandardSteadyClockCore.Instance.IsRtcResetDetected());
+            if (!_bypassUninitializedClock && !_steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            context.ResponseData.Write(_steadyClock.IsRtcResetDetected());
 
             return ResultCode.Success;
         }
@@ -63,7 +104,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
         // GetSetupResultValue() -> u32
         public ResultCode GetSetupResultValue(ServiceCtx context)
         {
-            context.ResponseData.Write((uint)StandardSteadyClockCore.Instance.GetSetupResultValue());
+            if (!_bypassUninitializedClock && !_steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            context.ResponseData.Write((uint)_steadyClock.GetSetupResultValue());
 
             return ResultCode.Success;
         }
@@ -72,7 +118,12 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
         // GetInternalOffset() -> nn::TimeSpanType
         public ResultCode GetInternalOffset(ServiceCtx context)
         {
-            context.ResponseData.WriteStruct(StandardSteadyClockCore.Instance.GetInternalOffset());
+            if (!_bypassUninitializedClock && !_steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            context.ResponseData.WriteStruct(_steadyClock.GetInternalOffset());
 
             return ResultCode.Success;
         }
@@ -81,9 +132,19 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
         // SetInternalOffset(nn::TimeSpanType)
         public ResultCode SetInternalOffset(ServiceCtx context)
         {
+            if (!_writePermission)
+            {
+                return ResultCode.PermissionDenied;
+            }
+
+            if (!_bypassUninitializedClock && !_steadyClock.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
             TimeSpanType internalOffset = context.RequestData.ReadStruct<TimeSpanType>();
 
-            StandardSteadyClockCore.Instance.SetInternalOffset(internalOffset);
+            _steadyClock.SetInternalOffset(internalOffset);
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs
index 0d866177..d5b21f8c 100644
--- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ISystemClock.cs
@@ -1,5 +1,9 @@
 using Ryujinx.Common;
+using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.HLE.HOS.Kernel.Common;
+using Ryujinx.HLE.HOS.Kernel.Threading;
 using Ryujinx.HLE.HOS.Services.Time.Clock;
+using System;
 
 namespace Ryujinx.HLE.HOS.Services.Time.StaticService
 {
@@ -7,34 +11,31 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
     {
         private SystemClockCore _clockCore;
         private bool            _writePermission;
+        private bool            _bypassUninitializedClock;
+        private int             _operationEventReadableHandle;
 
-        public ISystemClock(SystemClockCore clockCore, bool writePermission)
+        public ISystemClock(SystemClockCore clockCore, bool writePermission, bool bypassUninitializedClock)
         {
-            _clockCore       = clockCore;
-            _writePermission = writePermission;
+            _clockCore                    = clockCore;
+            _writePermission              = writePermission;
+            _bypassUninitializedClock     = bypassUninitializedClock;
+            _operationEventReadableHandle = 0;
         }
 
         [Command(0)]
         // GetCurrentTime() -> nn::time::PosixTime
         public ResultCode GetCurrentTime(ServiceCtx context)
         {
-            SteadyClockCore      steadyClockCore  = _clockCore.GetSteadyClockCore();
-            SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(context.Thread);
+            if (!_bypassUninitializedClock && !_clockCore.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
 
-            ResultCode result = _clockCore.GetSystemClockContext(context.Thread, out SystemClockContext clockContext);
+            ResultCode result = _clockCore.GetCurrentTime(context.Thread, out long posixTime);
 
             if (result == ResultCode.Success)
             {
-                result = ResultCode.TimeMismatch;
-
-                if (currentTimePoint.ClockSourceId == clockContext.SteadyTimePoint.ClockSourceId)
-                {
-                    long posixTime = clockContext.Offset + currentTimePoint.TimePoint;
-
-                    context.ResponseData.Write(posixTime);
-
-                    result = 0;
-                }
+                context.ResponseData.Write(posixTime);
             }
 
             return result;
@@ -49,31 +50,26 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
                 return ResultCode.PermissionDenied;
             }
 
-            long                 posixTime        = context.RequestData.ReadInt64();
-            SteadyClockCore      steadyClockCore  = _clockCore.GetSteadyClockCore();
-            SteadyClockTimePoint currentTimePoint = steadyClockCore.GetCurrentTimePoint(context.Thread);
-
-            SystemClockContext clockContext = new SystemClockContext()
+            if (!_bypassUninitializedClock && !_clockCore.IsInitialized())
             {
-                Offset          = posixTime - currentTimePoint.TimePoint,
-                SteadyTimePoint = currentTimePoint
-            };
-
-            ResultCode result = _clockCore.SetSystemClockContext(clockContext);
-
-            if (result == ResultCode.Success)
-            {
-                result = _clockCore.Flush(clockContext);
+                return ResultCode.UninitializedClock;
             }
 
-            return result;
+            long posixTime = context.RequestData.ReadInt64();
+
+            return _clockCore.SetCurrentTime(context.Thread, posixTime);
         }
 
         [Command(2)]
-        // GetSystemClockContext() -> nn::time::SystemClockContext
+        // GetClockContext() -> nn::time::SystemClockContext
         public ResultCode GetSystemClockContext(ServiceCtx context)
         {
-            ResultCode result = _clockCore.GetSystemClockContext(context.Thread, out SystemClockContext clockContext);
+            if (!_bypassUninitializedClock && !_clockCore.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
+            ResultCode result = _clockCore.GetClockContext(context.Thread, out SystemClockContext clockContext);
 
             if (result == ResultCode.Success)
             {
@@ -84,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
         }
 
         [Command(3)]
-        // SetSystemClockContext(nn::time::SystemClockContext)
+        // SetClockContext(nn::time::SystemClockContext)
         public ResultCode SetSystemClockContext(ServiceCtx context)
         {
             if (!_writePermission)
@@ -92,16 +88,37 @@ namespace Ryujinx.HLE.HOS.Services.Time.StaticService
                 return ResultCode.PermissionDenied;
             }
 
+            if (!_bypassUninitializedClock && !_clockCore.IsInitialized())
+            {
+                return ResultCode.UninitializedClock;
+            }
+
             SystemClockContext clockContext = context.RequestData.ReadStruct<SystemClockContext>();
 
             ResultCode result = _clockCore.SetSystemClockContext(clockContext);
 
-            if (result == ResultCode.Success)
+            return result;
+        }
+
+        [Command(4)] // 9.0.0+
+        // GetOperationEventReadableHandle() -> handle<copy>
+        public ResultCode GetOperationEventReadableHandle(ServiceCtx context)
+        {
+            if (_operationEventReadableHandle == 0)
             {
-                result = _clockCore.Flush(clockContext);
+                KEvent kEvent = new KEvent(context.Device.System);
+
+                _clockCore.RegisterOperationEvent(kEvent.WritableEvent);
+
+                if (context.Process.HandleTable.GenerateHandle(kEvent.ReadableEvent, out _operationEventReadableHandle) != KernelResult.Success)
+                {
+                    throw new InvalidOperationException("Out of handles!");
+                }
             }
 
-            return result;
+            context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_operationEventReadableHandle);
+
+            return ResultCode.Success;
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneService.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneService.cs
deleted file mode 100644
index c65107df..00000000
--- a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneService.cs
+++ /dev/null
@@ -1,218 +0,0 @@
-using ARMeilleure.Memory;
-using Ryujinx.Common;
-using Ryujinx.Common.Logging;
-using Ryujinx.HLE.HOS.Services.Time.TimeZone;
-using System;
-using System.Text;
-
-namespace Ryujinx.HLE.HOS.Services.Time.StaticService
-{
-    class ITimeZoneService : IpcService
-    {
-        public ITimeZoneService() { }
-
-        [Command(0)]
-        // GetDeviceLocationName() -> nn::time::LocationName
-        public ResultCode GetDeviceLocationName(ServiceCtx context)
-        {
-            char[] tzName = TimeZoneManager.Instance.GetDeviceLocationName().ToCharArray();
-
-            int padding = 0x24 - tzName.Length;
-
-            if (padding < 0)
-            {
-                return ResultCode.LocationNameTooLong;
-            }
-
-            context.ResponseData.Write(tzName);
-
-            for (int index = 0; index < padding; index++)
-            {
-                context.ResponseData.Write((byte)0);
-            }
-
-            return ResultCode.Success;
-        }
-
-        [Command(1)]
-        // SetDeviceLocationName(nn::time::LocationName)
-        public ResultCode SetDeviceLocationName(ServiceCtx context)
-        {
-            string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
-
-            return TimeZoneManager.Instance.SetDeviceLocationName(locationName);
-        }
-
-        [Command(2)]
-        // GetTotalLocationNameCount() -> u32
-        public ResultCode GetTotalLocationNameCount(ServiceCtx context)
-        {
-            context.ResponseData.Write(TimeZoneManager.Instance.GetTotalLocationNameCount());
-
-            return ResultCode.Success;
-        }
-
-        [Command(3)]
-        // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
-        public ResultCode LoadLocationNameList(ServiceCtx context)
-        {
-            uint index          = context.RequestData.ReadUInt32();
-            long bufferPosition = context.Request.ReceiveBuff[0].Position;
-            long bufferSize     = context.Request.ReceiveBuff[0].Size;
-
-            ResultCode errorCode = TimeZoneManager.Instance.LoadLocationNameList(index, out string[] locationNameArray, (uint)bufferSize / 0x24);
-
-            if (errorCode == 0)
-            {
-                uint offset = 0;
-
-                foreach (string locationName in locationNameArray)
-                {
-                    int padding = 0x24 - locationName.Length;
-
-                    if (padding < 0)
-                    {
-                        return ResultCode.LocationNameTooLong;
-                    }
-
-                    context.Memory.WriteBytes(bufferPosition + offset, Encoding.ASCII.GetBytes(locationName));
-                    MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + locationName.Length, padding);
-
-                    offset += 0x24;
-                }
-
-                context.ResponseData.Write((uint)locationNameArray.Length);
-            }
-
-            return errorCode;
-        }
-
-        [Command(4)]
-        // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
-        public ResultCode LoadTimeZoneRule(ServiceCtx context)
-        {
-            long bufferPosition = context.Request.ReceiveBuff[0].Position;
-            long bufferSize     = context.Request.ReceiveBuff[0].Size;
-
-            if (bufferSize != 0x4000)
-            {
-                // TODO: find error code here
-                Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)");
-
-                throw new InvalidOperationException();
-            }
-
-
-            string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
-
-            ResultCode resultCode = TimeZoneManager.Instance.LoadTimeZoneRules(out TimeZoneRule rules, locationName);
-
-            // Write TimeZoneRule if success
-            if (resultCode == 0)
-            {
-                MemoryHelper.Write(context.Memory, bufferPosition, rules);
-            }
-
-            return resultCode;
-        }
-
-        [Command(100)]
-        // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
-        public ResultCode ToCalendarTime(ServiceCtx context)
-        {
-            long posixTime      = context.RequestData.ReadInt64();
-            long bufferPosition = context.Request.SendBuff[0].Position;
-            long bufferSize     = context.Request.SendBuff[0].Size;
-
-            if (bufferSize != 0x4000)
-            {
-                // TODO: find error code here
-                Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)");
-
-                throw new InvalidOperationException();
-            }
-
-            TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition);
-
-            ResultCode resultCode = TimeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar);
-
-            if (resultCode == 0)
-            {
-                context.ResponseData.WriteStruct(calendar);
-            }
-
-            return resultCode;
-        }
-
-        [Command(101)]
-        // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
-        public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context)
-        {
-            long posixTime = context.RequestData.ReadInt64();
-
-            ResultCode resultCode = TimeZoneManager.Instance.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar);
-
-            if (resultCode == 0)
-            {
-                context.ResponseData.WriteStruct(calendar);
-            }
-
-            return resultCode;
-        }
-
-        [Command(201)]
-        // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
-        public ResultCode ToPosixTime(ServiceCtx context)
-        {
-            long inBufferPosition = context.Request.SendBuff[0].Position;
-            long inBufferSize     = context.Request.SendBuff[0].Size;
-
-            CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
-
-            if (inBufferSize != 0x4000)
-            {
-                // TODO: find error code here
-                Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{inBufferSize:x} (expected 0x4000)");
-
-                throw new InvalidOperationException();
-            }
-
-            TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition);
-
-            ResultCode resultCode = TimeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime);
-
-            if (resultCode == 0)
-            {
-                long outBufferPosition = context.Request.RecvListBuff[0].Position;
-                long outBufferSize     = context.Request.RecvListBuff[0].Size;
-
-                context.Memory.WriteInt64(outBufferPosition, posixTime);
-                context.ResponseData.Write(1);
-            }
-
-            return resultCode;
-        }
-
-        [Command(202)]
-        // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
-        public ResultCode ToPosixTimeWithMyRule(ServiceCtx context)
-        {
-            CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
-
-            ResultCode resultCode = TimeZoneManager.Instance.ToPosixTimeWithMyRules(calendarTime, out long posixTime);
-
-            if (resultCode == 0)
-            {
-                long outBufferPosition = context.Request.RecvListBuff[0].Position;
-                long outBufferSize     = context.Request.RecvListBuff[0].Size;
-
-                context.Memory.WriteInt64(outBufferPosition, posixTime);
-
-                // There could be only one result on one calendar as leap seconds aren't supported.
-                context.ResponseData.Write(1);
-            }
-
-            return resultCode;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs
new file mode 100644
index 00000000..7acb0257
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForGlue.cs
@@ -0,0 +1,142 @@
+using ARMeilleure.Memory;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Time.TimeZone;
+using System;
+using System.Text;
+
+namespace Ryujinx.HLE.HOS.Services.Time.StaticService
+{
+    class ITimeZoneServiceForGlue : IpcService
+    {
+        private TimeZoneContentManager _timeZoneContentManager;
+        private ITimeZoneServiceForPsc _inner;
+        private bool                   _writePermission;
+
+        public ITimeZoneServiceForGlue(TimeZoneContentManager timeZoneContentManager, bool writePermission)
+        {
+            _timeZoneContentManager = timeZoneContentManager;
+            _writePermission        = writePermission;
+            _inner                  = new ITimeZoneServiceForPsc(timeZoneContentManager.Manager, writePermission);
+        }
+
+        [Command(0)]
+        // GetDeviceLocationName() -> nn::time::LocationName
+        public ResultCode GetDeviceLocationName(ServiceCtx context)
+        {
+            return _inner.GetDeviceLocationName(context);
+        }
+
+        [Command(1)]
+        // SetDeviceLocationName(nn::time::LocationName)
+        public ResultCode SetDeviceLocationName(ServiceCtx context)
+        {
+            if (!_writePermission)
+            {
+                return ResultCode.PermissionDenied;
+            }
+
+            string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
+
+            return _timeZoneContentManager.SetDeviceLocationName(locationName);
+        }
+
+        [Command(2)]
+        // GetTotalLocationNameCount() -> u32
+        public ResultCode GetTotalLocationNameCount(ServiceCtx context)
+        {
+            return _inner.GetTotalLocationNameCount(context);
+        }
+
+        [Command(3)]
+        // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
+        public ResultCode LoadLocationNameList(ServiceCtx context)
+        {
+            uint index          = context.RequestData.ReadUInt32();
+            long bufferPosition = context.Request.ReceiveBuff[0].Position;
+            long bufferSize     = context.Request.ReceiveBuff[0].Size;
+
+            ResultCode errorCode = _timeZoneContentManager.LoadLocationNameList(index, out string[] locationNameArray, (uint)bufferSize / 0x24);
+
+            if (errorCode == 0)
+            {
+                uint offset = 0;
+
+                foreach (string locationName in locationNameArray)
+                {
+                    int padding = 0x24 - locationName.Length;
+
+                    if (padding < 0)
+                    {
+                        return ResultCode.LocationNameTooLong;
+                    }
+
+                    context.Memory.WriteBytes(bufferPosition + offset, Encoding.ASCII.GetBytes(locationName));
+                    MemoryHelper.FillWithZeros(context.Memory, bufferPosition + offset + locationName.Length, padding);
+
+                    offset += 0x24;
+                }
+
+                context.ResponseData.Write((uint)locationNameArray.Length);
+            }
+
+            return errorCode;
+        }
+
+        [Command(4)]
+        // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
+        public ResultCode LoadTimeZoneRule(ServiceCtx context)
+        {
+            long bufferPosition = context.Request.ReceiveBuff[0].Position;
+            long bufferSize     = context.Request.ReceiveBuff[0].Size;
+
+            if (bufferSize != 0x4000)
+            {
+                // TODO: find error code here
+                Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)");
+
+                throw new InvalidOperationException();
+            }
+
+            string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
+
+            ResultCode resultCode = _timeZoneContentManager.LoadTimeZoneRule(out TimeZoneRule rules, locationName);
+
+            // Write TimeZoneRule if success
+            if (resultCode == ResultCode.Success)
+            {
+                MemoryHelper.Write(context.Memory, bufferPosition, rules);
+            }
+
+            return resultCode;
+        }
+
+        [Command(100)]
+        // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
+        public ResultCode ToCalendarTime(ServiceCtx context)
+        {
+            return _inner.ToCalendarTime(context);
+        }
+
+        [Command(101)]
+        // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
+        public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context)
+        {
+            return _inner.ToCalendarTimeWithMyRule(context);
+        }
+
+        [Command(201)]
+        // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
+        public ResultCode ToPosixTime(ServiceCtx context)
+        {
+            return _inner.ToPosixTime(context);
+        }
+
+        [Command(202)]
+        // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
+        public ResultCode ToPosixTimeWithMyRule(ServiceCtx context)
+        {
+            return _inner.ToPosixTimeWithMyRule(context);
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs
new file mode 100644
index 00000000..ed31fe7c
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/StaticService/ITimeZoneServiceForPsc.cs
@@ -0,0 +1,294 @@
+using ARMeilleure.Memory;
+using Ryujinx.Common;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Time.Clock;
+using Ryujinx.HLE.HOS.Services.Time.TimeZone;
+using Ryujinx.HLE.Utilities;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+
+namespace Ryujinx.HLE.HOS.Services.Time.StaticService
+{
+    class ITimeZoneServiceForPsc : IpcService
+    {
+        private TimeZoneManager _timeZoneManager;
+        private bool            _writePermission;
+
+        public ITimeZoneServiceForPsc(TimeZoneManager timeZoneManager, bool writePermission)
+        {
+            _timeZoneManager = timeZoneManager;
+            _writePermission = writePermission;
+        }
+
+        [Command(0)]
+        // GetDeviceLocationName() -> nn::time::LocationName
+        public ResultCode GetDeviceLocationName(ServiceCtx context)
+        {
+            ResultCode result = _timeZoneManager.GetDeviceLocationName(out string deviceLocationName);
+
+            if (result == ResultCode.Success)
+            {
+                WriteLocationName(context, deviceLocationName);
+            }
+
+            return result;
+        }
+
+        [Command(1)]
+        // SetDeviceLocationName(nn::time::LocationName)
+        public ResultCode SetDeviceLocationName(ServiceCtx context)
+        {
+            if (!_writePermission)
+            {
+                return ResultCode.PermissionDenied;
+            }
+            
+            return ResultCode.NotImplemented;
+        }
+
+        [Command(2)]
+        // GetTotalLocationNameCount() -> u32
+        public ResultCode GetTotalLocationNameCount(ServiceCtx context)
+        {
+            ResultCode result = _timeZoneManager.GetTotalLocationNameCount(out uint totalLocationNameCount);
+
+            if (result == ResultCode.Success)
+            {
+                context.ResponseData.Write(totalLocationNameCount);
+            }
+
+            return ResultCode.Success;
+        }
+
+        [Command(3)]
+        // LoadLocationNameList(u32 index) -> (u32 outCount, buffer<nn::time::LocationName, 6>)
+        public ResultCode LoadLocationNameList(ServiceCtx context)
+        {
+            return ResultCode.NotImplemented;
+        }
+
+        [Command(4)]
+        // LoadTimeZoneRule(nn::time::LocationName locationName) -> buffer<nn::time::TimeZoneRule, 0x16>
+        public ResultCode LoadTimeZoneRule(ServiceCtx context)
+        {
+            return ResultCode.NotImplemented;
+        }
+
+        [Command(5)] // 2.0.0+
+        // GetTimeZoneRuleVersion() -> nn::time::TimeZoneRuleVersion
+        public ResultCode GetTimeZoneRuleVersion(ServiceCtx context)
+        {
+            ResultCode result = _timeZoneManager.GetTimeZoneRuleVersion(out UInt128 timeZoneRuleVersion);
+
+            if (result == ResultCode.Success)
+            {
+                context.ResponseData.WriteStruct(timeZoneRuleVersion);
+            }
+
+            return result;
+        }
+
+        [Command(6)] // 5.0.0+
+        // GetDeviceLocationNameAndUpdatedTime() -> (nn::time::LocationName, nn::time::SteadyClockTimePoint)
+        public ResultCode GetDeviceLocationNameAndUpdatedTime(ServiceCtx context)
+        {
+            ResultCode result = _timeZoneManager.GetDeviceLocationName(out string deviceLocationName);
+
+            if (result == ResultCode.Success)
+            {
+                result = _timeZoneManager.GetUpdatedTime(out SteadyClockTimePoint timeZoneUpdateTimePoint);
+
+                if (result == ResultCode.Success)
+                {
+                    WriteLocationName(context, deviceLocationName);
+
+                    // Skip padding
+                    context.ResponseData.BaseStream.Position += 0x4;
+
+                    context.ResponseData.WriteStruct(timeZoneUpdateTimePoint);
+                }
+            }
+
+            return result;
+        }
+
+        [Command(7)] // 9.0.0+
+        // SetDeviceLocationNameWithTimeZoneRule(nn::time::LocationName locationName, buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary)
+        public ResultCode SetDeviceLocationNameWithTimeZoneRule(ServiceCtx context)
+        {
+            if (!_writePermission)
+            {
+                return ResultCode.PermissionDenied;
+            }
+
+            (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
+
+            string locationName = Encoding.ASCII.GetString(context.RequestData.ReadBytes(0x24)).TrimEnd('\0');
+
+            ResultCode result;
+
+            using (MemoryStream timeZoneBinaryStream = new MemoryStream(context.Memory.ReadBytes(bufferPosition, bufferSize)))
+            {
+                result = _timeZoneManager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream);
+            }
+
+            return result;
+        }
+
+        [Command(8)] // 9.0.0+
+        // ParseTimeZoneBinary(buffer<nn::time::TimeZoneBinary, 0x21> timeZoneBinary) -> buffer<nn::time::TimeZoneRule, 0x16>
+        public ResultCode ParseTimeZoneBinary(ServiceCtx context)
+        {
+            (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x21();
+
+            long timeZoneRuleBufferPosition = context.Request.ReceiveBuff[0].Position;
+            long timeZoneRuleBufferSize     = context.Request.ReceiveBuff[0].Size;
+
+            if (timeZoneRuleBufferSize != 0x4000)
+            {
+                // TODO: find error code here
+                Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{timeZoneRuleBufferSize:x} (expected 0x4000)");
+
+                throw new InvalidOperationException();
+            }
+
+            ResultCode result;
+
+            using (MemoryStream timeZoneBinaryStream = new MemoryStream(context.Memory.ReadBytes(bufferPosition, bufferSize)))
+            {
+                result = _timeZoneManager.ParseTimeZoneRuleBinary(out TimeZoneRule timeZoneRule, timeZoneBinaryStream);
+
+                if (result == ResultCode.Success)
+                {
+                    MemoryHelper.Write(context.Memory, timeZoneRuleBufferPosition, timeZoneRule);
+                }
+            }
+
+            return result;
+        }
+
+        [Command(20)] // 9.0.0+
+        // GetDeviceLocationNameOperationEventReadableHandle() -> handle<copy>
+        public ResultCode GetDeviceLocationNameOperationEventReadableHandle(ServiceCtx context)
+        {
+            return ResultCode.NotImplemented;
+        }
+
+        [Command(100)]
+        // ToCalendarTime(nn::time::PosixTime time, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
+        public ResultCode ToCalendarTime(ServiceCtx context)
+        {
+            long posixTime      = context.RequestData.ReadInt64();
+            long bufferPosition = context.Request.SendBuff[0].Position;
+            long bufferSize     = context.Request.SendBuff[0].Size;
+
+            if (bufferSize != 0x4000)
+            {
+                // TODO: find error code here
+                Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{bufferSize:x} (expected 0x4000)");
+
+                throw new InvalidOperationException();
+            }
+
+            TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, bufferPosition);
+
+            ResultCode resultCode = _timeZoneManager.ToCalendarTime(rules, posixTime, out CalendarInfo calendar);
+
+            if (resultCode == 0)
+            {
+                context.ResponseData.WriteStruct(calendar);
+            }
+
+            return resultCode;
+        }
+
+        [Command(101)]
+        // ToCalendarTimeWithMyRule(nn::time::PosixTime) -> (nn::time::CalendarTime, nn::time::sf::CalendarAdditionalInfo)
+        public ResultCode ToCalendarTimeWithMyRule(ServiceCtx context)
+        {
+            long posixTime = context.RequestData.ReadInt64();
+
+            ResultCode resultCode = _timeZoneManager.ToCalendarTimeWithMyRules(posixTime, out CalendarInfo calendar);
+
+            if (resultCode == 0)
+            {
+                context.ResponseData.WriteStruct(calendar);
+            }
+
+            return resultCode;
+        }
+
+        [Command(201)]
+        // ToPosixTime(nn::time::CalendarTime calendarTime, buffer<nn::time::TimeZoneRule, 0x15> rules) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
+        public ResultCode ToPosixTime(ServiceCtx context)
+        {
+            long inBufferPosition = context.Request.SendBuff[0].Position;
+            long inBufferSize     = context.Request.SendBuff[0].Size;
+
+            CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
+
+            if (inBufferSize != 0x4000)
+            {
+                // TODO: find error code here
+                Logger.PrintError(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{inBufferSize:x} (expected 0x4000)");
+
+                throw new InvalidOperationException();
+            }
+
+            TimeZoneRule rules = MemoryHelper.Read<TimeZoneRule>(context.Memory, inBufferPosition);
+
+            ResultCode resultCode = _timeZoneManager.ToPosixTime(rules, calendarTime, out long posixTime);
+
+            if (resultCode == 0)
+            {
+                long outBufferPosition = context.Request.RecvListBuff[0].Position;
+                long outBufferSize     = context.Request.RecvListBuff[0].Size;
+
+                context.Memory.WriteInt64(outBufferPosition, posixTime);
+                context.ResponseData.Write(1);
+            }
+
+            return resultCode;
+        }
+
+        [Command(202)]
+        // ToPosixTimeWithMyRule(nn::time::CalendarTime calendarTime) -> (u32 outCount, buffer<nn::time::PosixTime, 0xa>)
+        public ResultCode ToPosixTimeWithMyRule(ServiceCtx context)
+        {
+            CalendarTime calendarTime = context.RequestData.ReadStruct<CalendarTime>();
+
+            ResultCode resultCode = _timeZoneManager.ToPosixTimeWithMyRules(calendarTime, out long posixTime);
+
+            if (resultCode == 0)
+            {
+                long outBufferPosition = context.Request.RecvListBuff[0].Position;
+                long outBufferSize     = context.Request.RecvListBuff[0].Size;
+
+                context.Memory.WriteInt64(outBufferPosition, posixTime);
+
+                // There could be only one result on one calendar as leap seconds aren't supported.
+                context.ResponseData.Write(1);
+            }
+
+            return resultCode;
+        }
+
+        private void WriteLocationName(ServiceCtx context, string locationName)
+        {
+            char[] locationNameArray = locationName.ToCharArray();
+
+            int padding = 0x24 - locationNameArray.Length;
+
+            Debug.Assert(padding >= 0, "LocationName exceeded limit (0x24 bytes)");
+
+            context.ResponseData.Write(locationNameArray);
+
+            for (int index = 0; index < padding; index++)
+            {
+                context.ResponseData.Write((byte)0);
+            }
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs
new file mode 100644
index 00000000..7c5d7163
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/TimeManager.cs
@@ -0,0 +1,184 @@
+using System;
+using System.IO;
+using Ryujinx.HLE.Exceptions;
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Time.Clock;
+using Ryujinx.HLE.HOS.Services.Time.TimeZone;
+using Ryujinx.HLE.Utilities;
+
+namespace Ryujinx.HLE.HOS.Services.Time
+{
+    class TimeManager
+    {
+        private static TimeManager _instance;
+
+        public static TimeManager Instance
+        {
+            get
+            {
+                if (_instance == null)
+                {
+                    _instance = new TimeManager();
+                }
+
+                return _instance;
+            }
+        }
+
+        public StandardSteadyClockCore                  StandardSteadyClock         { get; }
+        public TickBasedSteadyClockCore                 TickBasedSteadyClock        { get; }
+        public StandardLocalSystemClockCore             StandardLocalSystemClock    { get; }
+        public StandardNetworkSystemClockCore           StandardNetworkSystemClock  { get; }
+        public StandardUserSystemClockCore              StandardUserSystemClock     { get; }
+        public TimeZoneContentManager                   TimeZone                    { get; }
+        public EphemeralNetworkSystemClockCore          EphemeralNetworkSystemClock { get; }
+        public TimeSharedMemory                         SharedMemory                { get; }
+        public LocalSystemClockContextWriter            LocalClockContextWriter     { get; }
+        public NetworkSystemClockContextWriter          NetworkClockContextWriter   { get; }
+        public EphemeralNetworkSystemClockContextWriter EphemeralClockContextWriter { get; }
+
+        // TODO: 9.0.0+ power states and alarms
+
+        public TimeManager()
+        {
+            StandardSteadyClock         = new StandardSteadyClockCore();
+            TickBasedSteadyClock        = new TickBasedSteadyClockCore();
+            StandardLocalSystemClock    = new StandardLocalSystemClockCore(StandardSteadyClock);
+            StandardNetworkSystemClock  = new StandardNetworkSystemClockCore(StandardSteadyClock);
+            StandardUserSystemClock     = new StandardUserSystemClockCore(StandardLocalSystemClock, StandardNetworkSystemClock);
+            TimeZone                    = new TimeZoneContentManager();
+            EphemeralNetworkSystemClock = new EphemeralNetworkSystemClockCore(StandardSteadyClock);
+            SharedMemory                = new TimeSharedMemory();
+            LocalClockContextWriter     = new LocalSystemClockContextWriter(SharedMemory);
+            NetworkClockContextWriter   = new NetworkSystemClockContextWriter(SharedMemory);
+            EphemeralClockContextWriter = new EphemeralNetworkSystemClockContextWriter();
+        }
+
+        public void Initialize(Switch device, Horizon system, KSharedMemory sharedMemory, long timeSharedMemoryAddress, int timeSharedMemorySize)
+        {
+            SharedMemory.Initialize(device, sharedMemory, timeSharedMemoryAddress, timeSharedMemorySize);
+
+            // Here we use system on purpose as device. System isn't initialized at this point.
+            StandardUserSystemClock.CreateAutomaticCorrectionEvent(system);
+        }
+
+        public void InitializeTimeZone(Switch device)
+        {
+            TimeZone.Initialize(this, device);
+        }
+
+
+        public void SetupStandardSteadyClock(KThread thread, UInt128 clockSourceId, TimeSpanType setupValue, TimeSpanType internalOffset, TimeSpanType testOffset, bool isRtcResetDetected)
+        {
+            SetupInternalStandardSteadyClock(clockSourceId, setupValue, internalOffset, testOffset, isRtcResetDetected);
+
+            TimeSpanType currentTimePoint = StandardSteadyClock.GetCurrentRawTimePoint(thread);
+
+            SharedMemory.SetupStandardSteadyClock(thread, clockSourceId, currentTimePoint);
+
+            // TODO: propagate IPC late binding of "time:s" and "time:p"
+        }
+
+        private void SetupInternalStandardSteadyClock(UInt128 clockSourceId, TimeSpanType setupValue, TimeSpanType internalOffset, TimeSpanType testOffset, bool isRtcResetDetected)
+        {
+            StandardSteadyClock.SetClockSourceId(clockSourceId);
+            StandardSteadyClock.SetSetupValue(setupValue);
+            StandardSteadyClock.SetInternalOffset(internalOffset);
+            StandardSteadyClock.SetTestOffset(testOffset);
+
+            if (isRtcResetDetected)
+            {
+                StandardSteadyClock.SetRtcReset();
+            }
+
+            StandardSteadyClock.MarkInitialized();
+
+            // TODO: propagate IPC late binding of "time:s" and "time:p"
+        }
+
+        public void SetupStandardLocalSystemClock(KThread thread, SystemClockContext clockContext, long posixTime)
+        {
+            StandardLocalSystemClock.SetUpdateCallbackInstance(LocalClockContextWriter);
+
+            SteadyClockTimePoint currentTimePoint = StandardLocalSystemClock.GetSteadyClockCore().GetCurrentTimePoint(thread);
+            if (currentTimePoint.ClockSourceId == clockContext.SteadyTimePoint.ClockSourceId)
+            {
+                StandardLocalSystemClock.SetSystemClockContext(clockContext);
+            }
+            else
+            {
+                if (StandardLocalSystemClock.SetCurrentTime(thread, posixTime) != ResultCode.Success)
+                {
+                    throw new InternalServiceException("Cannot set current local time");
+                }
+            }
+
+            StandardLocalSystemClock.MarkInitialized();
+
+            // TODO: propagate IPC late binding of "time:s" and "time:p"
+        }
+
+        public void SetupStandardNetworkSystemClock(SystemClockContext clockContext, TimeSpanType sufficientAccuracy)
+        {
+            StandardNetworkSystemClock.SetUpdateCallbackInstance(NetworkClockContextWriter);
+
+            if (StandardNetworkSystemClock.SetSystemClockContext(clockContext) != ResultCode.Success)
+            {
+                throw new InternalServiceException("Cannot set network SystemClockContext");
+            }
+
+            StandardNetworkSystemClock.SetStandardNetworkClockSufficientAccuracy(sufficientAccuracy);
+            StandardNetworkSystemClock.MarkInitialized();
+
+            // TODO: propagate IPC late binding of "time:s" and "time:p"
+        }
+
+        public void SetupTimeZoneManager(string locationName, SteadyClockTimePoint timeZoneUpdatedTimePoint, uint totalLocationNameCount, UInt128 timeZoneRuleVersion, Stream timeZoneBinaryStream)
+        {
+            if (TimeZone.Manager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream) != ResultCode.Success)
+            {
+                throw new InternalServiceException("Cannot set DeviceLocationName with a given TimeZoneBinary");
+            }
+
+            TimeZone.Manager.SetUpdatedTime(timeZoneUpdatedTimePoint, true);
+            TimeZone.Manager.SetTotalLocationNameCount(totalLocationNameCount);
+            TimeZone.Manager.SetTimeZoneRuleVersion(timeZoneRuleVersion);
+            TimeZone.Manager.MarkInitialized();
+
+            // TODO: propagate IPC late binding of "time:s" and "time:p"
+        }
+
+        public void SetupEphemeralNetworkSystemClock()
+        {
+            EphemeralNetworkSystemClock.SetUpdateCallbackInstance(EphemeralClockContextWriter);
+            EphemeralNetworkSystemClock.MarkInitialized();
+
+            // TODO: propagate IPC late binding of "time:s" and "time:p"
+        }
+
+        public void SetupStandardUserSystemClock(KThread thread, bool isAutomaticCorrectionEnabled, SteadyClockTimePoint steadyClockTimePoint)
+        {
+            if (StandardUserSystemClock.SetAutomaticCorrectionEnabled(thread, isAutomaticCorrectionEnabled) != ResultCode.Success)
+            {
+                throw new InternalServiceException("Cannot set automatic user time correction state");
+            }
+
+            StandardUserSystemClock.SetAutomaticCorrectionUpdatedTime(steadyClockTimePoint);
+            StandardUserSystemClock.MarkInitialized();
+
+            SharedMemory.SetAutomaticCorrectionEnabled(isAutomaticCorrectionEnabled);
+
+            // TODO: propagate IPC late binding of "time:s" and "time:p"
+        }
+
+        public void SetStandardSteadyClockRtcOffset(KThread thread, TimeSpanType rtcOffset)
+        {
+            StandardSteadyClock.SetSetupValue(rtcOffset);
+
+            TimeSpanType currentTimePoint = StandardSteadyClock.GetCurrentRawTimePoint(thread);
+
+            SharedMemory.SetSteadyClockRawTimePoint(thread, currentTimePoint);
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs b/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs
new file mode 100644
index 00000000..f714c662
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/TimeSharedMemory.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Ryujinx.HLE.HOS.Kernel.Memory;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.Time.Clock;
+using Ryujinx.HLE.HOS.Services.Time.Types;
+using Ryujinx.HLE.Utilities;
+
+namespace Ryujinx.HLE.HOS.Services.Time
+{
+    class TimeSharedMemory
+    {
+        private Switch        _device;
+        private KSharedMemory _sharedMemory;
+        private long          _timeSharedMemoryAddress;
+        private int           _timeSharedMemorySize;
+
+        private const uint SteadyClockContextOffset         = 0x00;
+        private const uint LocalSystemClockContextOffset    = 0x38;
+        private const uint NetworkSystemClockContextOffset  = 0x80;
+        private const uint AutomaticCorrectionEnabledOffset = 0xC8;
+
+        public void Initialize(Switch device, KSharedMemory sharedMemory, long timeSharedMemoryAddress, int timeSharedMemorySize)
+        {
+            _device                  = device;
+            _sharedMemory            = sharedMemory;
+            _timeSharedMemoryAddress = timeSharedMemoryAddress;
+            _timeSharedMemorySize    = timeSharedMemorySize;
+
+            // Clean the shared memory
+            _device.Memory.FillWithZeros(_timeSharedMemoryAddress, _timeSharedMemorySize);
+        }
+
+        public KSharedMemory GetSharedMemory()
+        {
+            return _sharedMemory;
+        }
+
+        public void SetupStandardSteadyClock(KThread thread, UInt128 clockSourceId, TimeSpanType currentTimePoint)
+        {
+            TimeSpanType ticksTimeSpan;
+
+            // As this may be called before the guest code, we support passing a null thread to make this api usable.
+            if (thread == null)
+            {
+                ticksTimeSpan = TimeSpanType.FromSeconds(0);
+            }
+            else
+            {
+                ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
+            }
+
+            SteadyClockContext context = new SteadyClockContext
+            {
+                InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds),
+                ClockSourceId  = clockSourceId
+            };
+
+            WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context);
+        }
+
+        public void SetAutomaticCorrectionEnabled(bool isAutomaticCorrectionEnabled)
+        {
+            // We convert the bool to byte here as a bool in C# takes 4 bytes...
+            WriteObjectToSharedMemory(AutomaticCorrectionEnabledOffset, 0, Convert.ToByte(isAutomaticCorrectionEnabled));
+        }
+
+        public void SetSteadyClockRawTimePoint(KThread thread, TimeSpanType currentTimePoint)
+        {
+            SteadyClockContext context       = ReadObjectFromSharedMemory<SteadyClockContext>(SteadyClockContextOffset, 4);
+            TimeSpanType       ticksTimeSpan = TimeSpanType.FromTicks(thread.Context.CntpctEl0, thread.Context.CntfrqEl0);
+
+            context.InternalOffset = (ulong)(currentTimePoint.NanoSeconds - ticksTimeSpan.NanoSeconds);
+
+            WriteObjectToSharedMemory(SteadyClockContextOffset, 4, context);
+        }
+
+        public void UpdateLocalSystemClockContext(SystemClockContext context)
+        {
+            WriteObjectToSharedMemory(LocalSystemClockContextOffset, 4, context);
+        }
+
+        public void UpdateNetworkSystemClockContext(SystemClockContext context)
+        {
+            WriteObjectToSharedMemory(NetworkSystemClockContextOffset, 4, context);
+        }
+
+        private T ReadObjectFromSharedMemory<T>(long offset, long padding)
+        {
+            long indexOffset  = _timeSharedMemoryAddress + offset;
+
+            T    result;
+            uint index;
+            uint possiblyNewIndex;
+
+            do
+            {
+                index = _device.Memory.ReadUInt32(indexOffset);
+
+                long objectOffset = indexOffset + 4 + padding + (index & 1) * Marshal.SizeOf<T>();
+
+                result = _device.Memory.ReadStruct<T>(objectOffset);
+
+                Thread.MemoryBarrier();
+
+                possiblyNewIndex = _device.Memory.ReadUInt32(indexOffset);
+            } while (index != possiblyNewIndex);
+
+            return result;
+        }
+
+        private void WriteObjectToSharedMemory<T>(long offset, long padding, T value)
+        {
+            long indexOffset  = _timeSharedMemoryAddress + offset;
+            uint newIndex     = _device.Memory.ReadUInt32(indexOffset) + 1;
+            long objectOffset = indexOffset + 4 + padding + (newIndex & 1) * Marshal.SizeOf<T>();
+
+            _device.Memory.WriteStruct(objectOffset, value);
+
+            Thread.MemoryBarrier();
+
+            _device.Memory.WriteUInt32(indexOffset, newIndex);
+        }
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs
index 3a98013e..b32a9795 100644
--- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZone.cs
@@ -903,7 +903,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
             return ParsePosixName(name.ToCharArray(), out outRules, false);
         }
 
-        internal static unsafe bool LoadTimeZoneRules(out TimeZoneRule outRules, Stream inputData)
+        internal static unsafe bool ParseTimeZoneBinary(out TimeZoneRule outRules, Stream inputData)
         {
             outRules = new TimeZoneRule
             {
@@ -1357,10 +1357,8 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
 
             int[] ip = MonthsLengths[IsLeap((int)year)];
 
-            while (dayOfYear >= ip[calendarTime.Month])
+            for (calendarTime.Month = 0; dayOfYear >= ip[calendarTime.Month]; ++calendarTime.Month)
             {
-                calendarTime.Month += 1;
-
                 dayOfYear -= ip[calendarTime.Month];
             }
 
@@ -1709,7 +1707,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
                 Time = new CalendarTime()
                 {
                     Year   = (short)calendarTime.Year,
-                    Month  = calendarTime.Month,
+                    Month  = (sbyte)(calendarTime.Month + 1),
                     Day    = calendarTime.Day,
                     Hour   = calendarTime.Hour,
                     Minute = calendarTime.Minute,
diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs
new file mode 100644
index 00000000..f02781b3
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs
@@ -0,0 +1,191 @@
+using LibHac.Fs;
+using LibHac.Fs.NcaUtils;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.HOS.Services.Time.Clock;
+using Ryujinx.HLE.Resource;
+using Ryujinx.HLE.Utilities;
+using System.Collections.Generic;
+using System.IO;
+
+using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
+
+namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
+{
+    class TimeZoneContentManager
+    {
+        private const long TimeZoneBinaryTitleId = 0x010000000000080E;
+
+        private Switch   _device;
+        private string[] _locationNameCache;
+
+        public TimeZoneManager Manager { get; private set; }
+
+        public TimeZoneContentManager()
+        {
+            Manager = new TimeZoneManager();
+        }
+
+        internal void Initialize(TimeManager timeManager, Switch device)
+        {
+            _device = device;
+
+            InitializeLocationNameCache();
+
+            SteadyClockTimePoint timeZoneUpdatedTimePoint = timeManager.StandardSteadyClock.GetCurrentTimePoint(null);
+
+            ResultCode result = GetTimeZoneBinary("UTC", out Stream timeZoneBinaryStream);
+
+            if (result == ResultCode.Success)
+            {
+                // TODO: Read TimeZoneVersion from sysarchive.
+                timeManager.SetupTimeZoneManager("UTC", timeZoneUpdatedTimePoint, (uint)_locationNameCache.Length, new UInt128(), timeZoneBinaryStream);
+            }
+            else
+            {
+                // In the case the user don't have the timezone system archive, we just mark the manager as initialized.
+                Manager.MarkInitialized();
+            }
+        }
+
+        private void InitializeLocationNameCache()
+        {
+            if (HasTimeZoneBinaryTitle())
+            {
+                using (IStorage ncaFileStream = new LocalStorage(_device.FileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open))
+                {
+                    Nca         nca              = new Nca(_device.System.KeySet, ncaFileStream);
+                    IFileSystem romfs            = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
+                    Stream      binaryListStream = romfs.OpenFile("binaryList.txt", OpenMode.Read).AsStream();
+
+                    StreamReader reader = new StreamReader(binaryListStream);
+
+                    List<string> locationNameList = new List<string>();
+
+                    string locationName;
+                    while ((locationName = reader.ReadLine()) != null)
+                    {
+                        locationNameList.Add(locationName);
+                    }
+
+                    _locationNameCache = locationNameList.ToArray();
+                }
+            }
+            else
+            {
+                _locationNameCache = new string[0];
+
+                Logger.PrintWarning(LogClass.ServiceTime, "TimeZoneBinary system title not found! TimeZone conversions will not work, provide the system archive to fix this warning. (See https://github.com/Ryujinx/Ryujinx#requirements for more informations)");
+            }
+        }
+
+        private bool IsLocationNameValid(string locationName)
+        {
+            foreach (string cachedLocationName in _locationNameCache)
+            {
+                if (cachedLocationName.Equals(locationName))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public ResultCode SetDeviceLocationName(string locationName)
+        {
+            ResultCode result = GetTimeZoneBinary(locationName, out Stream timeZoneBinaryStream);
+
+            if (result == ResultCode.Success)
+            {
+                result = Manager.SetDeviceLocationNameWithTimeZoneRule(locationName, timeZoneBinaryStream);
+            }
+
+            return result;
+        }
+
+        public ResultCode LoadLocationNameList(uint index, out string[] outLocationNameArray, uint maxLength)
+        {
+            List<string> locationNameList = new List<string>();
+
+            for (int i = 0; i < _locationNameCache.Length && i < maxLength; i++)
+            {
+                if (i < index)
+                {
+                    continue;
+                }
+
+                string locationName = _locationNameCache[i];
+
+                // If the location name is too long, error out.
+                if (locationName.Length > 0x24)
+                {
+                    outLocationNameArray = new string[0];
+
+                    return ResultCode.LocationNameTooLong;
+                }
+
+                locationNameList.Add(locationName);
+            }
+
+            outLocationNameArray = locationNameList.ToArray();
+
+            return ResultCode.Success;
+        }
+
+        public string GetTimeZoneBinaryTitleContentPath()
+        {
+            return _device.System.ContentManager.GetInstalledContentPath(TimeZoneBinaryTitleId, StorageId.NandSystem, ContentType.Data);
+        }
+
+        public bool HasTimeZoneBinaryTitle()
+        {
+            return !string.IsNullOrEmpty(GetTimeZoneBinaryTitleContentPath());
+        }
+
+        internal ResultCode GetTimeZoneBinary(string locationName, out Stream timeZoneBinaryStream)
+        {
+            timeZoneBinaryStream = null;
+
+            if (!IsLocationNameValid(locationName))
+            {
+                return ResultCode.TimeZoneNotFound;
+            }
+
+            using (IStorage ncaFileStream = new LocalStorage(_device.FileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open))
+            {
+                Nca         nca   = new Nca(_device.System.KeySet, ncaFileStream);
+                IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
+
+                timeZoneBinaryStream = romfs.OpenFile($"zoneinfo/{locationName}", OpenMode.Read).AsStream();
+            }
+
+            return ResultCode.Success;
+        }
+
+        internal ResultCode LoadTimeZoneRule(out TimeZoneRule outRules, string locationName)
+        {
+            outRules = new TimeZoneRule
+            {
+                Ats   = new long[TzMaxTimes],
+                Types = new byte[TzMaxTimes],
+                Ttis  = new TimeTypeInfo[TzMaxTypes],
+                Chars = new char[TzCharsArraySize]
+            };
+
+            if (!HasTimeZoneBinaryTitle())
+            {
+                throw new InvalidSystemResourceException($"TimeZoneBinary system title not found! Please provide it. (See https://github.com/Ryujinx/Ryujinx#requirements for more informations)");
+            }
+
+            ResultCode result = GetTimeZoneBinary(locationName, out Stream timeZoneBinaryStream);
+
+            if (result == ResultCode.Success)
+            {
+                result = Manager.ParseTimeZoneRuleBinary(out outRules, timeZoneBinaryStream);
+            }
+
+            return result;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs
index 2497f6a3..1a80365a 100644
--- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneManager.cs
@@ -1,50 +1,28 @@
-using LibHac.Fs;
-using LibHac.Fs.NcaUtils;
-using Ryujinx.Common.Logging;
-using Ryujinx.HLE.FileSystem;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
+using Ryujinx.HLE.HOS.Services.Time.Clock;
+using Ryujinx.HLE.Utilities;
 using System.IO;
-using TimeZoneConverter;
-using TimeZoneConverter.Posix;
-
 using static Ryujinx.HLE.HOS.Services.Time.TimeZone.TimeZoneRule;
 
 namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
 {
-    public sealed class TimeZoneManager
+    class TimeZoneManager
     {
-        private const long TimeZoneBinaryTitleId = 0x010000000000080E;
+        private bool                 _isInitialized;
+        private TimeZoneRule         _myRules;
+        private string               _deviceLocationName;
+        private UInt128              _timeZoneRuleVersion;
+        private uint                 _totalLocationNameCount;
+        private SteadyClockTimePoint _timeZoneUpdateTimePoint;
+        private object               _lock;
 
-        private static TimeZoneManager instance;
-
-        private static object instanceLock = new object();
-
-        private Switch       _device;
-        private TimeZoneRule _myRules;
-        private string       _deviceLocationName;
-        private string[]     _locationNameCache;
-
-        public static TimeZoneManager Instance
+        public TimeZoneManager()
         {
-            get
-            {
-                lock (instanceLock)
-                {
-                    if (instance == null)
-                    {
-                        instance = new TimeZoneManager();
-                    }
+            _isInitialized       = false;
+            _deviceLocationName  = "UTC";
+            _timeZoneRuleVersion = new UInt128();
+            _lock                = new object();
 
-                    return instance;
-                }
-            }
-        }
-
-        TimeZoneManager()
-        {
-            // Empty rules (UTC)
+            // Empty rules
             _myRules = new TimeZoneRule
             {
                 Ats   = new long[TzMaxTimes],
@@ -53,236 +31,237 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
                 Chars = new char[TzCharsArraySize]
             };
 
-            _deviceLocationName = "UTC";
+            _timeZoneUpdateTimePoint = SteadyClockTimePoint.GetRandom();
         }
 
-        internal void Initialize(Switch device)
+        public bool IsInitialized()
         {
-            _device = device;
+            bool res;
 
-            InitializeLocationNameCache();
-        }
-
-        private void InitializeLocationNameCache()
-        {
-            if (HasTimeZoneBinaryTitle())
+            lock (_lock)
             {
-                using (IStorage ncaFileStream = new LocalStorage(_device.FileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open))
+                res = _isInitialized;
+            }
+
+            return res;
+        }
+
+        public void MarkInitialized()
+        {
+            lock (_lock)
+            {
+                _isInitialized = true;
+            }
+        }
+
+        public ResultCode GetDeviceLocationName(out string deviceLocationName)
+        {
+            ResultCode result = ResultCode.UninitializedClock;
+
+            deviceLocationName = null;
+
+            lock (_lock)
+            {
+                if (_isInitialized)
                 {
-                    Nca         nca              = new Nca(_device.System.KeySet, ncaFileStream);
-                    IFileSystem romfs            = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
-                    Stream      binaryListStream = romfs.OpenFile("binaryList.txt", OpenMode.Read).AsStream();
-
-                    StreamReader reader = new StreamReader(binaryListStream);
-
-                    List<string> locationNameList = new List<string>();
-
-                    string locationName;
-                    while ((locationName = reader.ReadLine()) != null)
-                    {
-                        locationNameList.Add(locationName);
-                    }
-
-                    _locationNameCache = locationNameList.ToArray();
+                    deviceLocationName = _deviceLocationName;
+                    result             = ResultCode.Success;
                 }
             }
-            else
-            {
-                ReadOnlyCollection<TimeZoneInfo> timeZoneInfos = TimeZoneInfo.GetSystemTimeZones();
-                _locationNameCache = new string[timeZoneInfos.Count];
 
-                int i = 0;
-
-                foreach (TimeZoneInfo timeZoneInfo in timeZoneInfos)
-                {
-                    bool needConversion = TZConvert.TryWindowsToIana(timeZoneInfo.Id, out string convertedName);
-                    if (needConversion)
-                    {
-                        _locationNameCache[i] = convertedName;
-                    }
-                    else
-                    {
-                        _locationNameCache[i] = timeZoneInfo.Id;
-                    }
-                    i++;
-                }
-
-                // As we aren't using the system archive, "UTC" might not exist on the host system.
-                // Load from C# TimeZone APIs UTC id.
-                string utcId             = TimeZoneInfo.Utc.Id;
-                bool   utcNeedConversion = TZConvert.TryWindowsToIana(utcId, out string utcConvertedName);
-                if (utcNeedConversion)
-                {
-                    utcId = utcConvertedName;
-                }
-
-                _deviceLocationName = utcId;
-            }
+            return result;
         }
 
-        private bool IsLocationNameValid(string locationName)
+        public ResultCode SetDeviceLocationNameWithTimeZoneRule(string locationName, Stream timeZoneBinaryStream)
         {
-            foreach (string cachedLocationName in _locationNameCache)
+            ResultCode result = ResultCode.TimeZoneConversionFailed;
+
+            lock (_lock)
             {
-                if (cachedLocationName.Equals(locationName))
+                bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out TimeZoneRule rules, timeZoneBinaryStream);
+
+                if (timeZoneConversionSuccess)
                 {
-                    return true;
+                    _deviceLocationName = locationName;
+                    _myRules            = rules;
+                    result              = ResultCode.Success;
                 }
             }
-            return false;
+
+            return result;
         }
 
-        public string GetDeviceLocationName()
+        public void SetTotalLocationNameCount(uint totalLocationNameCount)
         {
-            return _deviceLocationName;
-        }
-
-        public ResultCode SetDeviceLocationName(string locationName)
-        {
-            ResultCode resultCode = LoadTimeZoneRules(out TimeZoneRule rules, locationName);
-
-            if (resultCode == 0)
+            lock (_lock)
             {
-                _myRules            = rules;
-                _deviceLocationName = locationName;
+                _totalLocationNameCount = totalLocationNameCount;
             }
-
-            return resultCode;
         }
 
-        public ResultCode LoadLocationNameList(uint index, out string[] outLocationNameArray, uint maxLength)
+        public ResultCode GetTotalLocationNameCount(out uint totalLocationNameCount)
         {
-            List<string> locationNameList = new List<string>();
+            ResultCode result = ResultCode.UninitializedClock;
 
-            for (int i = 0; i < _locationNameCache.Length && i < maxLength; i++)
+            totalLocationNameCount = 0;
+
+            lock (_lock)
             {
-                if (i < index)
+                if (_isInitialized)
                 {
-                    continue;
-                }
-
-                string locationName = _locationNameCache[i];
-
-                // If the location name is too long, error out.
-                if (locationName.Length > 0x24)
-                {
-                    outLocationNameArray = new string[0];
-
-                    return ResultCode.LocationNameTooLong;
-                }
-
-                locationNameList.Add(locationName);
-            }
-
-            outLocationNameArray = locationNameList.ToArray();
-
-            return ResultCode.Success;
-        }
-
-        public uint GetTotalLocationNameCount()
-        {
-            return (uint)_locationNameCache.Length;
-        }
-
-        public string GetTimeZoneBinaryTitleContentPath()
-        {
-            return _device.System.ContentManager.GetInstalledContentPath(TimeZoneBinaryTitleId, StorageId.NandSystem, ContentType.Data);
-        }
-
-        public bool HasTimeZoneBinaryTitle()
-        {
-            return !string.IsNullOrEmpty(GetTimeZoneBinaryTitleContentPath());
-        }
-
-        internal ResultCode LoadTimeZoneRules(out TimeZoneRule outRules, string locationName)
-        {
-            outRules = new TimeZoneRule
-            {
-                Ats   = new long[TzMaxTimes],
-                Types = new byte[TzMaxTimes],
-                Ttis  = new TimeTypeInfo[TzMaxTypes],
-                Chars = new char[TzCharsArraySize]
-            };
-
-            if (!IsLocationNameValid(locationName))
-            {
-                return ResultCode.TimeZoneNotFound;
-            }
-
-            if (!HasTimeZoneBinaryTitle())
-            {
-                // If the user doesn't have the system archives, we generate a POSIX rule string and parse it to generate a incomplete TimeZoneRule
-                // TODO: As for now not having system archives is fine, we should enforce the usage of system archives later.
-                Logger.PrintWarning(LogClass.ServiceTime, "TimeZoneBinary system archive not found! Time conversions will not be accurate!");
-                try
-                {
-                    TimeZoneInfo info      = TZConvert.GetTimeZoneInfo(locationName);
-                    string       posixRule = PosixTimeZone.FromTimeZoneInfo(info);
-
-                    if (!TimeZone.ParsePosixName(posixRule, out outRules))
-                    {
-                        return ResultCode.TimeZoneConversionFailed;
-                    }
-
-                    return 0;
-                }
-                catch (TimeZoneNotFoundException)
-                {
-                    Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {locationName})");
-
-                    return ResultCode.TimeZoneNotFound;
+                    totalLocationNameCount = _totalLocationNameCount;
+                    result                 = ResultCode.Success;
                 }
             }
-            else
+
+            return result;
+        }
+
+        public ResultCode SetUpdatedTime(SteadyClockTimePoint timeZoneUpdatedTimePoint, bool bypassUninitialized = false)
+        {
+            ResultCode result = ResultCode.UninitializedClock;
+
+            lock (_lock)
             {
-                using (IStorage ncaFileStream = new LocalStorage(_device.FileSystem.SwitchPathToSystemPath(GetTimeZoneBinaryTitleContentPath()), FileAccess.Read, FileMode.Open))
+                if (_isInitialized || bypassUninitialized)
                 {
-                    Nca         nca        = new Nca(_device.System.KeySet, ncaFileStream);
-                    IFileSystem romfs      = nca.OpenFileSystem(NcaSectionType.Data, _device.System.FsIntegrityCheckLevel);
-                    Stream      tzIfStream = romfs.OpenFile($"zoneinfo/{locationName}", OpenMode.Read).AsStream();
-
-                    if (!TimeZone.LoadTimeZoneRules(out outRules, tzIfStream))
-                    {
-                        return ResultCode.TimeZoneConversionFailed;
-                    }
+                    _timeZoneUpdateTimePoint = timeZoneUpdatedTimePoint;
+                    result                   = ResultCode.Success;
                 }
-
-                return 0;
             }
+
+            return result;
         }
 
-        internal ResultCode ToCalendarTimeWithMyRules(long time, out CalendarInfo calendar)
+        public ResultCode GetUpdatedTime(out SteadyClockTimePoint timeZoneUpdatedTimePoint)
         {
-            return ToCalendarTime(_myRules, time, out calendar);
-        }
+            ResultCode result;
 
-        internal static ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
-        {
-            ResultCode error = TimeZone.ToCalendarTime(rules, time, out calendar);
-
-            if (error != ResultCode.Success)
+            lock (_lock)
             {
-                return error;
+                if (_isInitialized)
+                {
+                    timeZoneUpdatedTimePoint = _timeZoneUpdateTimePoint;
+                    result                   = ResultCode.Success;
+                }
+                else
+                {
+                    timeZoneUpdatedTimePoint = SteadyClockTimePoint.GetRandom();
+                    result                   = ResultCode.UninitializedClock;
+                }
             }
 
-            return ResultCode.Success;
+            return result;
         }
 
-        internal ResultCode ToPosixTimeWithMyRules(CalendarTime calendarTime, out long posixTime)
+        public ResultCode ParseTimeZoneRuleBinary(out TimeZoneRule outRules, Stream timeZoneBinaryStream)
         {
-            return ToPosixTime(_myRules, calendarTime, out posixTime);
-        }
+            ResultCode result = ResultCode.Success;
 
-        internal static ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
-        {
-            ResultCode error = TimeZone.ToPosixTime(rules, calendarTime, out posixTime);
-
-            if (error != ResultCode.Success)
+            lock (_lock)
             {
-                return error;
+                bool timeZoneConversionSuccess = TimeZone.ParseTimeZoneBinary(out outRules, timeZoneBinaryStream);
+
+                if (!timeZoneConversionSuccess)
+                {
+                    result = ResultCode.TimeZoneConversionFailed;
+                }
             }
 
-            return ResultCode.Success;
+            return result;
+        }
+
+        public void SetTimeZoneRuleVersion(UInt128 timeZoneRuleVersion)
+        {
+            lock (_lock)
+            {
+                _timeZoneRuleVersion = timeZoneRuleVersion;
+            }
+        }
+
+        public ResultCode GetTimeZoneRuleVersion(out UInt128 timeZoneRuleVersion)
+        {
+            ResultCode result;
+
+            lock (_lock)
+            {
+                if (_isInitialized)
+                {
+                    timeZoneRuleVersion = _timeZoneRuleVersion;
+                    result              = ResultCode.Success;
+                }
+                else
+                {
+                    timeZoneRuleVersion = new UInt128();
+                    result              = ResultCode.UninitializedClock;
+                }
+            }
+
+            return result;
+        }
+
+        public ResultCode ToCalendarTimeWithMyRules(long time, out CalendarInfo calendar)
+        {
+            ResultCode result;
+
+            lock (_lock)
+            {
+                if (_isInitialized)
+                {
+                    result = ToCalendarTime(_myRules, time, out calendar);
+                }
+                else
+                {
+                    calendar = new CalendarInfo();
+                    result   = ResultCode.UninitializedClock;
+                }
+            }
+
+            return result;
+        }
+
+        public ResultCode ToCalendarTime(TimeZoneRule rules, long time, out CalendarInfo calendar)
+        {
+            ResultCode result;
+
+            lock (_lock)
+            {
+                result = TimeZone.ToCalendarTime(rules, time, out calendar);
+            }
+
+            return result;
+        }
+
+        public ResultCode ToPosixTimeWithMyRules(CalendarTime calendarTime, out long posixTime)
+        {
+            ResultCode result;
+
+            lock (_lock)
+            {
+                if (_isInitialized)
+                {
+                    result = ToPosixTime(_myRules, calendarTime, out posixTime);
+                }
+                else
+                {
+                    posixTime = 0;
+                    result    = ResultCode.UninitializedClock;
+                }
+            }
+
+            return result;
+        }
+
+        public ResultCode ToPosixTime(TimeZoneRule rules, CalendarTime calendarTime, out long posixTime)
+        {
+            ResultCode result;
+
+            lock (_lock)
+            {
+                result = TimeZone.ToPosixTime(rules, calendarTime, out posixTime);
+            }
+
+            return result;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs b/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs
new file mode 100644
index 00000000..4cf1fc99
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Time/Types/SteadyClockContext.cs
@@ -0,0 +1,12 @@
+using Ryujinx.HLE.Utilities;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Time.Types
+{
+    [StructLayout(LayoutKind.Sequential)]
+    struct SteadyClockContext
+    {
+        public ulong   InternalOffset;
+        public UInt128 ClockSourceId;
+    }
+}
diff --git a/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs b/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs
index 823c8288..3fcd3a14 100644
--- a/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs
+++ b/Ryujinx.HLE/HOS/Services/Time/Types/TimePermissions.cs
@@ -8,10 +8,15 @@ namespace Ryujinx.HLE.HOS.Services.Time
         LocalSystemClockWritableMask   = 0x1,
         UserSystemClockWritableMask    = 0x2,
         NetworkSystemClockWritableMask = 0x4,
-        UnknownPermissionMask          = 0x8,
+        TimeZoneWritableMask           = 0x8,
+        SteadyClockWritableMask        = 0x10,
+        BypassUninitialized            = 0x20,
 
-        User   = 0,
-        Applet = LocalSystemClockWritableMask | UserSystemClockWritableMask | UnknownPermissionMask,
-        System = NetworkSystemClockWritableMask
+        User         = 0,
+        Admin        = LocalSystemClockWritableMask | UserSystemClockWritableMask | TimeZoneWritableMask,
+        System       = NetworkSystemClockWritableMask,
+        SystemUpdate = BypassUninitialized,
+        Repair       = SteadyClockWritableMask,
+        Manufacture  = LocalSystemClockWritableMask | UserSystemClockWritableMask | NetworkSystemClockWritableMask | TimeZoneWritableMask | SteadyClockWritableMask
     }
 }