C#中夏令时切换时段的模糊本地时间转UTC的区分处理方案咨询
嗨,这个问题确实是夏令时切换场景下的典型坑——尤其是像英国这类每年调两次钟的时区,回拨时段的重复时间确实会让常规转换方法失效。我来给你拆解下可行的解决思路:
首先得明确核心问题:你提到的29/10/2023 01:30:00这个时间,在英国时区里会出现两次:
- 第一次是BST夏令时:此时UTC偏移是+1小时,对应UTC时间是
2023-10-29 00:30:00 - 第二次是GMT标准时间:此时UTC偏移是0,对应UTC时间就是
2023-10-29 01:30:00
而C#默认的ToUniversalTime()或者TimeZoneInfo.ConvertTimeToUtc在处理模糊时间时,会默认选择偏移量更小的那次(也就是标准时间的结果),所以你才会每次得到同一个UTC时间。
好在你有外部API提供的时区标记(GMT BST或GMT),这就好办了,有两种可靠的处理方式:
方法一:直接用DateTimeOffset指定偏移量(最直观)
既然API已经明确告诉你这个时间属于夏令时还是标准时段,直接给时间加上对应的偏移量,再转UTC就行:
using System; using System.Globalization; // 从API获取的原始数据 string dateStr = "29/10/2023 01:30:00"; string timeZoneTag = "GMT BST"; // 或者 "GMT" // 解析本地时间 DateTime localTime = DateTime.ParseExact(dateStr, "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture); // 根据标记选择偏移量 TimeSpan offset = timeZoneTag.Contains("BST") ? TimeSpan.FromHours(1) : TimeSpan.Zero; // 转成UTC DateTime utcTime = new DateTimeOffset(localTime, offset).UtcDateTime;
这种方式简单直接,完全依赖API给出的标记,不需要依赖时区信息的复杂判断,适合你这种有明确标记的场景。
方法二:用TimeZoneInfo处理模糊时间(更通用的时区场景)
如果以后API标记有变化,或者你需要适配更多时区,用TimeZoneInfo的模糊时间处理方法会更灵活:
using System; using System.Globalization; using System.Linq; // 获取英国时区(注意ID是"GMT Standard Time") var ukTimeZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time"); string dateStr = "29/10/2023 01:30:00"; string timeZoneTag = "GMT BST"; DateTime localTime = DateTime.ParseExact(dateStr, "dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture); DateTime utcTime; if (ukTimeZone.IsAmbiguousTime(localTime)) { // 获取这个本地时间对应的所有可能UTC偏移量 var possibleOffsets = ukTimeZone.GetAmbiguousTimeOffsets(localTime); // 偏移量大的是夏令时(BST),小的是标准时间(GMT) var targetOffset = timeZoneTag.Contains("BST") ? possibleOffsets.Max() : possibleOffsets.Min(); utcTime = localTime.Subtract(targetOffset); } else { // 非模糊时间直接转换 utcTime = TimeZoneInfo.ConvertTimeToUtc(localTime, ukTimeZone); }
这里GetAmbiguousTimeOffsets会返回该本地时间对应的所有可能时区偏移,我们根据API标记选择对应的偏移量,再计算出正确的UTC时间。
补充说明
为什么默认的ToUniversalTime()会失效?因为如果你的DateTime对象的Kind是Unspecified,它会默认使用当前系统的时区来转换,而且对于模糊时间,CLR会默认选择夏令时结束后的标准时间,也就是偏移量更小的那次,所以你每次得到的都是01:30 UTC。
用上面两种方法,就能精准区分两次重复的本地时间,转换出正确的UTC时间啦。
备注:内容来源于stack exchange,提问作者Ant

