下面通過一個例子進行說明。
SQL> create table test_date (date_col date);
表已創建。SQL> insert into test_date values (to_date('2000-1-1 0:0:0', 'yyyy-mm-dd hh24:mi:ss'));
已創建 1 行。
SQL> insert into test_date values (to_date('1-1-1 0:0:0', 'yyyy-mm-dd hh24:mi:ss'));
已創建 1 行。
SQL> insert into test_date values (to_date('-1-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'));
已創建 1 行。
SQL> insert into test_date values (to_date('-101-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'));
已創建 1 行。
SQL> insert into test_date values (to_date('-4712-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'));
已創建 1 行。
SQL> insert into test_date values (to_date('9999-12-31 23:59:59', 'syyyy-mm-dd hh24:mi:ss'));
已創建 1 行。
SQL> insert into test_date values (sysdate);
已創建 1 行。
SQL> insert into test_date values (to_date('-4713-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'));
insert into test_date values (to_date('-4713-1-1 0:0:0', 'syyyy-mm-dd hh24:mi:ss'))
*
ERROR 位于第 1 行:
ORA-01841: (全)年度值必須介于 -4713 和 +9999 之間,且不為 0 SQL> insert into test_date values (to_date('0000-1-1 0:0:0', 'yyyy-mm-dd hh24:mi:ss'));
insert into test_date values (to_date('0000-1-1 0:0:0', 'yyyy-mm-dd hh24:mi:ss'))
*
ERROR 位于第 1 行:
ORA-01841: (全)年度值必須介于 -4713 和 +9999 之間,且不為 0 SQL> col dump_date format a80
SQL> select to_char(date_col, 'syyyy-mm-dd hh24:mi:ss'), dump(date_col) dump_date from test_date;
TO_CHAR(DATE_COL,'SY DUMP_DATE
-------------------- ---------------------------------------
2000-01-01 00:00:00 Typ=12 Len=7: 120,100,1,1,1,1,1
0001-01-01 00:00:00 Typ=12 Len=7: 100,101,1,1,1,1,1
-0001-01-01 00:00:00 Typ=12 Len=7: 100,99,1,1,1,1,1
-0101-01-01 00:00:00 Typ=12 Len=7: 99,99,1,1,1,1,1
-4712-01-01 00:00:00 Typ=12 Len=7: 53,88,1,1,1,1,1
9999-12-31 23:59:59 Typ=12 Len=7: 199,199,12,31,24,60,60
2004-12-15 13:56:19 Typ=12 Len=7: 120,104,12,15,14,57,20
已選擇7行。通過最后兩條語句已經可以看出Oracle的DATE類型的取值范圍是公元前4712年1月1日至公元9999年12月31日。而且根據日期的特定,要不然是公元1年,要不然是公元前1年,不會出現0年的情況。
日期類型長度是7,7個字節分別表示世紀、年、月、日、時、分和秒。
由于不會出現0的情況,月和日都是按照原值存儲的,月的范圍是1~12,日的范圍是1~31。
由于時、分、秒都會出現0的情況,因此存儲時采用原值加1的方式。0時保存為1,13時保存為14,23時保存為24。分和秒的情況與小時類似。小時的范圍是0~23,在數據庫中以1~24保存。分和秒的范圍都是0~59,在數據庫中以1~60保存。
年和世紀的情況相對比較復雜,可分為公元前和公元后兩種情況。由于最小的世紀的值是-47(公元前4712年),最大值是99(公元9999年)。為了避免負數的產生,oracle把世紀加100保存在數據庫中。公元2000年,世紀保存為120,公元9999年,世紀保存為199,公元前101年,世紀保存為99(100+(-1)),公元前4712年,世紀保存為53(100+(-47))。
注重,對于公元前1年,雖然已經是公元前了,但是表示世紀的前兩位的值仍然是0,因此,這時的保存的世紀的值仍然是100。世紀的范圍是-47~99,保存的值是53~199。
年的保存與世紀的保存方式類似,也把年的值加上100進行保存。對于公元2000年,年保持為100,公元1年保存為101,公元2004年保存為104,公元9999年保存為199,公元前1年,保存為99(100+(-1)),公元前101年,保存為99(100+(-1)),公元前4712年保存為88(100+(-12))。對于公元前的年,保存的值總是小于等于100,對于公元后的年,保存的值總是大于等于100。年的范圍是0~99,保存的值是1~199。
注重:一般的世紀,都包含了100年,而對于0世紀,由于包含公元前和公元后兩部分且不包含0年,因此包含了198年。
下面通過一個例子進行說明。
SQL> create table test_time (col_time timestamp);
表已創建。
SQL> insert into test_time values (to_timestamp('0001-1-1 0:0:0.0', 'syyyy-mm-dd hh24:mi:ss.ff'));
已創建 1 行。
SQL> insert into test_time values (to_timestamp('2000-1-1 0:0:0.0', 'syyyy-mm-dd hh24:mi:ss.ff'));
已創建 1 行。
SQL> insert into test_time values (to_timestamp('9999-12-31 23:59:59.999999', 'syyyy-mm-dd hh24:mi:ss.ff'));
已創建 1 行。
SQL> insert into test_time values (to_timestamp('-0001-1-1 0:0:0.0', 'syyyy-mm-dd hh24:mi:ss.ff'));
已創建 1 行。
SQL> insert into test_time values (to_timestamp('-0100-3-4 13:2:3.234015', 'syyyy-mm-dd hh24:mi:ss.ff'));
已創建 1 行。
SQL> insert into test_time values (systimestamp);
已創建 1 行。
SQL> insert into test_time values (to_timestamp('2000-1-1 0:0:0.123456789', 'syyyy-mm-dd hh24:mi:ss.ff9'));
已創建 1 行。
SQL> commit;
提交完成。
SQL> select to_char(col_time, 'syyyy-mm-dd hh24:mi:ss.ff9') time, dump(col_time) dump_time
2 from test_time;
TIME DUMP_TIME
------------------------------ ----------------------------------------------------
0001-01-01 00:00:00.000000000 Typ=180 Len=7: 100,101,1,1,1,1,1
2000-01-01 00:00:00.000000000 Typ=180 Len=7: 120,100,1,1,1,1,1
9999-12-31 23:59:59.999999000 Typ=180 Len=11: 199,199,12,31,24,60,60,59,154,198,24
-0001-01-01 00:00:00.000000000 Typ=180 Len=7: 100,99,1,1,1,1,1
-0100-03-04 13:02:03.234015000 Typ=180 Len=11: 99,100,3,4,14,3,4,13,242,201,24
2004-12-15 16:14:52.738000000 Typ=180 Len=11: 120,104,12,15,17,15,53,43,252,252,128
2000-01-01 00:00:00.123457000 Typ=180 Len=11: 120,100,1,1,1,1,1,7,91,205,232
已選擇7行。
與DATE類型對比可以發現,對于TIMESTAMP類型,假如不包含微秒信息或者微秒值為0,那么存儲結果和DATE完全相同。當微秒值為0時,Oracle為了節省空間,不會保存微秒信息。
假如毫秒值不為0,Oracle把微秒值當作一個9位數的數字來保存。
比如999999000,保存為59,154,198,24。234015000保存為13,242,201,24。
SQL> select to_char(999999000, 'xxxxxxxxxx') from dual;
TO_CHAR(999
-----------
3b9ac618
SQL> select to_number('3b', 'xxx') one, to_number('9a', 'xxx') two,
2 to_number('c6', 'xxx') three, to_number('18', 'xxx') four from dual;
ONE TWO THREE FOUR
---------- ---------- ---------- ----------
59 154 198 24
SQL> select to_char(234015000, 'xxxxxxxx') from dual;
TO_CHAR(2
---------
df2c918
SQL> select to_number('d', 'xxx') one, to_number('f2', 'xxx') two,
2 to_number('c9', 'xxx') three, to_number('18', 'xxx') four from dual;
ONE TWO THREE FOUR
---------- ---------- ---------- ----------
13 242 201 24
另外,注重一點,不指定精度的情況下,TIMESTAMP默認取6位。長度超過6位,會四舍五入到6位。假如希望保存9位的TIMESTAMP,必須明確指定精度。
SQL> alter table test_time modify (col_time timestamp(9));
表已更改。
SQL> insert into test_time values (to_timestamp('2000-1-1 0:0:0.123456789', 'syyyy-mm-dd hh24:mi:ss.ff9'));
已創建 1 行。
SQL> select to_char(col_time, 'syyyy-mm-dd hh24:mi:ss.ff9') time, dump(col_time) dump_time
2 from test_time;
TIME DUMP_TIME
------------------------------ ---------------------------------------------------
0001-01-01 00:00:00.000000000 Typ=180 Len=7: 100,101,1,1,1,1,1
2000-01-01 00:00:00.000000000 Typ=180 Len=7: 120,100,1,1,1,1,1
9999-12-31 23:59:59.999999000 Typ=180 Len=11: 199,199,12,31,24,60,60,59,154,198,24
-0001-01-01 00:00:00.000000000 Typ=180 Len=7: 100,99,1,1,1,1,1
-0100-03-04 13:02:03.234015000 Typ=180 Len=11: 99,100,3,4,14,3,4,13,242,201,24
2004-12-15 16:14:52.738000000 Typ=180 Len=11: 120,104,12,15,17,15,53,43,252,252,128
2000-01-01 00:00:00.123457000 Typ=180 Len=11: 120,100,1,1,1,1,1,7,91,205,232
2000-01-01 00:00:00.123456789 Typ=180 Len=11: 120,100,1,1,1,1,1,7,91,205,21
已選擇8行。
假如直接在SQL語句中對SYSDATE或由TO_DATE函數生成日期進行DUMP操作,會發現得到的結果與DUMP數據庫中保存的日期的結果不一樣。
SQL> truncate table test_date;
表已截掉。
SQL> insert into test_date values (to_date('2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss'));
已創建 1 行。
SQL> col dump_date format a65
SQL> select to_char(date_col, 'syyyy-mm-dd hh24:mi:ss') dat, dump(date_col) dump_date from test_date;
DAT DUMP_DATE
-------------------- ---------------------------------------------------------
2004-12-17 16:42:42 Typ=12 Len=7: 120,104,12,17,17,43,43
SQL> select to_char(to_date('2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss'), 'syyyy-mm-dd hh24:mi:ss') dat,
2 dump(to_date('2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss')) dump_date from dual;
DAT DUMP_DATE
-------------------- ---------------------------------------------------------
2004-12-17 16:42:42 Typ=13 Len=8: 212,7,12,17,16,42,42,0
存儲在數據庫中的DATE類型是12,而直接在SQL中使用的DATE類型是13。而且二者的長度以及表示方式都不相同。這兩種類型的不同指出主要體現在兩點:一:時、分、秒的表示不同;二、世紀和年的表示不同。
SQL中使用DATE的時分秒沒有采用加1存儲方式,而且原值存儲。
SQL中使用DATE沒有采用世紀、年的方式保持,而是采用了按數值保存的方式。第一位表示低位,第二位表示高位。低位表示最大的值是255。如上面的例子中,212+7×256=2004。
SQL> select to_char(to_date('-2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss'), 'syyyy-mm-dd hh24:mi:ss') dat,
2 dump(to_date('-2004-12-17 16:42:42', 'syyyy-mm-dd hh24:mi:ss')) dump_date from dual;
DAT DUMP_DATE
-------------------- ---------------------------------------------------
-2004-12-17 16:42:42 Typ=13 Len=8: 44,248,12,17,16,42,42,0
SQL> select dump(to_date('-1-1-1', 'syyyy-mm-dd')) from dual;
DUMP(TO_DATE('-1-1-1','SYYYY-MM-D
---------------------------------
Typ=13 Len=8: 255,255,1,1,0,0,0,0
對于公元前的日期,Oracle從255,255開始保存。公元前的年的保存的值和對應的公元后的年的值相加的和是256,255。如上例中的公元2004年和公元前2004年的值相加:212+44=256,7+248=255。
SQL中DATE類型最后還包括一個0,似乎目前沒有使用。SQL> CREATE TABLE TEST_TIMESTAMP(TIME1 TIMESTAMP(9), TIME2 TIMESTAMP(6) WITH LOCAL TIME ZONE,
2 TIME3 TIMESTAMP(4) WITH TIME ZONE);
表已創建。
SQL> INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
已創建 1 行。
SQL> SELECT * FROM TEST_TIMESTAMP;
TIME1
----------------------------------------------------
TIME2
----------------------------------------------------
TIME3
----------------------------------------------------
11-1月 -05 11.08.15.027000000 下午
11-1月 -05 11.08.15.027000 下午
11-1月 -05 11.08.15.0270 下午 +08:00
SQL> SELECT DUMP(TIME1, 16), DUMP(TIME2, 16), DUMP(TIME3, 16) FROM TEST_TIMESTAMP;
DUMP(TIME1,16)
-------------------------------------------------------------
DUMP(TIME2,16)
-------------------------------------------------------------
DUMP(TIME3,16)
-------------------------------------------------------------
Typ=180 Len=11: 78,69,1,b,18,9,10,1,9b,fc,c0
Typ=231 Len=11: 78,69,1,b,18,9,10,1,9b,fc,c0
Typ=181 Len=13: 78,69,1,b,10,9,10,1,9b,fc,c0,1c,3c
可以發現,假如客戶端和數據庫中的時區是一致的,那么TIMESTAMP和TIMESTAMP WITH LOCAL TIME ZONE存儲的數據是完全一樣的。
TIMESTAMP WITH TIME ZONE則略有不同,它保存的是0時區的時間,和所處的時區信息。
修改客戶端主機的時區,由東8區(+8區)改為0時區。
SQL> INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
已創建 1 行。
修改客戶端主機的時區,改為西5區(-5時區)。
SQL> INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
已創建 1 行。
修改客戶端主機的時區,改為西12區(-12時區)。
SQL> INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
已創建 1 行。
修改客戶端主機的時區,改為東13區(+13時區)。
SQL> INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
已創建 1 行。
修改客戶端主機的時區,改為西3.5區(-3.5時區)。
SQL> INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
已創建 1 行。
修改客戶端主機的時區,改為東9.5區(+9.5時區)。
SQL> INSERT INTO TEST_TIMESTAMP VALUES (SYSTIMESTAMP, SYSTIMESTAMP, SYSTIMESTAMP);
已創建 1 行。
SQL> COMMIT;
提交完成。
修改客戶端主機的時區,改回東8區(+8時區)。
SQL> SELECT * FROM TEST_TIMESTAMP;
TIME1
-----------------------------------------------
TIME2
-----------------------------------------------
TIME3
-----------------------------------------------
11-1月 -05 11.08.15.027000000 下午
11-1月 -05 11.08.15.027000 下午
11-1月 -05 11.08.15.0270 下午 +08:00
11-1月 -05 03.11.43.746000000 下午
11-1月 -05 11.11.43.746000 下午
11-1月 -05 03.11.43.7460 下午 +00:00
11-1月 -05 10.14.08.987000000 上午
11-1月 -05 11.14.08.987000 下午
11-1月 -05 10.14.08.9870 上午 -05:00
11-1月 -05 03.15.01.732000000 上午
11-1月 -05 11.15.01.732000 下午
11-1月 -05 03.15.01.7320 上午 -12:00
12-1月 -05 04.20.21.522000000 上午
11-1月 -05 11.20.21.522000 下午
12-1月 -05 04.20.21.5220 上午 +13:00
11-1月 -05 02.15.16.567000000 下午
12-1月 -05 01.45.16.567000 上午
11-1月 -05 02.15.16.5670 下午 -03:30
12-1月 -05 03.16.54.992000000 上午
12-1月 -05 01.46.54.992000 上午
12-1月 -05 03.16.54.9920 上午 +09:30
已選擇7行。
SQL> SELECT DUMP(TIME1, 16), DUMP(TIME2, 16), DUMP(TIME3, 16) FROM TEST_TIMESTAMP;
DUMP(TIME1,16)
-------------------------------------------------------------
DUMP(TIME2,16)
-------------------------------------------------------------
DUMP(TIME3,16)
-------------------------------------------------------------
Typ=180 Len=11: 78,69,1,b,18,9,10,1,9b,fc,c0
Typ=231 Len=11: 78,69,1,b,18,9,10,1,9b,fc,c0
Typ=181 Len=13: 78,69,1,b,10,9,10,1,9b,fc,c0,1c,3c
Typ=180 Len=11: 78,69,1,b,10,c,2c,2c,77,e,80
Typ=231 Len=11: 78,69,1,b,18,c,2c,2c,77,e,80
Typ=181 Len=13: 78,69,1,b,10,c,2c,2c,77,e,80,14,3c
Typ=180 Len=11: 78,69,1,b,b,f,9,3a,d4,6c,c0
Typ=231 Len=11: 78,69,1,b,18,f,9,3a,d4,6c,c0
Typ=181 Len=13: 78,69,1,b,10,f,9,3a,d4,6c,c0,f,3c
Typ=180 Len=11: 78,69,1,b,4,10,2,2b,a1,6f,0
Typ=231 Len=11: 78,69,1,b,18,10,2,2b,a1,6f,0
Typ=181 Len=13: 78,69,1,b,10,10,2,2b,a1,6f,0,8,3c
Typ=180 Len=11: 78,69,1,c,5,15,16,1f,1d,16,80
Typ=231 Len=11: 78,69,1,b,18,15,16,1f,1d,16,80
Typ=181 Len=13: 78,69,1,b,10,15,16,1f,1d,16,80,21,3c
Typ=180 Len=11: 78,69,1,b,f,10,11,21,cb,bb,c0
Typ=231 Len=11: 78,69,1,c,2,2e,11,21,cb,bb,c0
Typ=181 Len=13: 78,69,1,b,12,2e,11,21,cb,bb,c0,11,1e
Typ=180 Len=11: 78,69,1,c,4,11,37,3b,20,b8,0
Typ=231 Len=11: 78,69,1,c,2,2f,37,3b,20,b8,0
Typ=181 Len=13: 78,69,1,b,12,2f,37,3b,20,b8,0,1d,5a
SQL> SELECT TO_NUMBER('1C', 'XXX'), TO_NUMBER('3C', 'XXX') FROM DUAL;
TO_NUMBER('1C','XXX') TO_NUMBER('3C','XXX')
--------------------- ---------------------
28 60
SQL> SELECT TO_NUMBER('14', 'XXX'), TO_NUMBER('3C', 'XXX'), TO_NUMBER('143C', 'XXXXXXX') FROM DUAL;
TO_NUMBER('14','XXX') TO_NUMBER('3C','XXX')
--------------------- ---------------------
20 60
SQL> SELECT TO_NUMBER('3C', 'XXX') , TO_NUMBER('1E', 'XXX'), TO_NUMBER('5A', 'XXX') FROM DUAL;
TO_NUMBER('3C','XXX') TO_NUMBER('1E','XXX') TO_NUMBER('5A','XXX')
--------------------- --------------------- -------------------
60 30 90
可以看出,修改時區會導致系統TIMESTAMP時間發生變化,但是對于TIMESTAMP WITH LOCAL TIME ZONE類型,總是將系統的時間轉化到數據庫服務器上時區的時間進行存儲。
TIMESTAMP WITH TIME ZONE保存的是當前時間轉化到0時區的對應的時間,并通過最后兩位來保存時區信息。
第一位表示時區的小時部分。0時區用0x14表示。東n區在這個基礎上加n,西n區在這個基礎上減n。我們所處的東8區表示為0x1C。西5區表示為0xF。
第二位表示時區的分鐘部分。標準是0x3C,即60分鐘。對于東時區的半區,在這個基礎上加上30分鐘,假如是西時區,則減去30分鐘。