Тип для дати лише в C # - чому немає типу дати?


107

У нашому проекті C # ми маємо потребу представляти дату без часу. Я знаю про існування DateTime, однак він включає і час доби. Я хочу зробити явне, що певні змінні та аргументи методу засновані на даті . Отже, я не можу використовувати DateTime.Dateмайно

Які стандартні підходи до цієї проблеми? Звичайно, я не вперше зіткнувся з цим? Чому Dateв C # немає класу?

Хтось має гарну реалізацію, використовуючи структуру та, можливо, деякі методи розширення на DateTime, а можливо, реалізуючи деякі оператори, такі як == і <,>?


1
Хоча я розумію, що хочеться чіткої, чіткої семантики, які конкретні проблеми DateTimeстворюються?
Jeff Sternal

15
1 Мені потрібно пам’ятати, щоб зняти години на початку методу. 2 це не добре повідомляє, що працює виключно на побаченнях. Це важливо, наприклад, під час зберігання та завантаження з Db, де достатньо вузького типу. Програмування - це причастя для людей, а не комп’ютерів
Карло В. Данго

5
Я просто хотів сказати, що відсутність класу дат є великою справою, а використання DateTime взагалі не корисно. Як тільки ви зберігаєте свої "дати" як дату, ви ставите в заручники проблеми економії місцевих / часових поясів. Викинувши часову частину, можна відправити всі ваші дати назад у день, коли годинник змінюється (!). І користувачі в різних часових поясах побачать різні дати, коли вони намагаються перетворити дати. Час побачень прекрасний для відображення точних моментів часу (відмітки з якоїсь точки або будь-якого іншого), але вони дуже непридатні для відображення абстрактної дати.
Математик

2
Пізніше подібне питання stackoverflow.com/questions/7167710/… , і Джон Скіт каже, що має бути дата.
goodeye

8
Тип даних лише для дати - це DateTime, а цілий тип даних - це десятковий. Ті, хто стверджує, що нам не потрібна дата, тому що ви можете просто викинути часову частину, схоже на те, що нам не потрібні цілі числа, оскільки ми можемо викинути десяткову частину. У нашому світі є концепція дати, яка не включає час. 5 березня не 5 березня 00:00:00.
Неясний

Відповіді:


55

Дозвольте додати оновлення до цього класичного питання:

  • Бібліотека Noda Time Джона Скіта вже досить зріла, і вона має назву лише для дати LocalDate. (Локальна в цьому випадку просто означає локальну для когось , не обов'язково локальну для комп'ютера, на якому працює код.)

  • Тип, призначений лише для дати, називається Dateзапропонованим доповненням до .NET Core через проект corefxlab . Ви знайдете його в System.Timeпакеті разом із TimeOfDayтипом та кількома методами розширення до існуючих типів.

Я вивчив цю проблему значною мірою, тому також поділюсь кількома причинами необхідності таких типів:

  1. Існує логічна невідповідність між значенням дати та датою опівночі.

    • Не кожен місцевий день має півночі у кожному часовому поясі. Приклад: Бразилія на перехід на літній час весни вперед пересуває годинник з 11:59:59 до 01:00:00.

    • Час дати завжди позначається на певний час протягом дня, тоді як лише дата може стосуватися початку дня, кінця дня або всього діапазону дня.

  2. Приєднання часу до дати може призвести до зміни дати, коли значення передається з одного середовища в інше, якщо часові пояси не спостерігаються дуже уважно. Це зазвичай трапляється в JavaScript ( Dateоб'єктом якого є насправді дата + час), але це може бути легко і в .NET, або в серіалізації, коли дані передаються між JavaScript і .NET.

  3. Серіалізація DateTimeз XML або JSON (та іншими) завжди буде включати час, навіть якщо це не важливо. Це дуже заплутано, особливо враховуючи такі речі, як дати народження та ювілеї, де час не має значення.

  4. В архітектурному відношенні DateTimeце об'єкт значення DDD , але він порушує Єдиний відповідальний принцип декількома способами:

    • Він розроблений як дата + тип часу, але часто використовується лише як дата (ігнорування часу), або лише час дня (ігнорування дати). ( TimeSpanтакож часто використовується для часу, але це вже інша тема.)

    • DateTimeKindЗначення , що надається в .Kindвласності ділить один тип на три, Unspecifiedвид дійсно первинна мета структури, і слід використовувати таким чином. В Utcлюб'язно Вирівнює значення конкретно з UTC, а Localлюб'язні вирівнює значення з місцевим часовим поясом середовища.

      Проблема з окремим прапором для виду полягає в тому, що кожного разу, коли ви споживаєте його DateTime, ви повинні перевіряти, .Kindщоб вирішити, яку поведінку слід прийняти. Рамкові методи все це роблять, але інші часто забувають. Це справді порушення SRP, оскільки тип тепер має дві різні причини зміни (значення та вид).

    • Два з них призводять до використання API, які складаються, але часто є безглуздими або мають дивні випадкові випадки, спричинені побічними ефектами. Поміркуйте:

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

Підсумовуючи це, хоча, якщо анотацію DateTime можна використовувати лише для дати, це слід робити лише тоді, коли кожне місце, де вона використовується, дуже обережно ігнорувати час, а також дуже обережно не намагатися перетворитись на UTC та інше часові зони.


2
Якби тільки System.Time.Date
опинилося

1
Ви можете використовувати це сьогодні, просто підписавшись на канал myget corefx, і ви можете скористатися, System.Timeяк і будь-який інший пакет. Це просто ще не "офіційно".
Метт Джонсон-Пінт

16

Я підозрюю, що немає присвяченого чистого Dateкласу, тому що ви вже маєте, DateTimeякий може впоратися з ним. Це Dateпризведе до дублювання та плутанини.

Якщо ви хочете, щоб стандартний підхід подивився на DateTime.Dateвластивість, яка дає лише частину дати DateTimeз значенням часу, встановленим на 12:00:00 опівночі (00:00:00).


61
Великою перевагою спеціального класу Date є те, що він не страждає від складності часових поясів та літнього часу.
Димитрій К.

3
@DimitriC. Я не погоджуюся - ви можете використовувати DateTime з UTC, і ви не страждаєте від пояснених проблем, а також з DateTime, навіть якщо ви хочете просто дати, ви все одно можете робити математику, яка включає час (тобто, дайте мені дату, якщо я відняти 20 х 2 години від сьогодні).
Роберт Маклін

@Robert MacLean: Дякуємо за підкреслення зручності використання UTC DateTimes. Я зробив кілька тестів, і, схоже, DateTimeKind.Unspecified діє як UTC щодо віднімання. Так що, якщо ви обережно ставитеся до "роду" DateTimes, з яким ви працюєте, все вийде добре.
Димитрій К.

10
Подумати про UTC та щось, що стосується часового поясу - це лише марнотрата енергії, оскільки цього можна легко уникнути окремим класом дати. І я не бачу ніякої плутанини між Date та DateTime.
maulik13

6
Погодьтеся, що C # дійсно повинен мати клас дати. Не тільки річ перетворення часового поясу є постійним джерелом помилок підводних човнів, але це просто боляче при роботі з речами, що базуються на робочому дні, а не на основі часу.
Джуліан Береза

12

Я надіслав електронну пошту refsrcfeedback@microsoft.com, і це їх відповідь

Маркосе, це не вдале місце для запитань таких питань. Спробуйте http://stackoverflow.com Коротка відповідь полягає в тому, що вам потрібна модель для відображення моменту часу, а DateTime це робить, це найкорисніший сценарій на практиці . Те, що люди використовують два поняття (дата і час) для позначення моментів часу, є довільним і не корисним для розділення.

Тільки розв'язуйте, де це гарантовано, не робіть справ лише задля того, щоб робити речі наосліп. Подумайте про це так: яка у вас проблема, яка вирішується шляхом поділу DateTime на дату і час? І які проблеми у вас виникнуть, яких у вас зараз немає? Підказка: якщо ви подивитесь на використання DateTime у межах .NET: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references, ви побачите, що більшість повертається з методу. Якби у нас не було єдиного поняття, як DateTime, вам доведеться використовувати параметри або Tuples, щоб повернути пару дати і часу.

HTH, Кирило Осенков

У своєму електронному листі я запитав, чи це тому, що DateTime використовує TimeZoneInfo, щоб отримати час роботи машини - тепер у власності. Тож я б сказав, що це тому, що "бізнес-правила" є "занадто поєднаними", і вони мені це визнали.


Цей пост справді дає розуміння думок, що стоять за дизайнерським рішенням про відсутність вбудованого класу дати. Яке питання ви їм надіслали? Я не маю на увазі, що я згоден з цим рішенням з тих причин, які @TheMathemagician зазначив вище.
Роберт Йоргенсгард Енгдаль

@ RobertJørgensgaardEngdahl, на жаль, більше не маю доступу до цього облікового запису електронної пошти. Але я вважаю, що я запитав їх, чому вони поєднали час і дату разом у структурі DateTime. І думав, що я згоден з TheMathemagician, я думаю, що MS застосували такий дизайнерський підхід, оскільки, як міжнародна компанія, вона окупається їхніми потребами - а тепер вже пізно змінити її - при цьому розщеплення понять не робить.
MVCDS

2
Можливо, MS міг би просто пройти всю свиню і реалізувати SpaceTimeклас! Гей, на думку Ейнштейна, простір і час тісно пов'язані, тому нам також не потрібно буде розмежовувати їх, правда? (!!!!!!!!!!!) Я свого роду новим для C #, але я повинен сказати, що це мінне поле , виходячи з VB.NET , де є, просто date, Today(), nowі т.д. Ні DateTimeвипереджаючи сміття, немає глузуючи о. (І ці крапки з комою, і ця чутливість до
справді

2
І власний SQL Server має Dateтип, і результат повинен бути типовим Date- якщо це Dateрезультат типу, очікуваний як рядок без часу. Наприклад, Delphi також має Date як DateTime, але typeinfo відрізняється для Date та DateTime.
користувач2091150

1
Кирило Осенков відповідає на запитання "Чому не мати окремих класів дати та часу проти класу DateTime?". Фактичний Q був «Чому не також мати окремі дати і класи часу?». Я розумію, що дата і час повинні бути з'єднані в один Клас для багатьох випадків використання поняття дата-час . Однак, мабуть, принаймні стільки, як не більше, настільки ж дійсних випадків використання лише концепції дати . І звичайно, також існує багато дійсних випадків використання поняття часу .
Том


4

Якщо вам потрібно запустити порівняння дат, тоді використовуйте

yourdatetime.Date;

Якщо ви відображаєтесь на екрані, використовуйте

yourdatetime.ToShortDateString();

Частина .Date - це те, що я шукав.
Брендан Фогт

3

Дозвольте мені міркувати: Можливо, це тому, що до SQL Server 2008 не було типу даних Date в SQL, тому важко було б зберігати його на SQL сервері ?? І це зрештою продукт Microsoft?


Час дат db відрізняється від дати # C. Дати часу db не мають часового поясу, тому вони насправді не відносяться до конкретного моменту. Але C # знає, що мить є і зберігає кліщі з епохи UTC.
artsrc

2
дискусія стосується присвяченого ДАТА, а не стільки про частину дати, щоб я не розумів точку, яку ви намагаєтесь зробити?
Плеун

Це не дає відповіді на запитання. Щоб критикувати або вимагати роз'яснення у автора, залиште коментар під їх дописом.
Barranka

@Barranka - Питання містить "Чому в C # немає класу Дата?"
СТЛДєв

2

Хто знає, чому саме так. У структурі .NET є багато поганих дизайнерських рішень. Однак я думаю, що це досить другорядне. Ви завжди можете проігнорувати часову частину, тому навіть якщо якийсь код вирішує мати дату DateTime більше, ніж просто дату, код, який хвилює, повинен коли-небудь дивитися на частину дати. Крім того, ви можете створити новий тип, який представляє просто дату та використовувати функції в DateTime для виконання важкого підйому (розрахунки).


1
Я дійсно не думаю, що це було поганим рішенням, хочете ви використовувати лише побачення чи ні. Я не підкажу тебе, але це моя думка.
JonH

Я не думаю, що я це добре написав. Насправді у мене з цим не виникає особливих проблем, хоча я міг бачити, як з точки зору абстракції / елегантності було б доречніше два чи три типи. Моя думка справді полягала в тому, що в рамках .NET є багато речей, які можуть залишити вас дряпати голову, і не варто занадто засмучуватися, особливо враховуючи, що ця "проблема" є досить незначною порівняно з деякими кричущими дизайнерськими рішеннями (загальними обмеження).
siride

+1, тому що це правда ... це була єдина проблема (або найбільша) .NET :-) :-) Скільки версій SQL Server їм потрібно було додати типи DATE та TIME? І там вони були набагато кориснішими (принаймні з міркувань цілісності)
xanatos

Я також повинен додати, що я вважаю, що "все починається з -100 балів" - це хороший спосіб створити рамки, що не мають задоволення, і це може бути однією з речей, які потрапили в цей сміття.
siride

2
Мене просто покусало це питання, тому що 1 частина коду нехтувала використанням властивості .Date і, отже, не порівнювалась належним чином. Я, безумовно, думаю, що потрібен тип дати, який не зберігає жодного разу, щоб уникнути помилок такого типу
JoelFan

2

Чому? Ми можемо лише міркувати, і це не дуже допомагає вирішити інженерні проблеми. Хороша здогадка полягає в тому, що DateTimeмістить всю функціональність, яку мала б мати така структура.

Якщо це насправді має значення для вас, просто загорніть DateTimeу власну незмінну структуру, яка відображає лише дату (або подивіться на DateTime.Dateвластивість).


2

Крім відповіді Роберта, у вас є також DateTime.ToShortDateStringметод. Крім того, якщо ви дійсно хотіли об'єкт Date, ви завжди можете використовувати шаблон Adapter і обгортати об'єкт DateTime, викриваючи лише те, що вам потрібно (тобто місяць, день, рік).


2

Завжди є DateTime.Dateмайно, яке скорочує часову частину DateTime. Можливо, ви можете інкапсулювати або обернути DateTime у своєму власному типі дати.

А щодо питання, чому, ну, мабуть, вам доведеться запитати Андерса Гельсберга.


1

Тому що, щоб знати дату, ви повинні знати системний час (тиками), який включає час - так навіщо викидати цю інформацію?

DateTimeмає Dateмайно, якщо ви зовсім не піклуєтеся про час.


1

Так, також System.DateTime запечатаний. Я бачив, як деякі люди грають у цю гру, створюючи спеціальний клас, щоб отримати строкове значення часу, як згадувалося в попередніх публікаціях, таких як:

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

Це, можливо, непотрібно, оскільки ви можете просто витягнути GetShortTimeString з простого старого типу DateTime без нового класу


0

Якщо ви використовуєте властивості Date або Today, щоб отримати лише частину дати від об'єкта DateTime.

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

Тоді ви отримаєте компонент дати лише з часовим компонентом, встановленим на півночі.


1
це точно не те, що я хотів
Карло В. Данго

@Carlo V. Dango: Я не згоден. Я думаю, що це саме те, що ти хотів.
siride

1
@Carlo V. Dango: Що ти конкретно хочеш зробити, щоб ці властивості не дозволяли тобі досягти?
eph_tagh

5
Це досить просто: слід пам’яті дати може бути лише половиною сліду пам’яті DateTime (32 замість 64 біт). Ви будете впевнені, що ваш дурний колега не зробив .AddHours (1) до вашої дати, змінивши його, але "зберігаючи те саме" від POV "лише дати". Якщо (для помилки) DateTime встановлений на DateTimeKind.Local, а час нормалізується на UTC, дата, ймовірно, зміниться (трапилось зі мною через використання XmlSerialization та погано зроблений туди і назад в JSON) ... Чи достатньо?
xanatos
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.