Jenis untuk Tanggal hanya di C # - mengapa tidak ada jenis Tanggal?

108

Dalam proyek C # kami, kami memiliki kebutuhan untuk merepresentasikan tanggal tanpa waktu. Saya tahu tentang keberadaan DateTime, namun, ini juga menyertakan waktu dalam sehari. Saya ingin menjelaskan bahwa variabel dan argumen metode tertentu berbasis tanggal . Oleh karena itu saya tidak dapat menggunakan DateTime.Dateproperti itu

Apa pendekatan standar untuk masalah ini? Tentunya saya bukan orang pertama yang mengalami ini? Mengapa tidak ada Datekelas di C #?

Apakah ada yang punya implementasi bagus menggunakan struct dan mungkin beberapa metode ekstensi pada DateTime dan mungkin menerapkan beberapa operator seperti == dan <,>?

Carlo V. Dango
sumber
1
Sementara saya memahami menginginkan semantik yang eksplisit dan jelas, masalah spesifik apa yang timbul DateTime?
Jeff Sternal
15
1 Saya perlu ingat untuk menghapus jam pada awal metode. 2 tidak berkomunikasi dengan baik bahwa itu hanya berfungsi pada tanggal. Ini penting misalnya saat menyimpan dan memuat dari Db di mana tipe yang sempit sudah cukup. Pemrograman adalah persekutuan untuk orang-orang bukan komputer
Carlo V. Dango
6
Saya hanya ingin mengatakan bahwa kurangnya kelas kencan adalah masalah besar dan menggunakan DateTime tidak bagus sama sekali. Segera setelah Anda menyimpan "tanggal" Anda sebagai tanggal-waktu, Anda menjadi sandera dari masalah penghematan siang hari lokal / zona waktu. Membuang bagian waktu dapat mengirim semua tanggal Anda kembali pada suatu hari ketika jam berubah (!). Dan pengguna di zona waktu yang berbeda akan melihat tanggal yang berbeda ketika mereka mencoba untuk mengubah tanggal-waktu. Tanggal-waktu bagus untuk mewakili momen tepat dalam waktu (jiffies dari beberapa titik atau apapun) tetapi mereka sangat tidak sesuai untuk mewakili tanggal abstrak.
TheMathemagician
3
Pertanyaan serupa kemudian stackoverflow.com/questions/7167710/… , dan Jon Skeet mengatakan harus ada Tanggal.
selamat tinggal
9
Tipe data khusus tanggal adalah DateTime karena tipe data integer adalah desimal. Mereka yang berpendapat bahwa kita tidak memerlukan tanggal karena Anda dapat membuang bagian waktu sama dengan mengatakan bahwa kita tidak memerlukan bilangan bulat karena kita dapat membuang bagian desimalnya. Dunia kita memiliki konsep tanggal yang tidak termasuk waktu. 5 Maret bukan 5 Maret 00:00:00.
Vague

Jawaban:

55

Izinkan saya menambahkan pembaruan untuk pertanyaan klasik ini:

  • Perpustakaan Noda Time Jon Skeet sekarang sudah cukup matang, dan memiliki tipe khusus tanggal yang disebut LocalDate. (Lokal dalam hal ini hanya berarti lokal untuk seseorang , tidak harus lokal ke komputer tempat kode berjalan.)

  • Jenis khusus tanggal yang dipanggil Dateadalah tambahan yang diusulkan ke .NET Core, melalui proyek corefxlab . Anda akan menemukannya di dalam System.Timepaket, bersama dengan TimeOfDaytipe, dan beberapa metode ekstensi untuk tipe yang ada.

Saya telah mempelajari masalah ini secara signifikan, jadi saya juga akan membagikan beberapa alasan perlunya jenis-jenis ini:

  1. Ada perbedaan logis antara nilai tanggal-saja, dan nilai tanggal-di-tengah malam.

    • Tidak setiap hari lokal memiliki tengah malam di setiap zona waktu. Contoh: Transisi waktu musim panas musim semi-maju Brasil menggerakkan jam dari 11:59:59 ke 01:00:00.

    • Tanggal-waktu selalu mengacu pada waktu tertentu dalam hari, sedangkan tanggal-saja dapat mengacu pada awal hari, akhir hari, atau seluruh rentang hari.

  2. Melampirkan waktu ke tanggal dapat menyebabkan tanggal berubah karena nilai diteruskan dari satu lingkungan ke lingkungan lain, jika zona waktu tidak diawasi dengan sangat hati-hati. Ini biasanya terjadi di JavaScript (yang Dateobjeknya sebenarnya adalah tanggal + waktu), tetapi dapat dengan mudah terjadi di .NET juga, atau dalam serialisasi saat data dilewatkan antara JavaScript dan .NET.

  3. Membuat serial DateTimedengan XML atau JSON (dan lainnya) akan selalu menyertakan waktu, bahkan jika itu tidak penting. Ini sangat membingungkan, terutama mengingat hal-hal seperti tanggal lahir dan hari jadi, yang waktunya tidak relevan.

  4. Secara arsitektural, DateTimeadalah objek nilai DDD , tetapi melanggar Prinsip Bertanggung Jawab Tunggal dalam beberapa cara:

    • Ini dirancang sebagai tipe tanggal + waktu, tetapi sering digunakan sebagai hanya tanggal (mengabaikan waktu), atau waktu-hari-saja (mengabaikan tanggal). ( TimeSpanjuga sering digunakan untuk waktu-hari, tapi itu topik lain.)

    • The DateTimeKindnilai yang melekat pada .Kindproperti membagi jenis tunggal menjadi tiga, yang Unspecifiedsemacam ini benar-benar maksud asli dari struktur, dan harus digunakan seperti itu. The Utcjenis disejajarkan nilai khusus dengan UTC, dan Localaligns jenis nilai dengan zona waktu lokal lingkungan ini.

      Masalah dengan memiliki flag terpisah untuk jenis adalah bahwa setiap kali Anda menggunakan a DateTime, Anda harus memeriksa .Kinduntuk memutuskan perilaku apa yang akan diambil. Semua metode kerangka melakukan ini, tetapi yang lain sering lupa. Ini benar-benar pelanggaran SRP, karena jenisnya sekarang memiliki dua alasan berbeda untuk berubah (nilai, dan jenisnya).

    • Keduanya menyebabkan penggunaan API yang dikompilasi, tetapi sering kali tidak masuk akal, atau memiliki kasus tepi aneh yang disebabkan oleh efek samping. Mempertimbangkan:

      // nonsensical, caused by mixing types
      DateTime dt = DateTime.Today - TimeSpan.FromHours(3);  // when on today??
      
      // strange edge cases, caused by impact of Kind
      var london = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
      var paris = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
      var dt = new DateTime(2016, 3, 27, 2, 0, 0);  // unspecified kind
      var delta = paris.GetUtcOffset(dt) - london.GetUtcOffset(dt);  // side effect!
      Console.WriteLine(delta.TotalHours); // 0, when should be 1 !!!
      

Singkatnya, meskipun a DateTime dapat digunakan untuk tanggal saja, seharusnya hanya digunakan jika setiap tempat yang menggunakannya sangat berhati-hati untuk mengabaikan waktu, dan juga sangat berhati-hati untuk tidak mencoba beralih ke dan dari UTC atau lainnya. zona waktu.

Matt Johnson-Pint
sumber
2
Jika hanya System.Time.Dateakan berakhir dalam kerangka .NET: /
Robert Jørgensgaard Engdahl
1
Anda dapat menggunakan ini hari ini, cukup berlangganan feed corefx myget, dan Anda dapat menariknya System.Timeseperti paket lainnya. Ini belum "resmi".
Matt Johnson-Pint
16

Saya menduga tidak ada Datekelas murni yang berdedikasi karena Anda sudah memiliki DateTimeyang bisa mengatasinya. Memiliki Dateakan menyebabkan duplikasi dan kebingungan.

Jika Anda ingin pendekatan standar, lihat DateTime.Dateproperti yang hanya memberikan porsi tanggal a DateTimedengan nilai waktu ditetapkan ke 12:00:00 tengah malam (00:00:00).

Robert MacLean
sumber
61
Keuntungan besar dari kelas Tanggal khusus adalah ia tidak mengalami kerumitan zona waktu dan waktu musim panas.
Dimitri C.
3
@Tokopedia Saya tidak setuju - Anda dapat menggunakan DateTime dengan UTC dan Anda tidak mengalami masalah yang dijelaskan, ditambah dengan DateTime, bahkan jika Anda hanya menginginkan tanggal, Anda masih dapat melakukan matematika yang melibatkan waktu (yaitu beri saya tanggal jika saya mengurangi 20 x 2 jam dari hari ini).
Robert MacLean
@Robert MacLean: Terima kasih telah menggarisbawahi kemudahan menggunakan UTC DateTimes. Saya melakukan beberapa tes dan sepertinya DateTimeKind.Unspecified bertindak seperti UTC tentang pengurangan. Jadi memang, jika Anda berhati-hati dengan "jenis" DateTimes yang Anda kerjakan, semuanya akan baik-baik saja.
Dimitri C.
10
Harus memikirkan UTC dan segala sesuatu yang berkaitan dengan zona waktu hanyalah pemborosan energi karena dapat dengan mudah dihindari oleh kelas Tanggal yang terpisah. Dan saya tidak melihat adanya kebingungan antara Date dan DateTime.
maulik13
6
Setuju bahwa C # harus memiliki kelas Tanggal. Tidak hanya konversi zona waktu menjadi sumber bug kapal selam yang konstan, tetapi juga menyakitkan ketika berurusan dengan hal-hal yang berbasis hari kerja daripada berbasis waktu.
Julian Birch
12

Saya telah mengirim email ke [email protected] dan itulah jawaban mereka

Marcos, ini bukan tempat yang bagus untuk bertanya seperti ini. Coba http://stackoverflow.com Jawaban singkatnya adalah Anda memerlukan model untuk mewakili suatu titik waktu, dan DateTime melakukannya, ini adalah skenario yang paling berguna dalam praktiknya . Fakta bahwa manusia menggunakan dua konsep (tanggal dan waktu) untuk menandai titik waktu adalah sewenang-wenang dan tidak berguna untuk dipisahkan.

Hanya pisahkan jika diperlukan, jangan melakukan sesuatu hanya untuk melakukan sesuatu secara membabi buta. Pikirkan seperti ini: masalah apa yang Anda miliki yang diselesaikan dengan membagi DateTime menjadi Tanggal dan Waktu? Dan masalah apa yang akan Anda dapatkan yang tidak Anda miliki sekarang? Petunjuk: jika Anda melihat penggunaan DateTime di seluruh kerangka .NET: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references Anda akan melihat bahwa sebagian besar dikembalikan dari metode. Jika kami tidak memiliki konsep tunggal seperti DateTime, Anda harus menggunakan parameter atau Tuple untuk mengembalikan sepasang Tanggal dan Waktu.

HTH, Kirill Osenkov

Di email saya, saya mempertanyakan apakah itu karena DateTime menggunakan TimeZoneInfo untuk mendapatkan waktu mesin - dalam kepatutan Now. Jadi menurut saya itu karena "aturan bisnis" "terlalu digabungkan", mereka menegaskannya kepada saya.

MVCDS
sumber
Posting ini benar-benar memberikan wawasan tentang pemikiran di balik keputusan desain untuk tidak memiliki kelas kencan bawaan. Pertanyaan apa yang Anda kirimkan kepada mereka? Saya tidak bermaksud menyiratkan bahwa saya setuju dengan keputusan ini karena alasan yang @TheMathemagician tercantum di atas.
Robert Jørgensgaard Engdahl
@ RobertJørgensgaardEngdahl sayangnya saya tidak memiliki akses ke akun email itu lagi. Tapi saya yakin saya telah bertanya kepada mereka mengapa mereka menggabungkan Waktu dan Tanggal bersama-sama dalam struktur DateTime. Dan menurut saya saya setuju dengan TheMathemagician, saya pikir MS mengambil pendekatan desain ini karena sebagai perusahaan internasional ia membayar kebutuhan mereka - dan sekarang terlambat untuk mengubahnya - sementara pemisahan konsep tidak.
MVCDS
2
Mungkin MS bisa pergi begitu saja dan menerapkan SpaceTimekelas! Hei, menurut Einstein, ruang dan waktu sangat erat sehingga kita tidak perlu membedakan keduanya, bukan? (!!!!!!!!!!!) Aku agak baru untuk C #, tapi saya harus mengatakan, itu adalah ladang ranjau yang datang dari VB.NET mana, cukup, date, Today(), now, dll ada DateTimeawalan sampah, tidak ada menyia-nyiakan. (Dan titik koma dan sensitivitas huruf ini benar-benar menjengkelkan! Tembak saja saya sekarang!)
SteveCinq
2
Dan SQL Server mereka sendiri memiliki Datetipe dan hasil harus bertipe Date- jika Datetipe result diharapkan sebagai string tanpa waktu. Misalnya Delphi juga memiliki Date sebagai DateTime, tetapi ketik info yang berbeda untuk Date dan DateTime.
pengguna2091150
1
Kirill Osenkov menjawab pertanyaan "Mengapa tidak memisahkan Kelas Tanggal dan Waktu versus Kelas DateTime?". The aktual Q adalah "Mengapa tidak juga memiliki Tanggal terpisah dan Kelas Waktu?". Saya memahami bahwa Tanggal dan Waktu harus digabungkan menjadi satu Kelas untuk banyak kasus penggunaan konsep tanggal-waktu . Namun, mungkin ada paling tidak sebanyak jika tidak lebih dari kasus penggunaan valid dari konsep tanggal saja . Dan tentu saja ada banyak kasus penggunaan yang valid dari konsep waktu juga.
Tom
4

Jika Anda perlu menjalankan perbandingan tanggal, gunakan

yourdatetime.Date;

Jika Anda menampilkan ke layar gunakan

yourdatetime.ToShortDateString();
MattP
sumber
Bagian. Tanggal adalah apa yang saya cari.
Brendan Vogt
3

Izinkan saya berspekulasi: Mungkin karena sampai SQL Server 2008 belum ada datatype Date di SQL sehingga akan sulit jadi simpan di SQL server ?? Dan apakah itu Produk Microsoft?

Pleun
sumber
Db datetime berbeda dari C # datetime. Sebuah db datetime tidak memiliki zona waktu sehingga mereka tidak benar-benar merujuk ke instan tertentu. Tapi C # tahu bahwa instan dan menyimpan ticks sejak zaman UTC.
artsrc
2
pembahasannya adalah tentang DATE khusus, bukan tentang bagian waktu jadi saya tidak mengerti maksud yang ingin Anda sampaikan?
Pleun
Ini tidak memberikan jawaban atas pertanyaan tersebut. Untuk mengkritik atau meminta klarifikasi dari seorang penulis, tinggalkan komentar di bawah postingannya.
Barranka
@Barranka - Pertanyaannya berisi "Mengapa tidak ada kelas Date di C #?"
STLDev
2

Entah kenapa seperti itu. Ada banyak keputusan desain yang buruk dalam kerangka .NET. Namun, menurut saya ini cukup kecil. Anda selalu dapat mengabaikan bagian waktu, jadi meskipun beberapa kode memutuskan untuk merujuk DateTime lebih dari sekedar tanggal, kode yang peduli seharusnya hanya melihat bagian tanggal. Alternatifnya, Anda bisa membuat tipe baru yang hanya mewakili tanggal dan menggunakan fungsi di DateTime untuk melakukan pekerjaan berat (penghitungan).

siride
sumber
1
Saya benar-benar tidak berpikir ini adalah keputusan yang buruk, apakah Anda hanya ingin menggunakan kencan atau tidak. Saya tidak akan downvote ya tapi itu pendapat saya.
JonH
Saya tidak berpikir saya mengucapkannya dengan baik. Saya sebenarnya tidak memiliki banyak masalah dengan itu, meskipun saya bisa melihat bagaimana memiliki dua atau tiga jenis akan lebih sesuai dari sudut pandang abstraksi / keanggunan. Maksud saya sebenarnya adalah bahwa ada banyak hal dalam kerangka .NET yang mungkin membuat Anda menggaruk-garuk kepala dan tidak ada gunanya menjadi terlalu kesal, terutama mengingat "masalah" ini cukup kecil dibandingkan dengan beberapa keputusan desain yang mengerikan (umum kendala).
siride
+1 karena itu benar ... apakah ini satu-satunya masalah (atau yang terbesar) dari .NET :-) :-) Berapa banyak versi SQL Server yang mereka perlukan untuk menambahkan tipe DATE dan TIME? Dan di sana mereka JAUH lebih berguna (setidaknya untuk alasan integritas)
xanatos
Saya juga harus menambahkan bahwa menurut saya "semuanya dimulai pada -100 poin" adalah cara yang baik untuk membuat kerangka kerja yang buruk dan ini mungkin salah satu hal yang terjebak dalam sampah itu.
siride
2
Saya baru saja digigit oleh masalah ini karena 1 bagian dari kode lalai menggunakan properti .Date dan karenanya tidak dapat dibandingkan dengan benar. Saya pasti berpikir ada kebutuhan untuk tipe Tanggal yang tidak disimpan kapan saja, untuk menghindari jenis kesalahan ini
JoelFan
2

Mengapa? Kami hanya bisa berspekulasi dan tidak banyak membantu memecahkan masalah teknik. Tebakan yang bagus adalah yang DateTimeberisi semua fungsionalitas yang dimiliki oleh struct semacam itu.

Jika itu benar-benar penting bagi Anda, cukup bungkus DateTimedalam struct Anda yang tidak dapat diubah yang hanya memperlihatkan tanggal (atau lihat DateTime.Datepropertinya).

jason
sumber
2

Selain jawaban Robert, Anda juga memiliki DateTime.ToShortDateStringmetodenya. Juga, jika Anda benar-benar menginginkan objek Tanggal Anda selalu dapat menggunakan pola Adaptor dan membungkus objek DateTime hanya memperlihatkan apa yang Anda inginkan (yaitu bulan, hari, tahun).

Matt
sumber
2

Selalu ada DateTime.Dateproperti yang memotong bagian waktu DateTime. Mungkin Anda dapat merangkum atau membungkus DateTime dalam tipe Tanggal Anda sendiri.

Dan untuk pertanyaan mengapa, saya rasa Anda harus bertanya pada Anders Heljsberg.

Mikael Östberg
sumber
1

Karena untuk mengetahui tanggalnya, Anda harus mengetahui waktu sistem (dalam tanda centang), yang termasuk waktu - jadi mengapa membuang informasi itu?

DateTimememiliki Dateproperti jika Anda tidak peduli sama sekali tentang waktu.

Massif
sumber
1

Ya, System.DateTime juga disegel. Saya telah melihat beberapa orang bermain game dengan ini dengan membuat kelas khusus hanya untuk mendapatkan nilai string waktu seperti yang disebutkan oleh posting sebelumnya, hal-hal seperti:

class CustomDate
{
    public DateTime Date { get; set; }
    public bool IsTimeOnly { get; private set; }

    public CustomDate(bool isTimeOnly)
    {
        this.IsTimeOnly = isTimeOnly;
    }

    public string GetValue()
    {
        if (IsTimeOnly)
        {
            return Date.ToShortTimeString();
        }

        else
        {
            return Date.ToString();
        }
    }
}

Ini mungkin tidak perlu, karena Anda dapat dengan mudah mengekstrak GetShortTimeString dari tipe DateTime lama biasa tanpa kelas baru.

Ta01
sumber
0

Jika Anda menggunakan properti Tanggal atau Hari Ini untuk mendapatkan hanya bagian tanggal dari objek DateTime.

DateTime today = DateTime.Today;
DateTime yesterday = DateTime.Now.AddDays(-1).Date;

Kemudian Anda akan mendapatkan komponen tanggal hanya dengan komponen waktu disetel ke tengah malam.

eph_tagh
sumber
1
ini jelas bukan yang saya inginkan
Carlo V. Dango
@ Carlo V. Dango: Saya tidak setuju. Saya pikir itu persis seperti yang Anda inginkan.
siride
1
@ Carlo V. Dango: Apa yang secara khusus ingin Anda lakukan sehingga properti ini tidak memungkinkan Anda untuk mencapainya?
eph_tagh
5
Ini cukup mudah: jejak memori Tanggal mungkin hanya setengah dari jejak memori DateTime (32 bukannya 64 bit). Anda akan yakin bahwa rekan kerja bodoh Anda tidak .AddHours (1) pada teman kencan Anda mengubahnya tetapi "tetap sama" dari POV "hanya tanggal". Jika (karena kesalahan) DateTime disetel ke DateTimeKind.Local dan waktu dinormalisasi ke UTC, Tanggal mungkin akan berubah (terjadi pada saya melalui penggunaan XmlSerialization dan bolak-balik yang dilakukan dengan buruk ke JSON) ... Apakah itu cukup?
xanatos