C# 使用IConfiguration绑定DateTime时使用DateTimeStyles.RoundtripKind
TL;DR:我希望绑定C# 使用IConfiguration绑定DateTime时使用DateTimeStyles.RoundtripKind,c#,datetime,asp.net-core,C#,Datetime,Asp.net Core,TL;DR:我希望绑定DateTime值的结果与IConfiguration中的DateTime.Parse(stringValue,null,DateTimeStyles.RoundtripKind)相同,但默认情况下与DateTime.Parse(stringValue)相同 我正在使用IConfiguration的绑定功能从配置中提取DateTime值 假设我有一个JSON配置,如下所示: { testValue: "2020-03-08T14:37:46.9793888Z" }
DateTime
值的结果与IConfiguration
中的DateTime.Parse(stringValue,null,DateTimeStyles.RoundtripKind)
相同,但默认情况下与DateTime.Parse(stringValue)
相同
我正在使用
IConfiguration
的绑定功能从配置中提取DateTime
值
假设我有一个JSON配置,如下所示:
{
testValue: "2020-03-08T14:37:46.9793888Z"
}
我可以通过以下几种方法之一将值提取为DateTime
。以下是我想到的前两个:
// Directly from IConfiguration
var dateTimeValue = configuration.GetValue<DateTime>("testValue");
我正在编写关注DateTime
的Kind
属性的代码。普通解析忽略“Z”时间,并生成任何带有时区指示的内容Local
。使用RoundtripKind
样式进行这样的调用可以解决以下问题:
// Good, uses Kind if available
var dateTimeValue = DateTime.Parse(stringValue, null, DateTimeStyles.RoundtripKind);
有没有办法强制配置绑定器使用此调用而不是默认调用?我有一个想法:我可以用包装器类型替换选项类中的
DateTime
字段,然后对其执行解析。我们更改字段的类型,如下所示:
private class TestOptions
{
//public DateTime TestValue { get; set; }
public RoundtripDateTime TestValue { get; set; }
}
我们组成了一个包装类型(我把它做成了一个struct
,但是类也可以工作),包括与DateTime
之间的隐式转换(这样至少一些代码不需要更改)和很好的Parse()
和ToString()
方法。我们包括一个[TypeConverter]
属性,以便配置绑定器检测转换器类
[TypeConverter(typeof(RoundtripDateTimeTypeConverter))]
public struct RoundtripDateTime
{
public RoundtripDateTime(DateTime value)
{
Value = value;
}
public DateTime Value { get; set; }
public static implicit operator DateTime(RoundtripDateTime roundTripDateTime) => roundTripDateTime.Value;
public static implicit operator RoundtripDateTime(DateTime dateTime) => new RoundtripDateTime(dateTime);
public override string ToString() => Value.ToString("o");
public static RoundtripDateTime Parse(string s) => DateTime.Parse(s, null, DateTimeStyles.RoundtripKind);
public static RoundtripDateTime Parse(string s, CultureInfo culture) => DateTime.Parse(s, culture, DateTimeStyles.RoundtripKind);
}
最后,我们包括一个新的TypeConverter
实现,它将处理包装器的实例。(我作弊并查找源代码到DateTimeConverter
,以了解它已经在做什么,但这实际上要简单得多,因为它总是在文化无关模式下运行。[编辑:对于从string
的转换,添加了文化来帮助Parse()
处理不是ISO8601格式的日期时间。它仍然非常简单。])
这一切可能需要一些调整,但我想我有一些我可以处理的东西
如果你有更好的想法,请随意添加其他答案
private class TestOptions
{
//public DateTime TestValue { get; set; }
public RoundtripDateTime TestValue { get; set; }
}
[TypeConverter(typeof(RoundtripDateTimeTypeConverter))]
public struct RoundtripDateTime
{
public RoundtripDateTime(DateTime value)
{
Value = value;
}
public DateTime Value { get; set; }
public static implicit operator DateTime(RoundtripDateTime roundTripDateTime) => roundTripDateTime.Value;
public static implicit operator RoundtripDateTime(DateTime dateTime) => new RoundtripDateTime(dateTime);
public override string ToString() => Value.ToString("o");
public static RoundtripDateTime Parse(string s) => DateTime.Parse(s, null, DateTimeStyles.RoundtripKind);
public static RoundtripDateTime Parse(string s, CultureInfo culture) => DateTime.Parse(s, culture, DateTimeStyles.RoundtripKind);
}
public class RoundtripDateTimeTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (sourceType == typeof(string)) || base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string stringValue)
{
var text = stringValue.Trim();
if (text == string.Empty)
{
return (RoundtripDateTime)DateTime.MinValue;
}
else
{
try
{
return RoundtripDateTime.Parse(text, culture ?? CultureInfo.InvariantCulture);
}
catch (FormatException e)
{
throw new FormatException($"'{value}' is not a valid value for {nameof(RoundtripDateTime)}.", e);
}
}
}
else
{
return base.ConvertFrom(context, culture, value);
}
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string) && value is RoundtripDateTime roundtripDateTimeValue)
{
return (roundtripDateTimeValue.Value == DateTime.MinValue)
? string.Empty
: roundtripDateTimeValue.ToString();
}
else
{
return base.ConvertTo(context, culture, value, destinationType);
}
}
}