解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数

系统 2020 0
原文: 解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)

解剖SQLSERVER 第四篇  OrcaMDF里对dates类型数据的解析(译)

http://improve.dk/parsing-dates-in-orcamdf/

在SQLSERVER里面有几种不同的date相关类型,当前OrcaMDF 支持三种最常用的date类型:date,datetime,smalldatetime

 

SqlDate实现

date 类型在三种类型之中是最简单的,他是一个3个字节的定长类型,存储了日期值它支持的日期范围从0001-01-01到9999-12-31

默认值是1900-01-01

比较坑爹的是.NET里面还没有任何标准实现能够支持3个字节的整数类型,只有short类型和int类型,但是,他们要不太大要不太小

另外,要正确读取日期值,对于.NET的4字节整型我们必须执行一些转变去获取正确的数字

一旦我们获取到date的值,我们可以创建一个默认的datetime类型并且添加天数进去

      
        public
      
      
        class
      
      
         SqlDate : ISqlType

{

    
      
      
        public
      
      
        bool
      
      
         IsVariableLength

    {

        
      
      
        get
      
       { 
      
        return
      
      
        false
      
      
        ; }

    }



    
      
      
        public
      
      
        short
      
      ?
      
         FixedLength

    {

        
      
      
        get
      
       { 
      
        return
      
      
        3
      
      
        ; }

    }



    
      
      
        public
      
      
        object
      
       GetValue(
      
        byte
      
      
        [] value)

    {

        
      
      
        if
      
       (value.Length != 
      
        3
      
      
        )

            
      
      
        throw
      
      
        new
      
       ArgumentException(
      
        "
      
      
        Invalid value length: 
      
      
        "
      
       +
      
         value.Length);



        
      
      
        //
      
      
         Magic needed to read a 3 byte integer into .NET's 4 byte representation.

        
      
      
        //
      
      
         Reading backwards due to assumed little endianness.
      
      
        int
      
       date = (value[
      
        2
      
      ] << 
      
        16
      
      ) + (value[
      
        1
      
      ] << 
      
        8
      
      ) + value[
      
        0
      
      
        ];



        
      
      
        return
      
      
        new
      
       DateTime(
      
        1
      
      , 
      
        1
      
      , 
      
        1
      
      
        ).AddDays(date);

    }

}
      
    

相关测试

      
        using
      
      
         System;


      
      
        using
      
      
         NUnit.Framework;


      
      
        using
      
      
         OrcaMDF.Core.Engine.SqlTypes;


      
      
        namespace
      
      
         OrcaMDF.Core.Tests.Engine.SqlTypes

{

    [TestFixture]


      
      
        public
      
      
        class
      
      
         SqlDateTests

{

        [Test]


      
      
        public
      
      
        void
      
      
         GetValue()

{


      
      
        var
      
       type = 
      
        new
      
      
         SqlDate();


      
      
        var
      
       input = 
      
        new
      
      
        byte
      
      [] { 
      
        0xf6
      
      , 
      
        0x4c
      
      , 
      
        0x0b
      
      
         };

Assert.AreEqual(
      
      
        new
      
       DateTime(
      
        2028
      
      , 
      
        09
      
      , 
      
        09
      
      
        ), Convert.ToDateTime(type.GetValue(input)));

input 
      
      = 
      
        new
      
      
        byte
      
      [] { 
      
        0x71
      
      , 
      
        0x5c
      
      , 
      
        0x0b
      
      
         };

Assert.AreEqual(
      
      
        new
      
       DateTime(
      
        2039
      
      , 
      
        07
      
      , 
      
        17
      
      
        ), Convert.ToDateTime(type.GetValue(input)));

}

        [Test]


      
      
        public
      
      
        void
      
      
         Length()

{


      
      
        var
      
       type = 
      
        new
      
      
         SqlDate();

Assert.Throws
      
      <ArgumentException>(() => type.GetValue(
      
        new
      
      
        byte
      
      [
      
        2
      
      
        ]));

Assert.Throws
      
      <ArgumentException>(() => type.GetValue(
      
        new
      
      
        byte
      
      [
      
        4
      
      
        ]));

}

}

}
      
    

 

SqlDateTime实现

date类型只能存储日期,而datetime类型不但能存储date也能存储time

datetime存储8字节定长数据值,第一部分是time(4字节),而第二部分是date(4字节)

计算date部分跟上面介绍date类型基本上一样,不过这一次date部分是一个四字节整数,比上面的例子容易处理多了,上面的date类型是3个字节

time部分存储为自午夜时的ticks数,一个tick就是1/300th 秒,为了显示tick值,我们首先定义一个常量,常量值是10d/3d

time的各个部分实际同样存储在同一个整型值里面(比如时间,分钟,秒,毫秒),所以我们要独立访问这些单独的部分,我们必须

要执行一些转换 (包括取模和相除)

      
        部分     计算

小时   X 
      
      / 
      
        300
      
       / 
      
        60
      
       / 
      
        60
      
      
        

分钟   X 
      
      / 
      
        300
      
       / 
      
        60
      
       % 
      
        60
      
      
        

秒     X 
      
      / 
      
        300
      
       % 
      
        60
      
      
        

毫秒   X 
      
      % 
      
        300
      
       * 10d / 3d
    
      
        public
      
      
        class
      
      
         SqlDateTime : ISqlType

{

    
      
      
        private
      
      
        const
      
      
        double
      
       CLOCK_TICK_MS = 10d/
      
        3d;



    
      
      
        public
      
      
        bool
      
      
         IsVariableLength

    {

        
      
      
        get
      
       { 
      
        return
      
      
        false
      
      
        ; }

    }



    
      
      
        public
      
      
        short
      
      ?
      
         FixedLength

    {

        
      
      
        get
      
       { 
      
        return
      
      
        8
      
      
        ; }

    }



    
      
      
        public
      
      
        object
      
       GetValue(
      
        byte
      
      
        [] value)

    {

        
      
      
        if
      
       (value.Length != 
      
        8
      
      
        )

            
      
      
        throw
      
      
        new
      
       ArgumentException(
      
        "
      
      
        Invalid value length: 
      
      
        "
      
       +
      
         value.Length);



        
      
      
        int
      
       time = BitConverter.ToInt32(value, 
      
        0
      
      
        );

        
      
      
        int
      
       date = BitConverter.ToInt32(value, 
      
        4
      
      
        );



        
      
      
        return
      
      
        new
      
       DateTime(
      
        1900
      
      , 
      
        1
      
      , 
      
        1
      
      , time/
      
        300
      
      /
      
        60
      
      /
      
        60
      
      , time/
      
        300
      
      /
      
        60
      
      %
      
        60
      
      , time/
      
        300
      
      %
      
        60
      
      , (
      
        int
      
      )Math.Round(time%
      
        300
      
      *
      
        CLOCK_TICK_MS)).AddDays(date);

    }

}
      
    

相关测试

      
        using
      
      
         System;


      
      
        using
      
      
         NUnit.Framework;


      
      
        using
      
      
         OrcaMDF.Core.Engine.SqlTypes;


      
      
        namespace
      
      
         OrcaMDF.Core.Tests.Engine.SqlTypes

{

    [TestFixture]


      
      
        public
      
      
        class
      
      
         SqlDateTimeTests

{

        [Test]


      
      
        public
      
      
        void
      
      
         GetValue()

{


      
      
        var
      
       type = 
      
        new
      
      
         SqlDateTime();


      
      
        byte
      
      
        [] input;

input 
      
      = 
      
        new
      
      
        byte
      
      [] { 
      
        0x5e
      
      , 
      
        0x3b
      
      , 
      
        0x5d
      
      , 
      
        0x00
      
      , 
      
        0x25
      
      , 
      
        0x91
      
      , 
      
        0x00
      
      , 
      
        0x00
      
      
         };

Assert.AreEqual(
      
      
        new
      
       DateTime(
      
        2001
      
      , 
      
        09
      
      , 
      
        25
      
      , 
      
        05
      
      , 
      
        39
      
      , 
      
        26
      
      , 
      
        820
      
      
        ), (DateTime)type.GetValue(input));

input 
      
      = 
      
        new
      
      
        byte
      
      [] { 
      
        0xb6
      
      , 
      
        0x87
      
      , 
      
        0xf0
      
      , 
      
        0x00
      
      , 
      
        0xd1
      
      , 
      
        0x8b
      
      , 
      
        0x00
      
      , 
      
        0x00
      
      
         };

Assert.AreEqual(
      
      
        new
      
       DateTime(
      
        1997
      
      , 
      
        12
      
      , 
      
        31
      
      , 
      
        14
      
      , 
      
        35
      
      , 
      
        44
      
      , 
      
        607
      
      
        ), (DateTime)type.GetValue(input));

input 
      
      = 
      
        new
      
      
        byte
      
      [] { 
      
        0x2d
      
      , 
      
        0xfd
      
      , 
      
        0x1c
      
      , 
      
        0x01
      
      , 
      
        0x4a
      
      , 
      
        0x75
      
      , 
      
        0x00
      
      , 
      
        0x00
      
      
         };

Assert.AreEqual(
      
      
        new
      
       DateTime(
      
        1982
      
      , 
      
        03
      
      , 
      
        18
      
      , 
      
        17
      
      , 
      
        17
      
      , 
      
        36
      
      , 
      
        790
      
      
        ), (DateTime)type.GetValue(input));

input 
      
      = 
      
        new
      
      
        byte
      
      [] { 
      
        0xff
      
      , 
      
        0x81
      
      , 
      
        0x8b
      
      , 
      
        0x01
      
      , 
      
        0x7f
      
      , 
      
        0x24
      
      , 
      
        0x2d
      
      , 
      
        0x00
      
      
         };

Assert.AreEqual(
      
      
        new
      
       DateTime(
      
        9999
      
      , 
      
        12
      
      , 
      
        31
      
      , 
      
        23
      
      , 
      
        59
      
      , 
      
        59
      
      , 
      
        997
      
      
        ), (DateTime)type.GetValue(input));

}

        [Test]


      
      
        public
      
      
        void
      
      
         Length()

{


      
      
        var
      
       type = 
      
        new
      
      
         SqlDateTime();

Assert.Throws
      
      <ArgumentException>(() => type.GetValue(
      
        new
      
      
        byte
      
      [
      
        9
      
      
        ]));

Assert.Throws
      
      <ArgumentException>(() => type.GetValue(
      
        new
      
      
        byte
      
      [
      
        7
      
      
        ]));

}

}

}
      
    

 

 

SqlSmallDateTime实现

Smalldatetime 是一个不错的数据类型当你需要存储范围值内的日期值(1900~2079)并且他能精确到秒

大多数场景下,精确到秒已经足够了,在一个范围的时间间隔内和精确值不需要太精确的情况下会节省很多空间

smalldatetime 数据类型会只占用4个字节,前2个字节存储自午夜的分钟数,后2个字节存储日期,默认值是1900-1-1

处理的方法跟datetime差不多,只不过使用更小的范围

      
        部分     计算

小时    X 
      
      / 
      
        60
      
      
        

分钟    X 
      
      % 
      
        60
      
    
      
        public
      
      
        class
      
      
         SqlSmallDateTime : ISqlType

{

    
      
      
        public
      
      
        bool
      
      
         IsVariableLength

    {

        
      
      
        get
      
       { 
      
        return
      
      
        false
      
      
        ; }

    }



    
      
      
        public
      
      
        short
      
      ?
      
         FixedLength

    {

        
      
      
        get
      
       { 
      
        return
      
      
        4
      
      
        ; }

    }



    
      
      
        public
      
      
        object
      
       GetValue(
      
        byte
      
      
        [] value)

    {

        
      
      
        if
      
       (value.Length != 
      
        4
      
      
        )

            
      
      
        throw
      
      
        new
      
       ArgumentException(
      
        "
      
      
        Invalid value length: 
      
      
        "
      
       +
      
         value.Length);



        
      
      
        ushort
      
       time = BitConverter.ToUInt16(value, 
      
        0
      
      
        );

        
      
      
        ushort
      
       date = BitConverter.ToUInt16(value, 
      
        2
      
      
        );



        
      
      
        return
      
      
        new
      
       DateTime(
      
        1900
      
      , 
      
        1
      
      , 
      
        1
      
      , time / 
      
        60
      
      , time % 
      
        60
      
      , 
      
        0
      
      
        ).AddDays(date);

    }

}
      
    

相关测试

      
        using
      
      
         System;


      
      
        using
      
      
         NUnit.Framework;


      
      
        using
      
      
         OrcaMDF.Core.Engine.SqlTypes;


      
      
        namespace
      
      
         OrcaMDF.Core.Tests.Engine.SqlTypes

{

    [TestFixture]


      
      
        public
      
      
        class
      
      
         SqlSmallDateTimeTests

{

        [Test]


      
      
        public
      
      
        void
      
      
         GetValue()

{


      
      
        var
      
       type = 
      
        new
      
      
         SqlSmallDateTime();


      
      
        var
      
       input = 
      
        new
      
      
        byte
      
      [] { 
      
        0xab
      
      , 
      
        0x02
      
      , 
      
        0x5d
      
      , 
      
        0x26
      
      
         };

Assert.AreEqual(
      
      
        new
      
       DateTime(
      
        1926
      
      , 
      
        11
      
      , 
      
        22
      
      , 
      
        11
      
      , 
      
        23
      
      , 
      
        0
      
      
        ), Convert.ToDateTime(type.GetValue(input)));

input 
      
      = 
      
        new
      
      
        byte
      
      [] { 
      
        0x49
      
      , 
      
        0x03
      
      , 
      
        0x99
      
      , 
      
        0x09
      
      
         };

Assert.AreEqual(
      
      
        new
      
       DateTime(
      
        1906
      
      , 
      
        9
      
      , 
      
        24
      
      , 
      
        14
      
      , 
      
        1
      
      , 
      
        0
      
      
        ), Convert.ToDateTime(type.GetValue(input)));

}

        [Test]


      
      
        public
      
      
        void
      
      
         Length()

{


      
      
        var
      
       type = 
      
        new
      
      
         SqlSmallDateTime();

Assert.Throws
      
      <ArgumentException>(() => type.GetValue(
      
        new
      
      
        byte
      
      [
      
        3
      
      
        ]));

Assert.Throws
      
      <ArgumentException>(() => type.GetValue(
      
        new
      
      
        byte
      
      [
      
        5
      
      
        ]));

}

}

}
      
    

 

第四篇完

解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论