.NET Core从3.1升级到5.0后时区处理逻辑变更引发日期参数解析异常
问题产生原因
- .NET Core 3.1版本中,Web API默认使用的System.Text.Json序列化器,会将携带
Z标识的UTC时间格式参数自动转换为服务器本地时区对应的DateTime值,解析后对象的Kind属性为DateTimeKind.Local,你观测到的+3小时差值就是部署API的服务器本地时区与UTC时区的偏移量。 - 升级到.NET 5.0后,官方调整了System.Text.Json的默认日期解析规则,携带
Z标识的UTC时间会被直接解析为UTC时间,不会自动做本地时区转换,解析后对象的Kind属性为DateTimeKind.Utc,因此出现了解析值的差异。
可行解决方案
你可以根据业务实际情况选择以下任意一种方案修复:
方案1:全局配置序列化规则,恢复3.1版本的行为
该方案不需要修改业务代码,仅需修改启动配置即可兼容原有逻辑。
若使用默认System.Text.Json序列化器
先自定义日期转换类:
public class LocalTimeDateTimeConverter : JsonConverter<DateTime> { public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TryGetDateTime(out DateTime dateTime)) { return dateTime.Kind switch { DateTimeKind.Utc => dateTime.ToLocalTime(), DateTimeKind.Unspecified => DateTime.SpecifyKind(dateTime, DateTimeKind.Local), _ => dateTime }; } return default; } public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) { // 序列化输出规则可根据业务需求调整,示例是统一输出UTC格式时间 writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")); } }
再在启动配置中注册该转换器:
// .NET 6+ 顶层语句写法 builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new LocalTimeDateTimeConverter()); }); // .NET 5 带Startup类写法 public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new LocalTimeDateTimeConverter()); }); }
若使用Newtonsoft.Json序列化器
直接配置序列化设置即可:
builder.Services.AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Local; });
方案2:调整业务代码适配UTC时间
该方案长期更规范,可避免多节点部署时区不一致导致的时间问题。将原有依赖本地时间的业务逻辑全部调整为使用UTC时间计算、存储,返回给前端时再按需转换为对应用户的时区时间即可。
方案3:使用DateTimeOffset类型接收日期参数
DateTimeOffset自带时区偏移信息,不存在自动转换的歧义,解析和计算的稳定性更高。只需将参数类中的DateTime类型替换为DateTimeOffset即可:
public class DateQueryParam { public DateTimeOffset CreatedOnGreaterThan { get; set; } public DateTimeOffset CreatedOnLessThan { get; set; } }
后续需要本地时间时调用LocalDateTime属性,需要UTC时间时调用UtcDateTime属性即可。
内容的提问来源于stack exchange,提问作者Mennan
相关产品推荐
相关产品推荐

