如何将C# DateTimeOffset格式化为与SQL Server DateTime2(7)完全一致?
解决DateTimeOffset与SQL Server DateTime2(7)文本格式一致的问题
问题场景
两个不同的DateTimeOffset值:
var dt1 = new DateTimeOffset(638124107765100000, TimeSpan.Zero); var dt2 = new DateTimeOffset(638124107765000000, TimeSpan.Zero);
存入SQL Server的DateTime2(7)列后,得到的存储值完全相同:
2023-02-19 13:39:36.5066667
但通过Entity Framework读取后,值会发生变化。需求是将DateTimeOffset格式化为与SQL Server存储的文本完全一致的形式,允许少量精度损失,但需覆盖所有边缘情况。
此前尝试的方法均存在问题:
- 忽略毫秒部分中10以上的数值,结果为
2023-02-19 13:39:36.50000,无法匹配目标格式,且不确定是否覆盖所有边缘情况 - 基于Ticks转换,得到的结果为
2023-02-19 13:39:36.51000和2023-02-19 13:39:36.49999,说明Ticks与SQL Server存储的转换并非简单浮点转换
解决方案:模拟SQL Server DateTime2(7)的舍入规则
SQL Server的DateTime2(7)会将输入值舍入到7位小数的微秒精度,而.NET的DateTimeOffset的Ticks是100纳秒单位(1秒=10^7 Ticks),需准确映射两者的精度逻辑。
实现步骤
- 将
DateTimeOffset转换为UTC时间(确保与SQL存储的时区一致) - 计算总微秒数(1微秒=10 Ticks),对微秒数的小数部分进行四舍五入
- 重新构造
DateTimeOffset并格式化为目标字符串
代码实现
public static string FormatToSqlDateTime2(DateTimeOffset dto) { // 转换为UTC时间,对齐SQL存储的时区 var utcDateTime = dto.UtcDateTime; // 计算总微秒数(1微秒 = 10 Ticks) long totalMicroseconds = utcDateTime.Ticks / 10; // 获取Ticks余数,判断是否需要进位 long remainder = utcDateTime.Ticks % 10; // 模拟SQL的四舍五入规则:余数>=5则进1微秒 if (remainder >= 5) { totalMicroseconds += 1; } // 重新构造Ticks并生成新的UTC DateTime long adjustedTicks = totalMicroseconds * 10; var adjustedUtcDateTime = new DateTime(adjustedTicks, DateTimeKind.Utc); // 转回DateTimeOffset并格式化为SQL DateTime2(7)的字符串格式 var adjustedDto = new DateTimeOffset(adjustedUtcDateTime); return adjustedDto.ToString("yyyy-MM-dd HH:mm:ss.fffffff"); }
测试验证
针对示例中的两个DateTimeOffset值,调用上述方法后均会输出2023-02-19 13:39:36.5066667,与SQL Server存储的文本完全一致。
边缘情况覆盖
- 余数为4时:不进位,符合SQL舍入规则
- 余数为5时:进位,匹配SQL四舍五入逻辑
- 跨秒场景(如59.9999999秒):进位后自动转为下一分钟的00秒,方法可正确处理
内容的提问来源于stack exchange,提问作者Akash Kava
相关产品推荐
相关产品推荐

