From 7ccb1953253684526c3c931837413c6a9812ddb8 Mon Sep 17 00:00:00 2001 From: Edward Miller Date: Wed, 5 Jun 2024 19:52:57 -0500 Subject: [PATCH] add converters for DateOnly and TimeOnly --- .../src/Core/DateOnlyToDateTimeConverter.cs | 55 +++++++++++++++++++ .../src/Core/DatePicker/DatePicker.cs | 3 + .../src/Core/TimeOnlyToTimeSpanConverter.cs | 55 +++++++++++++++++++ .../src/Core/TimePicker/TimePicker.cs | 3 + 4 files changed, 116 insertions(+) create mode 100644 src/Controls/src/Core/DateOnlyToDateTimeConverter.cs create mode 100644 src/Controls/src/Core/TimeOnlyToTimeSpanConverter.cs diff --git a/src/Controls/src/Core/DateOnlyToDateTimeConverter.cs b/src/Controls/src/Core/DateOnlyToDateTimeConverter.cs new file mode 100644 index 000000000000..f0f1b416066e --- /dev/null +++ b/src/Controls/src/Core/DateOnlyToDateTimeConverter.cs @@ -0,0 +1,55 @@ +#if NET6_0_OR_GREATER +using System; +using System.ComponentModel; +using System.Globalization; + +namespace Microsoft.Maui.Controls +{ + internal class DateOnlyToDateTimeConverter : TypeConverter, IExtendedTypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) + => sourceType == typeof(string) || sourceType == typeof(DateTime); + + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) + => destinationType == typeof(string) || destinationType == typeof(DateTime); + + object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider) + { + if (DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)) + { + return DateOnly.FromDateTime(result); + } + throw new NotSupportedException($"Cannot convert \"{value}\" into {typeof(DateOnly)}"); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) + { + if (value is DateTime dateTime) + { + return DateOnly.FromDateTime(dateTime); + } + if (value is string stringValue && DateTime.TryParse(stringValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)) + { + return DateOnly.FromDateTime(result); + } + throw new NotSupportedException($"Cannot convert \"{value}\" into {typeof(DateOnly)}"); + } + + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + if (value is DateOnly dateOnly) + { + if (destinationType == typeof(string)) + { + return dateOnly.ToString(culture); + } + if (destinationType == typeof(DateTime)) + { + return dateOnly.ToDateTime(TimeOnly.MinValue); + } + } + throw new NotSupportedException($"Cannot convert \"{value}\" into {destinationType}"); + } + } +} +#endif \ No newline at end of file diff --git a/src/Controls/src/Core/DatePicker/DatePicker.cs b/src/Controls/src/Core/DatePicker/DatePicker.cs index aac0a0edc24b..fb0903e83d5a 100644 --- a/src/Controls/src/Core/DatePicker/DatePicker.cs +++ b/src/Controls/src/Core/DatePicker/DatePicker.cs @@ -52,6 +52,9 @@ public DatePicker() } /// +#if NET6_0_OR_GREATER + [System.ComponentModel.TypeConverter(typeof(DateOnlyToDateTimeConverter))] +#endif public DateTime Date { get { return (DateTime)GetValue(DateProperty); } diff --git a/src/Controls/src/Core/TimeOnlyToTimeSpanConverter.cs b/src/Controls/src/Core/TimeOnlyToTimeSpanConverter.cs new file mode 100644 index 000000000000..4c36d85ad62b --- /dev/null +++ b/src/Controls/src/Core/TimeOnlyToTimeSpanConverter.cs @@ -0,0 +1,55 @@ +#if NET6_0_OR_GREATER +using System; +using System.ComponentModel; +using System.Globalization; + +namespace Microsoft.Maui.Controls +{ + internal class TimeOnlyToTimeSpanConverter : TypeConverter, IExtendedTypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) + => sourceType == typeof(string) || sourceType == typeof(TimeOnly); + + public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType) + => destinationType == typeof(string) || destinationType == typeof(TimeSpan); + + object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider) + { + if (TimeOnly.TryParse(value, out var result)) + { + return result.ToTimeSpan(); + } + throw new NotSupportedException($"Cannot convert \"{value}\" into {typeof(TimeSpan)}"); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value) + { + if (value is TimeOnly timeOnly) + { + return timeOnly.ToTimeSpan(); + } + if (value is string stringValue && TimeOnly.TryParse(stringValue, out var result)) + { + return result.ToTimeSpan(); + } + throw new NotSupportedException($"Cannot convert \"{value}\" into {typeof(TimeSpan)}"); + } + + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + if (value is TimeSpan timeSpan) + { + if (destinationType == typeof(string)) + { + return timeSpan.ToString(); + } + if (destinationType == typeof(TimeOnly)) + { + return TimeOnly.FromTimeSpan(timeSpan); + } + } + throw new NotSupportedException($"Cannot convert \"{value}\" into {destinationType}"); + } + } +} +#endif \ No newline at end of file diff --git a/src/Controls/src/Core/TimePicker/TimePicker.cs b/src/Controls/src/Core/TimePicker/TimePicker.cs index 37f90dd08bc4..ab3cb096b897 100644 --- a/src/Controls/src/Core/TimePicker/TimePicker.cs +++ b/src/Controls/src/Core/TimePicker/TimePicker.cs @@ -67,6 +67,9 @@ public double CharacterSpacing } /// +#if NET6_0_OR_GREATER + [System.ComponentModel.TypeConverter(typeof(TimeOnlyToTimeSpanConverter))] +#endif public TimeSpan Time { get { return (TimeSpan)GetValue(TimeProperty); }