diff --git a/src/Essentials/src/Preferences/Preferences.android.cs b/src/Essentials/src/Preferences/Preferences.android.cs index 25f62be8be1f..4a14d0974e9a 100644 --- a/src/Essentials/src/Preferences/Preferences.android.cs +++ b/src/Essentials/src/Preferences/Preferences.android.cs @@ -84,6 +84,9 @@ public void Set(string key, T value, string sharedName) case DateTime dt: editor.PutLong(key, dt.ToBinary()); break; + case DateTimeOffset dt: + editor.PutString(key, dt.ToString("O")); + break; } } editor.Apply(); @@ -143,6 +146,13 @@ public T Get(string key, T defaultValue, string sharedName) var encodedValue = sharedPreferences.GetLong(key, dt.ToBinary()); value = DateTime.FromBinary(encodedValue); break; + case DateTimeOffset dt: + var savedDateTimeOffset = sharedPreferences.GetString(key, dt.ToString("O")); + if (DateTimeOffset.TryParse(savedDateTimeOffset, out var dateTimeOffset)) + { + value = dateTimeOffset; + } + break; } } } diff --git a/src/Essentials/src/Preferences/Preferences.ios.tvos.watchos.macos.cs b/src/Essentials/src/Preferences/Preferences.ios.tvos.watchos.macos.cs index f932977d8a3a..8aad96794cb0 100644 --- a/src/Essentials/src/Preferences/Preferences.ios.tvos.watchos.macos.cs +++ b/src/Essentials/src/Preferences/Preferences.ios.tvos.watchos.macos.cs @@ -85,6 +85,9 @@ public void Set(string key, T value, string sharedName) var encodedDateTime = Convert.ToString(dt.ToBinary(), CultureInfo.InvariantCulture); userDefaults.SetString(encodedDateTime, key); break; + case DateTimeOffset dt: + userDefaults.SetString(dt.ToString("O"), key); + break; } } } @@ -124,6 +127,13 @@ public T Get(string key, T defaultValue, string sharedName) var encodedDateTime = Convert.ToInt64(savedDateTime, CultureInfo.InvariantCulture); value = DateTime.FromBinary(encodedDateTime); break; + case DateTimeOffset dt: + var savedDateTimeOffset = userDefaults.StringForKey(key); + if (DateTimeOffset.TryParse(savedDateTimeOffset, out var dateTimeOffset)) + { + value = dateTimeOffset; + } + break; case string s: // the case when the string is not null value = userDefaults.StringForKey(key); diff --git a/src/Essentials/src/Preferences/Preferences.shared.cs b/src/Essentials/src/Preferences/Preferences.shared.cs index db0b565fc4be..50334006a3ee 100644 --- a/src/Essentials/src/Preferences/Preferences.shared.cs +++ b/src/Essentials/src/Preferences/Preferences.shared.cs @@ -229,6 +229,14 @@ public static DateTime Get(string key, DateTime defaultValue) => /// public static void Set(string key, DateTime value) => Set(key, value, null); + + /// + public static DateTimeOffset Get(string key, DateTimeOffset defaultValue) => + Get(key, defaultValue, null); + + /// + public static void Set(string key, DateTimeOffset value) => + Set(key, value, null); /// public static DateTime Get(string key, DateTime defaultValue, string? sharedName) => @@ -238,6 +246,14 @@ public static DateTime Get(string key, DateTime defaultValue, string? sharedName public static void Set(string key, DateTime value, string? sharedName) => Current.Set(key, value, sharedName); + /// + public static DateTimeOffset Get(string key, DateTimeOffset defaultValue, string? sharedName) => + Current.Get(key, defaultValue, sharedName); + + /// + public static void Set(string key, DateTimeOffset value, string? sharedName) => + Current.Set(key, value, sharedName); + static IPreferences Current => Storage.Preferences.Default; internal static string GetPrivatePreferencesSharedName(string feature) => @@ -263,6 +279,7 @@ internal static void SetDefault(IPreferences? implementation) => typeof(double), typeof(float), typeof(DateTime), + typeof(DateTimeOffset) }; internal static void CheckIsSupportedType() diff --git a/src/Essentials/src/Preferences/Preferences.tizen.cs b/src/Essentials/src/Preferences/Preferences.tizen.cs index 24331afb8ed8..a18732418d41 100644 --- a/src/Essentials/src/Preferences/Preferences.tizen.cs +++ b/src/Essentials/src/Preferences/Preferences.tizen.cs @@ -58,6 +58,10 @@ public void Set(string key, T value, string sharedName) { Preference.Set(fullKey, dt.ToBinary()); } + else if (value is DateTimeOffset dto) + { + Preference.Set(fullKey, dto.ToString("O")); + } else Preference.Set(fullKey, value); } @@ -85,6 +89,13 @@ public T Get(string key, T defaultValue, string sharedName) var encodedValue = Preference.Get(fullKey); value = (T)(object)DateTime.FromBinary(encodedValue); break; + case DateTimeOffset dt: + var savedDateTimeOffset = Preference.Get(fullKey); + if (DateTimeOffset.TryParse(savedDateTimeOffset, out var dateTimeOffset)) + { + value = (T)(object)dateTimeOffset; + } + break; default: // the case when the string is null if (typeof(T) == typeof(string)) diff --git a/src/Essentials/src/Preferences/Preferences.uwp.cs b/src/Essentials/src/Preferences/Preferences.uwp.cs index ca8a61d1b1fe..df49a8fdf554 100644 --- a/src/Essentials/src/Preferences/Preferences.uwp.cs +++ b/src/Essentials/src/Preferences/Preferences.uwp.cs @@ -88,6 +88,10 @@ public void Set(string key, T value, string sharedName) { appDataContainer.Values[key] = dt.ToBinary(); } + else if (value is DateTimeOffset dto) + { + appDataContainer.Values[key] = dto.ToString("O"); + } else { appDataContainer.Values[key] = value; @@ -109,6 +113,13 @@ public T Get(string key, T defaultValue, string sharedName) { return (T)(object)DateTime.FromBinary((long)tempValue); } + else if (defaultValue is DateTimeOffset dto) + { + if (DateTimeOffset.TryParse((string)tempValue, out var dateTimeOffset)) + { + return (T)(object)dateTimeOffset; + } + } else { return (T)tempValue; @@ -182,6 +193,10 @@ public void Set(string key, T value, string sharedName = null) if (value is null) prefs.TryRemove(key, out _); + else if (value is DateTime dt) + prefs[key] = string.Format(CultureInfo.InvariantCulture, "{0}", dt.ToBinary()); + else if (value is DateTimeOffset dto) + prefs[key] = dto.ToString("O"); else prefs[key] = string.Format(CultureInfo.InvariantCulture, "{0}", value); @@ -194,6 +209,19 @@ public T Get(string key, T defaultValue, string sharedName = null) { if (inner.TryGetValue(key, out var value) && value is not null) { + if (defaultValue is DateTime dt) + { + long tempValue = (long)Convert.ChangeType(value, typeof(long), CultureInfo.InvariantCulture); + return (T)(object)DateTime.FromBinary(tempValue); + } + else if (defaultValue is DateTimeOffset dto) + { + if (DateTimeOffset.TryParse((string)value, out var dateTimeOffset)) + { + return (T)(object)dateTimeOffset; + } + } + try { return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); diff --git a/src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt index 0373cf272708..863c34c09b5e 100644 --- a/src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-android/PublicAPI.Unshipped.txt @@ -55,4 +55,8 @@ static Microsoft.Maui.Devices.Sensors.Geolocation.StopListeningForeground() -> v *REMOVED*Microsoft.Maui.Media.IMediaPicker.CapturePhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.CaptureVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.PickPhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! -*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! \ No newline at end of file +*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue, string? sharedName) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value, string? sharedName) -> void +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value) -> void \ No newline at end of file diff --git a/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt index 0373cf272708..863c34c09b5e 100644 --- a/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -55,4 +55,8 @@ static Microsoft.Maui.Devices.Sensors.Geolocation.StopListeningForeground() -> v *REMOVED*Microsoft.Maui.Media.IMediaPicker.CapturePhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.CaptureVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.PickPhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! -*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! \ No newline at end of file +*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue, string? sharedName) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value, string? sharedName) -> void +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value) -> void \ No newline at end of file diff --git a/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index 0373cf272708..863c34c09b5e 100644 --- a/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -55,4 +55,8 @@ static Microsoft.Maui.Devices.Sensors.Geolocation.StopListeningForeground() -> v *REMOVED*Microsoft.Maui.Media.IMediaPicker.CapturePhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.CaptureVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.PickPhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! -*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! \ No newline at end of file +*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue, string? sharedName) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value, string? sharedName) -> void +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value) -> void \ No newline at end of file diff --git a/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt index 0373cf272708..863c34c09b5e 100644 --- a/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt @@ -55,4 +55,8 @@ static Microsoft.Maui.Devices.Sensors.Geolocation.StopListeningForeground() -> v *REMOVED*Microsoft.Maui.Media.IMediaPicker.CapturePhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.CaptureVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.PickPhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! -*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! \ No newline at end of file +*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue, string? sharedName) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value, string? sharedName) -> void +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value) -> void \ No newline at end of file diff --git a/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt index 053769bb3b48..923a8726d8a4 100644 --- a/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt @@ -57,4 +57,8 @@ static Microsoft.Maui.Devices.Sensors.Geolocation.StopListeningForeground() -> v *REMOVED*Microsoft.Maui.Media.IMediaPicker.CapturePhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.CaptureVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.PickPhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! -*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! \ No newline at end of file +*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue, string? sharedName) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value, string? sharedName) -> void +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value) -> void \ No newline at end of file diff --git a/src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt index 0373cf272708..863c34c09b5e 100644 --- a/src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/net/PublicAPI.Unshipped.txt @@ -55,4 +55,8 @@ static Microsoft.Maui.Devices.Sensors.Geolocation.StopListeningForeground() -> v *REMOVED*Microsoft.Maui.Media.IMediaPicker.CapturePhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.CaptureVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.PickPhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! -*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! \ No newline at end of file +*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue, string? sharedName) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value, string? sharedName) -> void +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value) -> void \ No newline at end of file diff --git a/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt b/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt index 0373cf272708..863c34c09b5e 100644 --- a/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt +++ b/src/Essentials/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt @@ -55,4 +55,8 @@ static Microsoft.Maui.Devices.Sensors.Geolocation.StopListeningForeground() -> v *REMOVED*Microsoft.Maui.Media.IMediaPicker.CapturePhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.CaptureVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! *REMOVED*Microsoft.Maui.Media.IMediaPicker.PickPhotoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! -*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! \ No newline at end of file +*REMOVED*Microsoft.Maui.Media.IMediaPicker.PickVideoAsync(Microsoft.Maui.Media.MediaPickerOptions? options = null) -> System.Threading.Tasks.Task! +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue, string? sharedName) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Get(string! key, System.DateTimeOffset defaultValue) -> System.DateTimeOffset +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value, string? sharedName) -> void +static Microsoft.Maui.Storage.Preferences.Set(string! key, System.DateTimeOffset value) -> void \ No newline at end of file diff --git a/src/Essentials/test/DeviceTests/Tests/Preferences_Tests.cs b/src/Essentials/test/DeviceTests/Tests/Preferences_Tests.cs index 4895b7253cf0..6ad20e0f767c 100644 --- a/src/Essentials/test/DeviceTests/Tests/Preferences_Tests.cs +++ b/src/Essentials/test/DeviceTests/Tests/Preferences_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Microsoft.Maui.Storage; using Xunit; @@ -220,11 +221,7 @@ public void Not_ContainsKey(string sharedName) Assert.False(Preferences.ContainsKey("NotContainsKey1", sharedName)); } - [Theory( -#if WINDOWS - Skip = "Fails on Windows unpackaged" -#endif - )] + [Theory] [InlineData(null, DateTimeKind.Utc)] [InlineData(sharedNameTestData, DateTimeKind.Utc)] [InlineData(null, DateTimeKind.Local)] @@ -455,11 +452,7 @@ public void Not_ContainsKey_NonStatic(string sharedName) Assert.False(Preferences.Default.ContainsKey("NotContainsKey1", sharedName)); } - [Theory( -#if WINDOWS - Skip = "Fails on Windows unpackaged" -#endif - )] + [Theory] [InlineData(null, DateTimeKind.Utc)] [InlineData(sharedNameTestData, DateTimeKind.Utc)] [InlineData(null, DateTimeKind.Local)] @@ -475,6 +468,28 @@ public void DateTimePreservesKind_NonStatic(string sharedName, DateTimeKind kind Assert.Equal(date, get); Assert.Equal(kind, get.Kind); } + + public static IEnumerable GetDateTimeOffsetData() + { + yield return [null, TimeSpan.Zero]; + yield return [sharedNameTestData, TimeSpan.Zero]; + yield return [null, TimeSpan.FromHours(1)]; + yield return [sharedNameTestData, TimeSpan.FromHours(1)]; + } + + [Theory] + [MemberData(nameof(GetDateTimeOffsetData))] + public void DateTimeOffsetPreservesOffset_NonStatic(string sharedName, TimeSpan offset) + { + var date = new DateTime(2018, 05, 07, 8, 30, 0); + var dateTimeOffset = new DateTimeOffset(date, offset); + + Preferences.Default.Set("datetimeoffset_offset", dateTimeOffset, sharedName); + + var get = Preferences.Default.Get("datetimeoffset_offset", DateTimeOffset.MinValue, sharedName); + + Assert.Equal(offset, get.Offset); + } #endregion } }