diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs
index 0955fb270f..795c3f7a70 100644
--- a/src/Ryujinx.Ava/AppHost.cs
+++ b/src/Ryujinx.Ava/AppHost.cs
@@ -671,7 +671,7 @@ namespace Ryujinx.Ava
 
             _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata =>
             {
-                appMetadata.LastPlayed = DateTime.UtcNow.ToString();
+                appMetadata.LastPlayed = DateTime.UtcNow;
             });
 
             return true;
diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml
index fa8ebf627b..227b4723bd 100644
--- a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml
+++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml
@@ -129,7 +129,7 @@
                                         TextWrapping="Wrap" />
                                     <TextBlock
                                         HorizontalAlignment="Stretch"
-                                        Text="{Binding LastPlayed}"
+                                        Text="{Binding LastPlayed, Converter={helpers:NullableDateTimeConverter}}"
                                         TextAlignment="Right"
                                         TextWrapping="Wrap" />
                                     <TextBlock
diff --git a/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs b/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs
new file mode 100644
index 0000000000..1d862de015
--- /dev/null
+++ b/src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs
@@ -0,0 +1,38 @@
+using Avalonia.Data.Converters;
+using Avalonia.Markup.Xaml;
+using Ryujinx.Ava.Common.Locale;
+using System;
+using System.Globalization;
+
+namespace Ryujinx.Ava.UI.Helpers
+{
+    internal class NullableDateTimeConverter : MarkupExtension, IValueConverter
+    {
+        private static readonly NullableDateTimeConverter _instance = new();
+
+        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            if (value == null)
+            {
+                return LocaleManager.Instance[LocaleKeys.Never];
+            }
+
+            if (value is DateTime dateTime)
+            {
+                return dateTime.ToLocalTime().ToString(culture);
+            }
+
+            throw new NotSupportedException();
+        }
+
+        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override object ProvideValue(IServiceProvider serviceProvider)
+        {
+            return _instance;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs b/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs
index 98caceb509..3627ada9a9 100644
--- a/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs
+++ b/src/Ryujinx.Ava/UI/Models/Generic/LastPlayedSortComparer.cs
@@ -1,4 +1,3 @@
-using Ryujinx.Ava.Common.Locale;
 using Ryujinx.Ui.App.Common;
 using System;
 using System.Collections.Generic;
@@ -14,20 +13,20 @@ namespace Ryujinx.Ava.UI.Models.Generic
 
         public int Compare(ApplicationData x, ApplicationData y)
         {
-            string aValue = x.LastPlayed;
-            string bValue = y.LastPlayed;
+            var aValue = x.LastPlayed;
+            var bValue = y.LastPlayed;
 
-            if (aValue == LocaleManager.Instance[LocaleKeys.Never])
+            if (!aValue.HasValue)
             {
-                aValue = DateTime.UnixEpoch.ToString();
+                aValue = DateTime.UnixEpoch;
             }
 
-            if (bValue == LocaleManager.Instance[LocaleKeys.Never])
+            if (!bValue.HasValue)
             {
-                bValue = DateTime.UnixEpoch.ToString();
+                bValue = DateTime.UnixEpoch;
             }
 
-            return (IsAscending ? 1 : -1) * DateTime.Compare(DateTime.Parse(bValue), DateTime.Parse(aValue));
+            return (IsAscending ? 1 : -1) * DateTime.Compare(bValue.Value, aValue.Value);
         }
     }
 }
\ No newline at end of file
diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
index 4db78afeb0..f8dd414358 100644
--- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs
@@ -1524,10 +1524,9 @@ namespace Ryujinx.Ava.UI.ViewModels
         {
             ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
             {
-                if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime))
+                if (appMetadata.LastPlayed.HasValue)
                 {
-                    double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
-
+                    double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
                     appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
                 }
             });
diff --git a/src/Ryujinx.Ui.Common/App/ApplicationData.cs b/src/Ryujinx.Ui.Common/App/ApplicationData.cs
index d9d3cf6853..f0aa40be26 100644
--- a/src/Ryujinx.Ui.Common/App/ApplicationData.cs
+++ b/src/Ryujinx.Ui.Common/App/ApplicationData.cs
@@ -10,27 +10,44 @@ using LibHac.Tools.FsSystem.NcaUtils;
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.FileSystem;
 using System;
+using System.Globalization;
 using System.IO;
+using System.Text.Json.Serialization;
 
 namespace Ryujinx.Ui.App.Common
 {
     public class ApplicationData
     {
-        public bool   Favorite      { get; set; }
-        public byte[] Icon          { get; set; }
-        public string TitleName     { get; set; }
-        public string TitleId       { get; set; }
-        public string Developer     { get; set; }
-        public string Version       { get; set; }
-        public string TimePlayed    { get; set; }
-        public double TimePlayedNum { get; set; }
-        public string LastPlayed    { get; set; }
-        public string FileExtension { get; set; }
-        public string FileSize      { get; set; }
-        public double FileSizeBytes { get; set; }
-        public string Path          { get; set; }
+        public bool      Favorite      { get; set; }
+        public byte[]    Icon          { get; set; }
+        public string    TitleName     { get; set; }
+        public string    TitleId       { get; set; }
+        public string    Developer     { get; set; }
+        public string    Version       { get; set; }
+        public string    TimePlayed    { get; set; }
+        public double    TimePlayedNum { get; set; }
+        public DateTime? LastPlayed    { get; set; }
+        public string    FileExtension { get; set; }
+        public string    FileSize      { get; set; }
+        public double    FileSizeBytes { get; set; }
+        public string    Path          { get; set; }
         public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
-        
+
+        [JsonIgnore]
+        public string LastPlayedString
+        {
+            get
+            {
+                if (!LastPlayed.HasValue)
+                {
+                    // TODO: maybe put localized string here instead of just "Never"
+                    return "Never";
+                }
+
+                return LastPlayed.Value.ToLocalTime().ToString(CultureInfo.CurrentCulture);
+            }
+        }
+
         public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath)
         {
             using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
diff --git a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs
index b7b57f1a29..0407036a02 100644
--- a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs
+++ b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs
@@ -414,21 +414,28 @@ namespace Ryujinx.Ui.App.Common
                     ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata =>
                     {
                         appMetadata.Title = titleName;
-                    });
 
-                    if (appMetadata.LastPlayed != "Never")
-                    {
-                        if (!DateTime.TryParse(appMetadata.LastPlayed, out _))
+                        if (appMetadata.LastPlayedOld == default || appMetadata.LastPlayed.HasValue)
                         {
-                            Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)");
+                            // Don't do the migration if last_played doesn't exist or last_played_utc already has a value.
+                            return;
+                        }
 
-                            appMetadata.LastPlayed = "Never";
+                        // Migrate from string-based last_played to DateTime-based last_played_utc.
+                        if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed))
+                        {
+                            Logger.Info?.Print(LogClass.Application, $"last_played found: \"{appMetadata.LastPlayedOld}\", migrating to last_played_utc");
+                            appMetadata.LastPlayed = lastPlayedOldParsed;
+
+                            // Migration successful: deleting last_played from the metadata file.
+                            appMetadata.LastPlayedOld = default;
                         }
                         else
                         {
-                            appMetadata.LastPlayed = appMetadata.LastPlayed[..^3];
+                            // Migration failed: emitting warning but leaving the unparsable value in the metadata file so the user can fix it.
+                            Logger.Warning?.Print(LogClass.Application, $"Last played string \"{appMetadata.LastPlayedOld}\" is invalid for current system culture, skipping (did current culture change?)");
                         }
-                    }
+                    });
 
                     ApplicationData data = new()
                     {
diff --git a/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs
index e19f7483fc..0abd4680da 100644
--- a/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs
+++ b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs
@@ -1,10 +1,19 @@
-namespace Ryujinx.Ui.App.Common
+using System;
+using System.Text.Json.Serialization;
+
+namespace Ryujinx.Ui.App.Common
 {
     public class ApplicationMetadata
     {
         public string Title { get; set; }
         public bool   Favorite   { get; set; }
         public double TimePlayed { get; set; }
-        public string LastPlayed { get; set; } = "Never";
+
+        [JsonPropertyName("last_played_utc")]
+        public DateTime? LastPlayed { get; set; } = null;
+
+        [JsonPropertyName("last_played")]
+        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+        public string LastPlayedOld { get; set; }
     }
 }
\ No newline at end of file
diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs
index f4cb3d0727..7cae62227d 100644
--- a/src/Ryujinx/Ui/MainWindow.cs
+++ b/src/Ryujinx/Ui/MainWindow.cs
@@ -876,7 +876,7 @@ namespace Ryujinx.Ui
 
                 _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata =>
                 {
-                    appMetadata.LastPlayed = DateTime.UtcNow.ToString();
+                    appMetadata.LastPlayed = DateTime.UtcNow;
                 });
             }
         }
@@ -1019,10 +1019,11 @@ namespace Ryujinx.Ui
             {
                 _applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
                 {
-                    DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed);
-                    double   sessionTimePlayed  = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
-
-                    appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
+                    if (appMetadata.LastPlayed.HasValue)
+                    {
+                        double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
+                        appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
+                    }
                 });
             }
         }
@@ -1089,7 +1090,7 @@ namespace Ryujinx.Ui
                     args.AppData.Developer,
                     args.AppData.Version,
                     args.AppData.TimePlayed,
-                    args.AppData.LastPlayed,
+                    args.AppData.LastPlayedString,
                     args.AppData.FileExtension,
                     args.AppData.FileSize,
                     args.AppData.Path,