解剖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
]));
}
}
}
?
第四篇完
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

