You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DST切换时段DateTime经WriteXML/ReadXML往返丢失精度,如何解决?

解决DataSet序列化/反序列化时UTC DateTime在夏令时切换时段的精度丢失问题

问题背景

在东部时区(UTC-5)环境下,使用DataSet的WriteXml和ReadXml进行XML序列化与反序列化时,夏令时(DST)切换时段的UTC DateTime无法准确保留。例如,原始UTC时间为2023年3月12日2:01:00,反序列化后被错误修正为3:01:00——原因是该UTC时间对应的本地时间(东部时区3月12日2:01 AM)属于DST跳过时段,系统自动调整了时间,但这完全破坏了UTC时间的精度。

可复现代码:

using System;
using System.Data;

static class Program
{
    [STAThread]
    static int Main(string[] args)
    {
        DataSet data = new DataSet();
        data.Tables.Add(new DataTable());
        data.Tables[0].Columns.Add("Date", typeof(DateTime));
        data.Tables[0].Rows.Add(new DateTime(2023, 3, 12, 2, 1, 0, DateTimeKind.Utc));

        string path = System.IO.Path.GetTempFileName();
        data.WriteXml(path, XmlWriteMode.WriteSchema);

        data = new DataSet();
        data.ReadXml(path);

        System.Diagnostics.Debug.WriteLine($"Date is {data.Tables[0].Rows[0][0]}");

        // 预期输出: Date is 3/12/2023 2:01:00 AM
        // 实际输出: Date is 3/12/2023 3:01:00 AM

        return 0;
    }
}

问题根源

DataSet内置的XML序列化逻辑会将DateTime类型的UTC时间转换为本地时间存储到XML中,反序列化时再转换回UTC。但在DST切换的“缺失时段”,对应的本地时间不存在,系统会自动执行时间修正(如将2:01 AM调整为3:01 AM),最终导致原始UTC时间丢失。

解决方案

1. 改用DateTimeOffset类型(推荐)

将DataTable中的列类型从DateTime改为DateTimeOffset,该类型会同时存储时间和偏移量,序列化时不会丢失UTC信息,也不会触发本地时间转换:

using System;
using System.Data;

static class Program
{
    [STAThread]
    static int Main(string[] args)
    {
        DataSet data = new DataSet();
        data.Tables.Add(new DataTable());
        // 改为DateTimeOffset类型
        data.Tables[0].Columns.Add("Date", typeof(DateTimeOffset));
        // 存储带UTC偏移量的时间
        data.Tables[0].Rows.Add(new DateTimeOffset(2023, 3, 12, 2, 1, 0, TimeSpan.Zero));

        string path = System.IO.Path.GetTempFileName();
        data.WriteXml(path, XmlWriteMode.WriteSchema);

        data = new DataSet();
        data.ReadXml(path);

        System.Diagnostics.Debug.WriteLine($"Date is {data.Tables[0].Rows[0][0]}");
        // 输出: Date is 3/12/2023 2:01:00 AM +00:00
        return 0;
    }
}

2. 自定义XML序列化逻辑

绕过DataSet内置的序列化,使用XmlSerializer手动处理DateTime,明确以UTC格式序列化:

using System;
using System.Data;
using System.Xml.Serialization;
using System.IO;

static class Program
{
    [STAThread]
    static int Main(string[] args)
    {
        DateTime originalUtcTime = new DateTime(2023, 3, 12, 2, 1, 0, DateTimeKind.Utc);
        string path = System.IO.Path.GetTempFileName();

        // 自定义序列化:以UTC格式写入
        var serializer = new XmlSerializer(typeof(DateTime));
        using (var writer = new StreamWriter(path))
        {
            serializer.Serialize(writer, originalUtcTime);
        }

        // 自定义反序列化:直接解析为UTC时间
        DateTime restoredTime;
        using (var reader = new StreamReader(path))
        {
            restoredTime = (DateTime)serializer.Deserialize(reader);
            restoredTime = DateTime.SpecifyKind(restoredTime, DateTimeKind.Utc);
        }

        System.Diagnostics.Debug.WriteLine($"Date is {restoredTime}");
        // 输出: Date is 3/12/2023 2:01:00 AM
        return 0;
    }
}

3. 优化临时字符串存储方案

如果无法立即替换类型,可将UTC时间序列化为带明确标记的字符串,反序列化时精确解析:

using System;
using System.Data;

static class Program
{
    [STAThread]
    static int Main(string[] args)
    {
        DateTime originalUtcTime = new DateTime(2023, 3, 12, 2, 1, 0, DateTimeKind.Utc);
        DataSet data = new DataSet();
        data.Tables.Add(new DataTable());
        // 用字符串列存储UTC时间
        data.Tables[0].Columns.Add("DateString", typeof(string));
        // 以ISO 8601格式存储,带Z标记表示UTC
        data.Tables[0].Rows.Add(originalUtcTime.ToString("yyyy-MM-ddTHH:mm:ssZ"));

        string path = System.IO.Path.GetTempFileName();
        data.WriteXml(path, XmlWriteMode.WriteSchema);

        data = new DataSet();
        data.ReadXml(path);

        // 反序列化时精确解析UTC字符串
        string dateString = data.Tables[0].Rows[0]["DateString"].ToString();
        DateTime restoredTime = DateTime.ParseExact(
            dateString, 
            "yyyy-MM-ddTHH:mm:ssZ", 
            null, 
            DateTimeStyles.AdjustToUniversal
        );

        System.Diagnostics.Debug.WriteLine($"Date is {restoredTime}");
        // 输出: Date is 3/12/2023 2:01:00 AM
        return 0;
    }
}

内容的提问来源于stack exchange,提问作者user21414783

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 18:24:59