Một kiểu chỉ dành cho Ngày trong C # - tại sao không có kiểu Ngày?


106

Trong dự án C # của chúng tôi, chúng tôi có nhu cầu đại diện cho một ngày không có thời gian. Tuy nhiên, tôi biết về sự tồn tại của DateTime, nó cũng kết hợp cả thời gian trong ngày.Tôi muốn nói rõ rằng một số biến và đối số phương thức nhất định dựa trên ngày tháng . Do đó tôi không thể sử dụng DateTime.Datetài sản

Các cách tiếp cận tiêu chuẩn cho vấn đề này là gì? Chắc chắn tôi không phải là người đầu tiên gặp phải điều này? Tại sao không cóDate lớp trong C #?

Có ai có cách triển khai hay bằng cách sử dụng cấu trúc và có thể là một số tiện ích mở rộng trên DateTime và có thể triển khai một số toán tử như == và <,> không?


1
Mặc dù tôi hiểu muốn có ngữ nghĩa rõ ràng, rõ ràng, nhưng vấn đề cụ thể nào sẽ DateTimetạo ra?
Jeff Sternal

15
1 Tôi cần nhớ xóa các giờ ở đầu phương pháp. 2 nó không giao tiếp tốt rằng nó chỉ hoạt động vào ngày tháng. Điều này rất quan trọng, ví dụ: khi lưu trữ và tải từ Db, trong đó loại hẹp sẽ đủ. Lập trình là sự hiệp thông cho những người không phải máy tính
Carlo V. Dango

5
Tôi chỉ muốn nói rằng việc thiếu lớp ngày tháng là một vấn đề lớn và việc sử dụng DateTime không tốt chút nào. Ngay sau khi bạn lưu trữ "ngày tháng" của mình dưới dạng ngày-giờ, bạn là con tin cho các vấn đề tiết kiệm ánh sáng ban ngày của ngôn ngữ / múi giờ. Bỏ đi phần thời gian có thể gửi tất cả các ngày của bạn trở lại một ngày khi đồng hồ thay đổi (!). Và người dùng ở các múi giờ khác nhau sẽ thấy các ngày khác nhau khi họ cố gắng chuyển đổi ngày-giờ. Ngày giờ phù hợp để thể hiện những khoảnh khắc chính xác về thời gian (ngay từ thời điểm nào đó hoặc bất cứ điều gì) nhưng chúng rất không phù hợp để biểu thị một ngày tháng trừu tượng.
TheMathemagician

2
Một câu hỏi tương tự sau đó là stackoverflow.com/questions/7167710/… và Jon Skeet nói rằng nên có một Ngày.
goodeye

7
Kiểu dữ liệu chỉ ngày là DateTime vì kiểu dữ liệu số nguyên là số thập phân. Những người cho rằng chúng ta không cần ngày tháng vì bạn có thể vứt bỏ phần thời gian cũng giống như nói rằng chúng ta không cần số nguyên vì chúng ta có thể vứt bỏ phần thập phân. Thế giới của chúng ta có một khái niệm về ngày không bao gồm thời gian. Ngày 5 tháng 3 không phải ngày 5 tháng 3 00:00:00.
Vague

Câu trả lời:


54

Cho phép tôi thêm cập nhật cho câu hỏi cổ điển này:

  • Thư viện Noda Time của Jon Skeet hiện đã khá hoàn thiện và có một loại chỉ ngày tháng được gọi là LocalDate. (Cục bộ trong trường hợp này chỉ có nghĩa là cục bộ đối với ai đó , không nhất thiết cục bộ đối với máy tính nơi mã đang chạy.)

  • Loại chỉ ngày được gọi Datelà một bổ sung được đề xuất cho .NET Core, thông qua dự án corefxlab . Bạn sẽ tìm thấy nó trong System.Timegói, cùng với một TimeOfDayloại và một số phương thức mở rộng cho các loại hiện có.

Tôi đã nghiên cứu vấn đề này một cách đáng kể, vì vậy tôi cũng sẽ chia sẻ một số lý do cho sự cần thiết của những loại này:

  1. Có sự khác biệt hợp lý giữa giá trị chỉ ngày và giá trị ngày vào lúc nửa đêm.

    • Không phải mọi ngày địa phương đều có nửa đêm ở mọi múi giờ. Ví dụ: Quá trình chuyển đổi thời gian tiết kiệm ánh sáng ban ngày vào mùa xuân chuyển tiếp của Brazil chuyển đồng hồ từ 11:59:59 sang 01:00:00.

    • Ngày-giờ luôn đề cập đến một thời gian cụ thể trong ngày, trong khi chỉ ngày có thể đề cập đến đầu ngày, cuối ngày hoặc toàn bộ phạm vi trong ngày.

  2. Việc gắn thời gian vào một ngày có thể dẫn đến việc thay đổi ngày khi giá trị được truyền từ môi trường này sang môi trường khác, nếu múi giờ không được theo dõi cẩn thận. Điều này thường xảy ra trong JavaScript (có Dateđối tượng thực sự là ngày + giờ), nhưng cũng có thể dễ dàng xảy ra trong .NET hoặc trong tuần tự hóa khi dữ liệu được truyền giữa JavaScript và .NET.

  3. Việc tuần tự hóa một DateTimetệp với XML hoặc JSON (và những thứ khác) sẽ luôn bao gồm thời gian, ngay cả khi điều đó không quan trọng. Điều này rất khó hiểu, đặc biệt là khi xem xét những thứ như ngày sinh và ngày kỷ niệm, trong đó thời gian không liên quan.

  4. Về mặt kiến ​​trúc, DateTimelà một đối tượng giá trị DDD , nhưng nó vi phạm Nguyên tắc duy nhất có trách nhiệm theo một số cách:

    • Nó được thiết kế dưới dạng ngày + giờ, nhưng thường được sử dụng dưới dạng chỉ ngày (bỏ qua thời gian) hoặc chỉ thời gian trong ngày (bỏ qua ngày). ( TimeSpancũng thường được sử dụng cho thời gian trong ngày, nhưng đó là một chủ đề khác.)

    • Các DateTimeKindgiá trị gắn liền với .Kindtài sản tách loại đơn thành ba, TheUnspecified loại thực sự là ý định ban đầu của cấu trúc, và nên được sử dụng theo cách đó. Các Utcloại gắn giá trị đặc biệt với UTC, vàLocal Canh lề loại giá trị với múi giờ địa phương của môi trường.

      Vấn đề với việc có một cờ riêng cho loại là mỗi khi bạn sử dụng một DateTime, bạn phải kiểm tra.Kind để quyết định hành vi nào sẽ thực hiện. Các phương thức khung đều làm được điều này, nhưng những phương pháp khác thường quên. Đây thực sự là một vi phạm SRP, vì loại hiện có hai lý do khác nhau để thay đổi (giá trị và loại).

    • Hai trong số này dẫn đến việc sử dụng API biên dịch, nhưng thường vô nghĩa hoặc có các trường hợp cạnh kỳ lạ do tác dụng phụ gây ra. Xem xét:

      // 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 !!!

Tóm lại, mặc dù a DateTime có thể được sử dụng cho ngày chỉ, nó chỉ nên làm như vậy khi mọi nơi sử dụng nó rất cẩn thận để bỏ qua thời gian và cũng rất cẩn thận để không cố gắng chuyển đổi sang và từ UTC hoặc các Múi giờ.


2
Giá như System.Time.Datekết thúc trong khuôn khổ .NET: /
Robert Jørgensgaard Engdahl

1
Bạn có thể sử dụng gói này ngay hôm nay, chỉ cần đăng ký vào nguồn cấp dữ liệu corefx myget và bạn có thể truy cập System.Timegiống như bất kỳ gói nào khác. Nó chỉ là chưa "chính thức".
Matt Johnson-Pint

17

Tôi nghi ngờ không có Datelớp thuần túy dành riêng vì bạn đã có lớp có DateTimethể xử lý nó. Đang cóDate thể sẽ dẫn đến sự trùng lặp và nhầm lẫn.

Nếu bạn muốn phương pháp tiếp cận tiêu chuẩn, hãy xem thuộc DateTime.Datetính chỉ cung cấp phần ngày của a DateTimevới giá trị thời gian được đặt thành 12:00:00 nửa đêm (00:00:00).


59
Một lợi thế lớn của lớp Date chuyên dụng là nó không bị phức tạp về múi giờ và thời gian tiết kiệm ánh sáng ban ngày.
Dimitri C.

3
@DimitriC. Tôi không đồng ý - bạn có thể sử dụng DateTime với UTC và bạn không gặp phải các vấn đề được giải thích, cộng với DateTime, ngay cả khi bạn muốn chỉ ngày, bạn vẫn có thể làm toán liên quan đến thời gian (tức là cho tôi ngày nếu tôi trừ đi 20 x 2 giờ từ hôm nay).
Robert MacLean

@Robert MacLean: Cảm ơn bạn đã nhấn mạnh sự tiện lợi của việc sử dụng UTC DateTimes. Tôi đã thực hiện một số bài kiểm tra và có vẻ như DateTimeKind.Unspod chỉ định hoạt động như UTC liên quan đến các phép trừ. Vì vậy, thực sự, nếu bạn cẩn thận về "loại" DateTimes mà bạn đang làm việc, mọi thứ sẽ trở nên tốt đẹp.
Dimitri C.

10
Việc phải suy nghĩ về UTC và bất cứ điều gì liên quan đến múi giờ chỉ là một sự lãng phí năng lượng vì nó có thể dễ dàng bị tránh bởi một lớp Ngày riêng biệt. Và tôi không thấy có sự nhầm lẫn nào giữa Date và DateTime.
maulik13

5
Đồng ý rằng C # thực sự nên có lớp Ngày. Việc chuyển đổi múi giờ không chỉ là nguồn thường xuyên của các lỗi tàu ngầm, mà còn rất khó khăn khi giải quyết những việc dựa trên ngày làm việc thay vì dựa trên thời gian.
Julian Birch

12

Tôi đã gửi email tới refsrcfeedback@microsoft.com và đó là câu trả lời của họ

Marcos, đây không phải là nơi tốt để hỏi những câu như thế này. Hãy thử http://stackoverflow.com Câu trả lời ngắn gọn là bạn cần một mô hình để đại diện cho một thời điểm và DateTime làm được điều đó, đó là kịch bản hữu ích nhất trong thực tế . Việc con người sử dụng hai khái niệm (ngày và giờ) để đánh dấu mốc thời gian là tùy tiện và không hữu ích để tách biệt.

Chỉ tách ra những nơi được bảo hành, đừng làm mọi việc chỉ vì lợi ích mà làm một cách mù quáng. Hãy suy nghĩ theo cách này: bạn gặp phải vấn đề gì được giải quyết bằng cách tách DateTime thành Ngày và Giờ? Và những vấn đề nào bạn sẽ nhận được mà bạn không có bây giờ? Gợi ý: nếu bạn xem cách sử dụng DateTime trên khuôn khổ .NET: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references bạn sẽ thấy rằng hầu hết được trả về từ một phương thức. Nếu chúng tôi không có một khái niệm như DateTime, bạn sẽ phải sử dụng các tham số hoặc Tuples để trả về một cặp Ngày và Giờ.

HTH, Kirill Osenkov

Trong email của mình, tôi đã hỏi liệu có phải do DateTime sử dụng TimeZoneInfo để lấy thời gian của máy hay không - trong Now propriety. Vì vậy, tôi muốn nói rằng đó là bởi vì "các quy tắc kinh doanh" quá "khớp", họ đã đồng nhất điều đó với tôi.


Bài đăng này thực sự cung cấp thông tin chi tiết về những suy nghĩ đằng sau quyết định thiết kế của việc không có lớp ngày tích hợp. Câu hỏi bạn gửi cho họ là gì? Tôi không có ý ám chỉ rằng tôi đồng ý với quyết định này vì những lý do mà @TheMathemagician đã liệt kê ở trên.
Robert Jørgensgaard Engdahl

@ RobertJørgensgaardEngdahl thật đáng buồn là tôi không có quyền truy cập vào tài khoản e-mail đó nữa. Nhưng tôi tin rằng tôi đã hỏi họ tại sao họ đã kết hợp Thời gian và Ngày với nhau trong cấu trúc DateTime. Và tôi nghĩ rằng tôi đồng ý với TheMathemagician, tôi nghĩ MS đã thực hiện phương pháp thiết kế này bởi vì là một công ty quốc tế, nó đáp ứng nhu cầu của họ - và bây giờ đã muộn để thay đổi nó - trong khi việc tách các khái niệm thì không.
MVCDS

2
Có lẽ MS có thể đi toàn bộ con heo và thực hiện một SpaceTimelớp! Này, theo Einstein, không gian và thời gian kết hợp chặt chẽ với nhau nên chúng ta cũng không cần phải phân biệt giữa chúng, đúng không? (!!!!!!!!!!!) Tôi kinda mới để C #, nhưng tôi có thể nói, đó là một bãi mìn đến từ VB.NET đâu, đơn giản, date, Today(), now, vv Không có DateTimetiền tố rác, không có về. (Và những dấu chấm phẩy và điều này trường hợp nhạy cảm chắc chắn là irksome Chỉ cần bắn tôi ngay bây giờ!)
SteveCinq

2
Và SQL Server của riêng họ có Datekiểu và kết quả phải là kiểu Date- nếu đó là Datekiểu kết quả được mong đợi là chuỗi không có thời gian. Ví dụ Delphi cũng có Date là DateTime, nhưng typeinfo khác với Date và DateTime.
user2091150,

1
Kirill Osenkov đang trả lời câu hỏi Q của "Tại sao không có Lớp ngày và Giờ riêng biệt với Lớp Ngày giờ?". Các thực tế Q đã "Tại sao không còn có ngày riêng biệt và lớp Thời gian?". Tôi hiểu rằng Ngày và Giờ nên được kết hợp thành một Lớp cho nhiều trường hợp sử dụng được cấp của khái niệm ngày-giờ . Tuy nhiên, có lẽ có ít nhất là nhiều nếu không muốn nói là nhiều hơn như các trường hợp sử dụng hợp lệ của khái niệm ngày . Và tất nhiên có rất nhiều công dụng trị các trường hợp một thời gian khái niệm cũng có.
Tom


4

Nếu bạn cần chạy so sánh ngày thì hãy sử dụng

yourdatetime.Date;

Nếu bạn đang hiển thị trên màn hình, hãy sử dụng

yourdatetime.ToShortDateString();

Phần .Date là những gì tôi đang tìm kiếm.
Brendan Vogt

3

Cho phép tôi suy đoán: Có thể là do cho đến SQL Server 2008 không có kiểu dữ liệu Ngày trong SQL nên sẽ rất khó để lưu trữ nó trong máy chủ SQL ?? Và xét cho cùng thì đó là một Sản phẩm của Microsoft?


Ngày giờ db khác với ngày giờ C #. Ngày giờ db không có múi giờ nên chúng không thực sự đề cập đến một thời điểm cụ thể. Nhưng C # biết rằng tức thời là và lưu trữ các tích tắc kể từ kỷ nguyên UTC.
Artsrc

2
cuộc thảo luận là về NGÀY dành riêng, không quá nhiều về phần ngày giờ nên tôi không hiểu ý bạn đang muốn đưa ra?
Pleun

Điều này không cung cấp câu trả lời cho câu hỏi. Để phê bình hoặc yêu cầu làm rõ từ tác giả, hãy để lại bình luận bên dưới bài đăng của họ.
Barranka

@Barranka - Câu hỏi chứa "Tại sao không có lớp Ngày trong C #?"
STLDev

2

Ai biết tại sao nó lại như vậy. Có rất nhiều quyết định thiết kế tồi trong .NET framework. Tuy nhiên, tôi nghĩ rằng đây là một trong những điều khá nhỏ. Bạn luôn có thể bỏ qua phần thời gian, vì vậy ngay cả khi một số mã quyết định có DateTime đề cập đến nhiều thứ hơn là chỉ ngày, thì mã quan tâm chỉ nên xem phần ngày. Ngoài ra, bạn có thể tạo một kiểu mới chỉ đại diện cho ngày tháng và sử dụng các hàm trong DateTime để thực hiện các công việc nặng nhọc (tính toán).


1
Tôi thực sự không nghĩ đây là một quyết định tồi, cho dù bạn có muốn sử dụng chỉ một buổi hẹn hò hay không. Tôi sẽ không phản đối bạn nhưng đó là ý kiến ​​của tôi.
JonH

Tôi không nghĩ rằng tôi đã nói nó tốt. Tôi thực sự không có nhiều vấn đề với nó, mặc dù tôi có thể thấy cách có hai hoặc ba loại sẽ phù hợp hơn từ quan điểm trừu tượng / sang trọng. Quan điểm của tôi thực sự là có rất nhiều thứ trong .NET framework có thể khiến bạn phải vò đầu bứt tai và điều đó không đáng để bạn phải bận tâm, đặc biệt là khi xem xét rằng "vấn đề" này là khá nhỏ so với một số quyết định thiết kế nghiêm trọng (chung chung ràng buộc).
siride

+1 bởi vì đó là sự thật ... đây có phải là vấn đề duy nhất (hoặc vấn đề lớn nhất) của .NET :-) :-) Họ cần bao nhiêu phiên bản SQL Server để thêm loại DATE và TIME? Và có họ NHIÊU hữu ích hơn (ít nhất là vì lý do toàn vẹn)
xanatos

Tôi cũng nên nói thêm rằng tôi nghĩ rằng "mọi thứ bắt đầu từ -100 điểm" là một cách tốt để tạo ra một khuôn khổ nghèo nàn và đây có thể là một trong những thứ bị cuốn vào rác đó.
siride

2
Tôi vừa gặp vấn đề này vì một phần của mã đã bỏ quên việc sử dụng thuộc tính .Date và do đó không so sánh đúng. Tôi chắc chắn nghĩ rằng đó là nhu cầu về một loại ngày mà không lưu trữ bất cứ lúc nào, để tránh loại lỗi
JoelFan

2

Tại sao? Chúng ta chỉ có thể suy đoán và nó không giúp ích gì nhiều để giải quyết các vấn đề kỹ thuật. Một dự đoán tốt là nó DateTimechứa tất cả các chức năng mà một cấu trúc như vậy sẽ có.

Nếu nó thực sự quan trọng với bạn, chỉ cần bọc DateTimetrong cấu trúc bất biến của riêng bạn mà chỉ hiển thị ngày (hoặc xem thuộc DateTime.Datetính).


2

Ngoài câu trả lời của Robert, bạn cũng có DateTime.ToShortDateStringphương pháp. Ngoài ra, nếu bạn thực sự muốn một đối tượng Ngày, bạn luôn có thể sử dụng mẫu Bộ điều hợp và bọc đối tượng DateTime chỉ hiển thị những gì bạn muốn (tức là tháng, ngày, năm).


2

Luôn luôn có DateTime.Datetài sản cắt bỏ phần thời gian của DateTime. Có thể bạn có thể đóng gói hoặc bọc DateTime trong kiểu Ngày của riêng mình.

Và đối với câu hỏi tại sao, tôi đoán bạn sẽ phải hỏi Anders Heljsberg.


1

Bởi vì để biết ngày, bạn phải biết thời gian của hệ thống (tính bằng tích tắc), bao gồm cả thời gian - vậy tại sao lại vứt bỏ thông tin đó?

DateTimecó một Datetài sản nếu bạn không quan tâm đến thời gian.


1

Yeah, System.DateTime cũng được niêm phong. Tôi đã thấy một số người chơi trò chơi này bằng cách tạo một lớp tùy chỉnh chỉ để nhận giá trị chuỗi của thời gian như đã đề cập trong các bài viết trước đó, những thứ như:

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();
        }
    }
}

Điều này có thể không cần thiết, vì bạn có thể dễ dàng giải nén GetShortTimeString từ một kiểu DateTime cũ đơn thuần mà không cần một lớp mới


0

Nếu bạn sử dụng thuộc tính Ngày hoặc Hôm nay để chỉ lấy phần ngày từ đối tượng DateTime.

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

Sau đó, bạn sẽ chỉ nhận được thành phần ngày với thành phần thời gian được đặt thành nửa đêm.


1
điều này chắc chắn không phải là những gì tôi muốn
Carlo V. Dango

@Carlo V. Dango: Tôi không đồng ý. Tôi nghĩ đó chính xác là những gì bạn muốn.
siride

1
@Carlo V. Dango: Cụ thể bạn đang muốn làm gì mà những thuộc tính này không cho phép bạn thực hiện?
eph_tagh

5
Nó khá dễ dàng: dấu chân bộ nhớ Ngày có thể sẽ chỉ bằng một nửa dấu chân bộ nhớ của DateTime (32 thay vì 64 bit). Bạn chắc chắn rằng đồng nghiệp ngu ngốc của bạn đã không .AddHours (1) vào ngày của bạn thay đổi nó mà "giữ nguyên" với POV của "chỉ ngày". Nếu (xảy ra lỗi) DateTime được đặt thành DateTimeKind.Local và thời gian được chuẩn hóa thành UTC, thì Ngày có thể sẽ thay đổi (đã xảy ra với tôi khi sử dụng XmlSerialization và chuyển sang JSON) ... Đã đủ chưa?
xanatos
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.