2021/06/18

JSON 和 SQL Server 日期轉換上的時區地雷

前端開發了一段時間,終於也讓我遇到日期時區關卡。(笑)

以下的時區為【+08:00】。

JSON 在日期轉換上嚴守 ISO8601 日期規則,並且一律按世界協調時間 (UTC+0) 傳送,導致伺服器接收到的日期都會是少一天的情形。

伺服器接收 ISO8601 日期

原本伺服器接收到 JSON 後,僅做文字擷取後直接送 SQL Server 資料庫,意外就這樣發生了。

SQL Server 面對 ISO8601 文字格式也僅僅只做「所見即所得」的日期轉換,不會做時區處理。

加上時區文字後:


SQL Server 無法識別時區區塊,導致轉換失敗。這裡值得一提的是在「完成時間」的地方,是完整的 ISO8601 日期格式,看來 SQL Server 已知用火,確實有支援 ISO8601 日期。

既然如此,那就用新日期格式 DateTime2 來試試:


以上可以得知:

  • SQL Server 不會對 ISO8601 日期進行時區轉換,僅做日期格式轉換。
  • DateTime 格式不支援 ISO8601 時區區塊
  • DateTime2 完整支援 ISO8601 日期格式。

資料庫不足的地方只能使用後端程式進行轉換,以 Delphi 為例:

ISO8601 日期文字沒有時區內容時,則 UTC 日期時區偏移 0,本機時區 +08:00。


ISO8601 日期文字有時區內容時,則 UTC 日期時區偏移 -08:00,本機時區時區偏移 0。


 

結論

如果是新專案,日期格式一律存放 UTC+0 日期,後端不需處理,而前端 JavaScript 會自動轉換對應時區,算是對前後端都很友善的選項。

前端是採用沒有自動轉換時區的開發工具,就辛苦一點手動處理。(攤手)

萬一是承接舊專案,或在已有資料庫的情形建構前端網站,那資料庫日期十之八九都可能會是以該時區日期存放,此時就會建議後端傳送或接收都要進行本機時區轉換。


不知不覺就掃掉全球化的地雷,哈!

See also

2021/05/25

EasyUI Form 讀取 API 的知識點

 

EasyUI Form 事件流程
EasyUI Form 事件流程

從原生到三方元件

HTML 原生的 Form 使用上很容易,原本也要採用這樣的設計,但為了界面一致性,採用 EasyUI Textbox 等元件配置,但 EasyUI 元件實際在渲染時會和我們預期配置不同,這也造成上傳時有些要注意的地方。例如設計的 input class="easyui-passwordbox" 就會被拆成兩個 Html 標籤處理:

2021/05/14

HTML Banner 設計:CSS 漸層 vs. Canvas 繪製



在網頁復刻應用程式時,第一關面對的就是 UI 重建,而最先遇到的,常常就是那個佔據網頁最顯眼位置的傢伙——介面橫幅 (Banner)。在重建的過程中,難免會把以前的開發套路帶進來,就像我一樣,結果就是浪費了一堆寶貴的時間。

 

Banner 結構解析:化繁為簡

要重建橫幅,就像組模型一樣,要先把它解構成一個個小零件,再重新組裝起來。以這個 Banner 來說,內容如下:

  • 漸層方塊
  • 主標題
  • 子標題

就這三個元件,搞得自己是灰頭土臉...

2021/05/03

How to embedded DBX DLL for Deployment in project, take Devart Driver as an example.


Table of Contents


English Version

Studying Devart dbExpress driver, readme file for SQL Server :

Users of dbExpress driver for SQL Server with Source Code can embed the driver into the application directly. For information on how to do this refer to Borland documentation.

This sentence mainly explains that Devart company can use certain mechanisms to make the existence of DBX Dll unnecessary when deploying the project, for example: dbexpsda40.dll and so on.

No need to deploy the advantages of DBX DLL

Some DBX Drivers need to install the DB Client, but Devart provides a [Direct Connection] mode, in which the database access components can be directly connected to the DB via TCP without the DB Client. In addition to omitting the conversion of DB Client, there is no need for additional deployment of DBX DLL settings, which can effectively eliminate potential system configuration problems and save time for publishing system configuration.

Disadvantages of not needing to deploy DBX DLL


Although there is no need to deploy DLL, based on the principle of equivalent exchange, the capacity of the DLL will be filled into the EXE we want to deployment, which will cause the execution file to become larger - it grows about 487 Kb.

Prerequisites for use


You need to purchase [version with source code] first, and exchange for knowledge achievements with a small price, I think it is worth it!

How to

After the purchase of the [Include source code version] condition is established, and you only need to add the [DbxSdaDriverLoader] unit to the project, supplemented by the following code, to achieve the goal of [No DBX DLL required]:

uses
  Data.DB, Data.SqlExpr, DBXDevartSQLServer, DbxSdaDriverLoader;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  SQLConnection1.DriverName := 'DevartSQLServerDirectBuiltin';
  SQLConnection1.LibraryName := 'dbexpsda41.dll';
  SQLConnection1.VendorLib := '';  
  SQLConnection1.GetDriverFunc := 'getSQLDriverSQLServerDirect';
  SQLConnection1.Params.Values['OS Authentication'] := 'False';
  SQLConnection1.Params.Values['HostName'] := 'Host';  
  SQLConnection1.Params.Values['User_Name'] := 'sa';
  SQLConnection1.Params.Values['Password'] := '';
  SQLConnection1.Params.Values['Database'] := 'Database';
  SQLConnection1.LoginPrompt := False;
  SQLConnection1.Connected := True;
end;


Summary

Developers using DBX don’t actually need to rush to replace components. Sometimes spend some cost, can bring more value.

The above information is shared with you!

================================================================

中文版

在研究 Devart dbExpress driver for SQL Server 的 Readme 檔案時,我發現了一句古代文字:

Users of dbExpress driver for SQL Server with Source Code can embed the driver into the application directly. For information on how to do this refer to Borland documentation.
這句話主要在說明 Devart 它可以利用某些機制,使佈署專案時可以不需要 DBX Dll 的存在,例如:dbexpsda40.dll 等。

不需佈署 DBX DLL 的優點

有些 DBX Driver 需要安裝 DB Client,但 Devart 提供【直接連接】模式,在此模式下可以讓資料庫存取元件在不用 DB Client 的場合下透過 TCP 直接連接 DB。除了少掉 DB Client 的轉換外,更不需要額外佈署 DBX DLL 的設定,可以有效排除潛在的系統設定問題和節省發佈系統的設定時間。

不需佈署 DBX DLL 的缺點


雖說不用佈署 DLL,但基於等價交換原則,DLL 的容量會填充到我們欲發佈的 EXE 上面,進而造成執行檔變大的情形 -- 大約成長 487 Kb 左右。

使用前提

方便部署的前提為【購買含原始碼版本】,用少許代價換取知識成果,我認為值得!

如何設計

在使用前提【購買含原始碼版本】條件成立下,之後只需在專案中加入【DbxSdaDriverLoader】單元,並輔以底下程式碼,便能達成【不需 DBX DLL】的目標:


uses
  Data.DB, Data.SqlExpr, DBXDevartSQLServer, DbxSdaDriverLoader;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  SQLConnection1.DriverName := 'DevartSQLServerDirectBuiltin';
  SQLConnection1.LibraryName := 'dbexpsda41.dll';
  SQLConnection1.VendorLib := '';  
  SQLConnection1.GetDriverFunc := 'getSQLDriverSQLServerDirect';
  SQLConnection1.Params.Values['OS Authentication'] := 'False';
  SQLConnection1.Params.Values['HostName'] := 'Host';  
  SQLConnection1.Params.Values['User_Name'] := 'sa';
  SQLConnection1.Params.Values['Password'] := '';
  SQLConnection1.Params.Values['Database'] := 'Database';
  SQLConnection1.LoginPrompt := False;
  SQLConnection1.Connected := True;
end;


總結

使用 DBX 的開發者其實不需要急著更換元件,有時使用更好的 Driver 也能夠達到【花得更少,用得更好】的目標,用少許的金錢所帶來的經濟效益遠超乎想像。


以上資訊和你分享!


See also

2021/04/26

Dbexpress (DBX) Factory for SQL SERVER

之前在這篇【那些年 DBeXpress 所教我的事:DBXCommon 單元】文章中提到 DBXCommon 是 Dbexpress (以下簡稱 DBX) 最底層的單元,像是 TDBXCommand 和 TDBXReader 便是 TSQLQuery 等 DBX DataSet 所包裝的內容,同時也是 DataSnap 底層架構,所以掌握 TDBXReader 就可說是掌握 DataSnap 效能這件事一點都不為過。

2021/04/19

Delphi 特規 JSON 和 JavaScript Object (2)

 


前情提要:Delphi 特規 JSON 和 JavaScript Object (1)

已知大匠可以將 Blob 型態轉為 Hex JSON Array,但容量會放大 4 倍是硬傷;而 Memo 型態卻也被視為 Blob 型態,導致傳輸內容更加地龐大,更有堪者,可達放大 6 倍之譜。


追綜原因

Delphi 轉換 JSON 封包主要是利用 TDBXJSONTools.TableToJSON 函式庫內容,它會利用 TDBXReader.ValueType 進行 Delphi Value to JSON Value 轉換:

JsonCell := DBXToJSON(Value.Value[I], Value.ValueType[I].DataType, IsLocalConnection);


而 DBXToJSON 裡對 DataType 解析,節錄如下:

2021/04/16

Delphi 特規 JSON 和 JavaScript Object (1)



Delphi 開發這麼久,一直對 TFDMemTable 等 TDataSet 轉成 JSON 內容很有意見,在預設條件下,產出的是【特規】JSON 格式,如下圖所示:


為了方便說明,底下【特規 JSON 】一律使用【DJSON 】代換。


一般 JavaScript 前端大部份的物件所接受的 JSON 格式會是:

[{"EmpNo":2,"LastName":Nelson"}, {"EmpNo":4,"LastName":Eden"}]


Delphi 預設的格式不適用在前端,而 10.2 版開始有提供轉換函式來產出前端常用的 JSON 格式。

特規有特規的好處

當時為了達到這功能的我感到非常困惑,為什麼這功能要等這麼久才能實現?

後來想想,其實這特規確實有存在的必要。

因為,剛剛我們有看到前端常用的 JSON 格式會有【欄位名稱重複】的情形,像是上述的 EmpNo, LastName 每一筆都會出現一次,當筆數一多時,過多的重複資料會產生不必要的傳輸量,這時採用 DJSON 格式反而會是比較好的選擇。

前端的硬體效能都堪比伺服器,在前端轉換更能有效分配資源。