最近在跟SQLServer数据库进行交互的时候发现一个奇怪的问题,在往数据库里边插入日期型数据的时候,在C#里面赋值的为 2014/05/19 23:59:59,但是存到数据库里边就变成了2014/05/20 00:00:00。

问题场景

当时需求是这样的,产品的销售策略要求管理员输入一个产品销售的开始日期SalesStart和结束日期SalesEnd,然后业务会根据当前的时间判断是否在这个产品销售范围内,如果不在则显示未开始或者已过期,所以存储的时候,对SalesEnd进行了处理,在存到数据库的时候,保存的是当天的23:59:59,当时我的处理是这样的:在截止日期加1天然后减去以1毫秒,代码如下:

ProductSalesPolicyModel productSaleModel;
productSaleModel = new ProductSalesPolicyModel();

productSaleModel.SalesStart = item.SaleStartTime;
productSaleModel.ValidStart = item.ValidStartTime;
productSaleModel.SalesEnd = item.SaleEndTime.AddDays(1).AddMilliseconds(-1);
productSaleModel.ValidEnd = item.ValidEndTime.AddDays(1).AddMilliseconds(-1);

然后利用企业库往数据库中使用某个存储过程插入数据,参数赋值的代码如下:

DataParameterCollection parameters;
parameters = new DataParameterCollection();
parameters.Add("SalesStart", DbType.DateTime, salePolicyModel.SalesStart);
parameters.Add("ValidStart", DbType.DateTime, salePolicyModel.ValidStart);
parameters.Add("ValidEnd", DbType.DateTime, salePolicyModel.ValidEnd);
parameters.Add("SalesEnd", DbType.DateTime, salePolicyModel.SalesEnd);

执行插入操作的细节就不写了。

执行完成之后,结果如下:

在数据库中ProductSalePolicy的表结构如下,其中ValidEnd的类型也是datetime

原因分析

初步分析,有可能是C#中DateTime和SQL Server中datetime数据精度不一致导致的

SQL Server中的时间精度

1. SQL Server中datetime精度

于是到MSDN上去找Transact-SQL中的datetime类型,果不其然,SQL中datetime字段的取值范围为 :

  • 日期范围为:1753.1.1 ~ 9999.12.31
  • 时间范围为:00:00:00 ~23:59:59.997

可见,datetime类型的时间精度是3.33毫秒,如果超过该精度会进行近似到0.000,0.003,0.007 秒,比如:

输入值

SQL 存储值

01/01/98 23:59:59.999

1998-01-02 00:00:00.000

01/01/98 23:59:59.995

01/01/98 23:59:59.996

01/01/98 23:59:59.997

01/01/98 23:59:59.998

1998-01-01 23:59:59.997

01/01/98 23:59:59.992

01/01/98 23:59:59.993

01/01/98 23:59:59.994

1998-01-01 23:59:59.993

01/01/98 23:59:59.990

01/01/98 23:59:59.991

1998-01-01 23:59:59.990

2. SQL Server中datetime2精度

鉴于SQL Server的datetime精度只有3.33毫秒,从SQL Server2008开始,提供了datetime2类型, 该类型的时间精度为00:00:00 ~ 23:59:59.9999999 即100纳秒,10-4毫秒。

System.Data这个主要和外部数据进行交互的名字空间中的DbType枚举可以看到,它提供了DataTime,和DataTime2这两种类型,分别对应了SQL Server中的这两种时间精度,从注释中可以看出,DataTime2的精度是100纳秒

namespace System.Data
{
    // Summary:
    //     Specifies the data type of a field, a property, or a Parameter object of
    //     a .NET Framework data provider.
    public enum DbType
    {
        //
        // Summary:
        //     A type representing a date and time value.
        DateTime = 6,
        //
        // Summary:
        //     Date and time data. Date value range is from January 1,1 AD through December
        //     31, 9999 AD. Time value range is 00:00:00 through 23:59:59.9999999 with an
        //     accuracy of 100 nanoseconds.
        DateTime2 = 26,
    }
}

C#中Datetime精度

现在再看看C#中的DataTime精度,因为我们在给存储过程中的ValidEnd参数进行赋值的时候定义的类型为DbType.Datetime ,同样,在MSDN上,有对其精度的说明:

Time values are measured in 100-nanosecond units called ticks

100ns就是DateTime的精度,即10-4毫秒。10-4 毫秒也是为DateTime类型的精度

通常我们看到的C#中的DateTime类型精确到100纳秒,即最大时间为23:59:59.9999999。这后面的也就是我们常用到的tick的精度。

原因

因为我在C#代码中处理的时候是item.SaleEndTime.AddDays(1).AddMilliseconds(-1);所以时间精度为0.0001毫秒,为2014/12/16 23:59:59.9990000,然而数据库中字段使用的是datetime类型,该类型的精度为3.33毫秒,所以存到SQL Server的时候被round到了2014/12/17 00:00:000000,从前面的分析也可以看出这个问题。

解决方法

根据分析,有两种方式:

  • 一种是将数据库中的该类型从datetime改为datetime2类型,但是这个类型只有SQL Server 2008及以上版本才支持,他的精度和C# 里面的DateTime数据类型的精度是一致的。
  • 再一种就是修改我们传到SQL Server中的值了,在一些不需要那么高的精度的场景下,比如本文中对结束日期添加23:59:59的这一处理,当时想都没想直接是AddMilliseconds,减的是1ms,而数据库中的精度是3.3ms,所以导致被SQL Server Round到了第二天的零点。这里把 AddMilliseconds改成AddSecond(-1)减1s即可。

总结

C# 中的DateTime的精度和SQL Server中的datetime2的精度(SQL Server 2008及以上版本才有) 是一致的,均为100纳秒(10-4毫秒),而SQL Server中的datetime类型精度只有3.33毫秒,所以如果把C# 中的DateTime类型存储到SQL Server中的datetime中时,会出现精度丢失的情况,在对精度要求较高的系统中,需要注意。

1ms引发的问题的更多相关文章

  1. Mysql中where条件一个单引号引发的性能损耗

    日常写SQL中可能会有一些小细节忽略了导致整个sql的性能下降了好几倍甚至几十倍,几百倍.以下这个示例就是mysql语句中的一个单引号('')引发的性能耗损,我相信很多朋友都遇到过,甚至还在这样写. ...

  2. try catch引发的性能优化深度思考

    关键代码拆解成如下图所示(无关部分已省略): 起初我认为可能是这个 getRowDataItemNumberFormat 函数里面某些方法执行太慢,从 formatData.replace 到 une ...

  3. [WCF]缺少一行代码引发的血案

    这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性.具体来说,这是一个关于如何自动为服务接口(契约 ...

  4. dubbox微服务实例及引发的“血案”

    Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 主要核心部件: Remoting: 网络通信框架 ...

  5. 安全防范:nginx下git引发的隐私泄露问题

    安全防范:nginx下git引发的隐私泄露问题 1   安全事件 最近阿里云服务器后台管理系统中收到一条安全提示消息,系统配置信息泄露: http://my.domain.com/.git/confi ...

  6. Integer.parseInt 引发的血案

    Integer.parseInt 处理一个空字符串, 结果出错了, 程序没有注意到,搞了很久, 引发了血案啊!! 最后,终于 观察到了, 最后的部分: Caused by: java.lang.NoC ...

  7. 选择目录,选择文件夹的COM组件问题。在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式。请确保您的 Main 函数带有 STAThreadAttribute 标记。 只有将调试器附加到该进程才会引发此异常。

    异常: 在可以调用 OLE 之前,必须将当前线程设置为单线程单元(STA)模式.请确保您的 Main 函数带有 STAThreadAttribute 标记. 只有将调试器附加到该进程才会引发此异常. ...

  8. 【手记】调用Process.EnterDebugMode引发异常:并非所有引用的特权或组都分配给呼叫方

    刚上线一个新版本,其中有台电脑打开软件就报[xx的类型初始值设定项引发异常](还好不是一大波电脑,新东西上线就怕哀鸿遍野),如图: 显然是该类型的静态构造函数中抛异常了(红线处就是类名),遂打开该类, ...

  9. JDBC驱动自身问题引发的FullGC

    公众号HelloJava刊出一篇<MySQL Statement cancellation timer 故障排查分享>,作者的某服务的线上机器报 502(502是 nginx 做后端健康检 ...

随机推荐

  1. Js 日期转换函数(UTC时间转换及日期想加减)

    IOS上Js日期转换中new Date("yyyy-mm-dd")不能正常工作,必须使用new Date("yyyy/MM/dd"); 日期相加减: Date. ...

  2. Mac/IOS/linux获取当前时间包含微秒毫秒的代码

    #include <sys/time.h> 1 struct UnityLocalTimeStat { int Year; int Month; int DayOfWeek; int Da ...

  3. spin.js

    $ajax提交,菊花加载的方式和位置: $.ajax({ type: "get", url: "http://www.xxx.com/test.html", b ...

  4. UVALive 4728 Squares (平面最远点对)

    题意:n个平行于坐标轴的正方形,求出最远点对的平方 题解:首先求出凸包,可以证明最远点对一定是凸包上的点对,接着可以证明最远点对(每个点的对踵点)一定只有3*n/2对 接着使用旋转卡壳找到最远点对,但 ...

  5. mybatis map常用数据类型

    JDBC Type Java Type CHAR String VARCHAR String LONGVARCHAR String NUMERIC java.math.BigDecimal DECIM ...

  6. 递推+高精度 UVA 10497 Sweet Child Makes Trouble(可爱的孩子惹麻烦)

    题目链接 题意: n个物品全部乱序排列(都不在原来的位置)的方案数. 思路: dp[i]表示i个物品都乱序排序的方案数,所以状态转移方程.考虑i-1个物品乱序,放入第i个物品一定要和i-1个的其中一个 ...

  7. C# winform 界面美化技巧(扁平化设计)

    关于C#界面美化的一些小技巧 在不使用第三方控件如 IrisSkin 的前提下,依然可以对winform做出让人眼前一亮的美化 首先,我们先来实现主界面的扁平化 此处分为两个步骤,第一步是更改winf ...

  8. Python 键盘记录

    之前写的键盘记录最后一直在纠结弹框与不弹框的问题,代码找不到了,今天重新来一遍 #!/usr/bin/env python# -*-coding:utf-8 -*-from ctypes import ...

  9. 【BZOJ】4144: [AMPPZ2014]Petrol

    题意 给定一个\(n\)个点.\(m\)条边的带权无向图,其中有\(s\)个点是加油站.每辆车都有一个油量上限\(b\),即每次行走距离不能超过\(b\),但在加油站可以补满.\(q\)次询问,每次给 ...

  10. swift初体验

    swift是一门类型安全的语言,同样也是基于c语言 那么c语言的一些类型也是实用的,不同的是:swift声明变量和常量是不一样的 let:用来修饰常量:var用来修饰变量 e.g: let num=1 ...