Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Lebih lanjut tentang Pengenalan zona waktu dalam Proyek berumur panjang

Beberapa waktu lalu, kami mulai menyesuaikan sistem dengan pasar baru yang membutuhkan dukungan zona waktu. Penelitian awal telah dijelaskan pada artikel sebelumnya. Sekarang pendekatan tersebut sedikit berkembang di bawah pengaruh realitas. Artikel ini menjelaskan masalah yang dihadapi selama diskusi dan keputusan akhir yang diterapkan.

TL;DR

  • Perlu membedakan istilah:
    • UTC adalah waktu lokal di zona +00:00, tanpa efek DST
    • DateTimeOffset – offset waktu lokal dari UTC ± NN:NN, di mana offset adalah offset dasar dari UTC tanpa efek DST (dalam C# TimeZoneInfo.BaseUtcOffset)
    • DateTime – waktu lokal tanpa informasi tentang zona waktu (kami mengabaikan atribut Jenis)
  • Pisahkan penggunaan menjadi eksternal dan internal:
    • Data input dan output melalui API, pesan, ekspor/impor file harus benar-benar dalam UTC (tipe DateTime)
    • Di dalam sistem, data disimpan bersama dengan offset (tipe DateTimeOffset)
  • Pisahkan penggunaan dalam kode lama menjadi kode non-DB (C#, JS) dan DB:
    • Kode non-DB hanya beroperasi dengan nilai lokal (tipe DateTime)
    • Basis data bekerja dengan nilai lokal + offset (tipe DateTimeOffset)
  • Proyek baru (komponen) menggunakan DateTimeOffset.
  • Dalam database, tipe DateTime hanya berubah menjadi DateTimeOffset:
    • Jenis bidang dalam tabel
    • Dalam parameter prosedur tersimpan
    • Konstruksi yang tidak kompatibel diperbaiki dalam kode
    • Informasi offset dilampirkan ke nilai yang diterima (gabungan sederhana)
    • Sebelum kembali ke kode non-DB, nilai dikonversi ke lokal
  • Tidak ada perubahan pada kode non-DB
  • DST diselesaikan menggunakan CLR Stored Procedures (untuk SQL Server 2016 Anda dapat menggunakan AT TIME ZONE).

Sekarang, lebih detail tentang kesulitan yang diatasi.

Standar industri TI yang “berakar dalam”

Butuh waktu yang cukup lama untuk membebaskan orang dari rasa takut menyimpan kurma dalam waktu setempat dengan offset. Beberapa waktu lalu, jika Anda bertanya kepada programmer berpengalaman:“Bagaimana cara mendukung zona waktu?” – satu-satunya pilihan adalah:“Gunakan UTC dan ubah ke waktu lokal sebelum demonstrasi”. Fakta bahwa untuk alur kerja normal Anda masih memerlukan informasi tambahan, seperti offset dan nama zona waktu, tersembunyi di balik kap implementasi. Dengan munculnya DateTimeOffset, detail seperti itu keluar, tetapi kelembaman "pengalaman pemrograman" tidak memungkinkan untuk dengan cepat menyetujui fakta lain:"Menyimpan tanggal lokal dengan offset UTC dasar" sama dengan menyimpan UTC. Keuntungan lain menggunakan DateTimeOffset di mana-mana memungkinkan Anda untuk mendelegasikan kontrol atas kepatuhan terhadap zona waktu .NET Framework dan SQL Server, meninggalkan kontrol manusia hanya saat input dan output data dari sistem. Kontrol manusia adalah kode yang ditulis oleh programmer untuk bekerja dengan nilai tanggal/waktu.

Untuk mengatasi rasa takut ini, saya harus mengadakan lebih dari satu sesi dengan penjelasan, penyajian contoh dan Proof of Concept. Semakin sederhana dan dekat contoh dengan tugas-tugas yang diselesaikan dalam proyek, semakin baik. Jika Anda memulai diskusi "secara umum", ini mengarah pada kerumitan pemahaman dan membuang-buang waktu. Secara singkat:lebih sedikit teori – lebih banyak praktik. Argumen untuk UTC dan melawan DateTimeOffset dapat dikaitkan dengan dua kategori:

  • “UTC sepanjang waktu” adalah standar dan sisanya tidak berfungsi
  • UTC memecahkan masalah dengan DST

Perlu dicatat bahwa baik UTC maupun DateTimeOffset tidak memecahkan masalah dengan DST tanpa menggunakan informasi tentang aturan untuk mengkonversi antar zona, yang tersedia melalui kelas TimeZoneInfo di C#.

Model yang disederhanakan

Seperti yang saya sebutkan di atas, dalam kode lama, perubahan hanya terjadi di database. Ini dapat dinilai dengan menggunakan contoh sederhana.

Contoh model dalam T-SQL

// 1) data storage
// input data in the user's locale, as he sees them
declare @input_user1 datetime = '2017-10-27 10:00:00'

// there is information about the zone in the user configuration
declare @timezoneOffset_user1 varchar(10) = '+03:00'
 
declare @storedValue datetimeoffset

// upon receiving values, attach the user’s offset
set @storedValue = TODATETIMEOFFSET(@input_user1, @timezoneOffset_user1)

// this value will be saved
select @storedValue 'stored'
 
// 2) display of information
// a different time zone is specified in the second user’s configuration,
declare @timezoneOffset_user2 varchar(10) = '-05:00'

// before returning to the client code, values are reduced to local ones
// this is how the data will look like in the database and on users’ displays
select
@storedValue 'stored value',
CONVERT(DATETIME, SWITCHOFFSET(@storedValue, @timezoneOffset_user1)) 'user1 Moscow',
CONVERT(DATETIME, SWITCHOFFSET(@storedValue, @timezoneOffset_user2)) 'user2 NY'
 
// 3) now the second user saves the data
declare @input_user2 datetime

// input local values are received, as the user sees them in New York
set @input_user2 = '2017-10-27 02:00:00.000'

// link to the offset information
set @storedValue = TODATETIMEOFFSET(@input_user2, @timezoneOffset_user2)
select @storedValue 'stored'
 
// 4) display of information
select
@storedValue 'stored value',
CONVERT(DATETIME, SWITCHOFFSET(@storedValue, @timezoneOffset_user1)) 'user1 Moscow',
CONVERT(DATETIME, SWITCHOFFSET(@storedValue, @timezoneOffset_user2)) 'user2 NY'

Hasil dari eksekusi script adalah sebagai berikut.

Contoh menunjukkan bahwa model ini memungkinkan membuat perubahan hanya dalam database, yang secara signifikan mengurangi risiko cacat.

Contoh fungsi untuk memproses nilai tanggal/waktu

// When receiving values from the non-DB code in DateTimeOffset, they will be local, 
// but with offset +00:00, so you must attach a user’s offset, but you cannot convert between 
// time zones. To do this, we translate the value into DateTime and then back with the indication of the offset 
// DateTime is converted to DateTimeOffset without problems, 
// so you do not need to change the call of the stored procedures in the client code

create function fn_ConcatinateWithTimeOffset(@dto datetimeoffset, @userId int)
returns DateTimeOffset as begin
    declare @user_time_zone varchar(10)
    set @user_time_zone = '-05:00' // from the user's settings @userId
    return todatetimeoffset(convert(datetime, @dto), @user_time_zone)
end

// Client code cannot read DateTimeOffset into variables of the DateTime type, 
// so you need to not only convert to a correct time zone but also reduce to DateTime, 
// otherwise, there will be an error

create function fn_GetUserDateTime(@dto datetimeoffset, @userId int)
returns DateTime as begin
    declare @user_time_zone varchar(10)
    set @user_time_zone = '-05:00' // from the user's settings @userId
    return convert(datetime, switchoffset(@dto, @user_time_zone))
end

Artefak Kecil

Selama penyesuaian kode SQL, ditemukan beberapa hal yang berfungsi untuk DateTime, tetapi tidak kompatibel dengan DateTimeOffset:

GETDATE()+1 harus diganti dengan DATEADD (hari, 1, SYSDATETIMEOFFSET ())

Kata kunci DEFAULT tidak kompatibel dengan DateTimeOffset, Anda perlu menggunakan SYSDATETIMEOFFSET()

Konstruksi ISNULL(date_field, NULL)> 0″ bekerja dengan DateTime, tetapi DateTimeOffset harus diganti dengan "date_field IS NOT NULL"

Kesimpulan atau UTC vs DateTimeOffset

Seseorang mungkin memperhatikan bahwa, seperti dalam pendekatan dengan UTC, kami menangani konversi saat menerima dan mengembalikan data. Lalu mengapa kita membutuhkan semua ini, jika ada solusi yang sudah dicoba dan berhasil? Ada beberapa alasan untuk ini:

  • DateTimeOffset memungkinkan Anda melupakan lokasi SQL Server.
  • Ini memungkinkan Anda untuk memindahkan sebagian pekerjaan ke sistem.
  • Konversi dapat diminimalkan jika DateTimeOffset digunakan di mana saja, melakukannya hanya sebelum menampilkan data atau mengeluarkannya ke sistem eksternal.

Alasan-alasan ini menurut saya penting karena menggunakan pendekatan ini.

Saya akan dengan senang hati menjawab pertanyaan Anda, silakan tulis komentar.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Penyembunyian Data Real-Time Menggunakan Pemicu

  2. Berapa Banyak RAM yang Dibutuhkan Server Database Baru Anda?

  3. PEMBARUAN untuk Statistik

  4. Pertimbangan Kinerja Instans Terkelola Azure SQL

  5. Hasilkan satu set atau urutan tanpa loop – bagian 2