Тип для даты только в C # - почему нет типа даты?

108

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

Какие стандартные подходы к этой проблеме? Неужто я не первая сталкиваюсь с этим? Почему Dateв C # нет класса?

Есть ли у кого-нибудь хорошая реализация с использованием структуры и, возможно, некоторых методов расширения в DateTime и, возможно, реализации некоторых операторов, таких как == и <,>?

Карло В. Данго
источник
1
Хотя я понимаю, что мне нужна явная, ясная семантика, какие именно проблемы это DateTimeсоздает?
Джефф Стернал,
15
1 Мне нужно не забыть убрать часы в начале метода. +2 не очень хорошо сообщается, что работает исключительно по датам. Это важно, например, при хранении и загрузке из Db, где будет достаточно узкого типа. Программирование - это общение для людей, а не для компьютеров,
Карло В. Данго
6
Я просто хотел сказать, что отсутствие класса даты - это большая проблема, а использование DateTime вообще бесполезно. Как только вы сохраняете свои "даты" как дату и время, вы становитесь заложником проблем с экономией времени на летнее время в локали / часовом поясе. Выбросив часть времени, вы можете вернуть все ваши даты на день, когда часы изменятся (!). И пользователи в разных часовых поясах будут видеть разные даты, когда они попытаются преобразовать дату и время. Дата-время отлично подходит для представления точных моментов времени (с некоторой точки или чего-то еще), но они очень не подходят для представления абстрактной даты.
TheMathemagician
3
Более поздний аналогичный вопрос stackoverflow.com/questions/7167710/… , и Джон Скит говорит, что должна быть дата.
goodeye
9
Тип данных только для даты относится к DateTime, как целочисленный тип данных - к десятичному. Те, кто утверждает, что нам не нужна дата, потому что вы можете просто выбросить временную часть, все равно что сказать, что нам не нужны целые числа, поскольку мы можем выбросить десятичную часть. В нашем мире есть понятие даты, которое не включает время. 5 марта - это не 5 марта 00:00:00.
Vague

Ответы:

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любезные выравнивает значение с местным часовым поясом среды.

      Проблема с отдельным флагом для вида состоит в том, что каждый раз, когда вы потребляете a 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 !!!

Таким образом, хотя a DateTime может использоваться только для даты, он должен делать это только тогда, когда каждое место, которое его использует, очень осторожно игнорирует время, а также очень осторожно, чтобы не пытаться преобразовать в и из UTC или другого часовые пояса.

Мэтт Джонсон-Пинт
источник
2
Если System.Time.Dateбы только
попал
1
Вы можете использовать это сегодня, просто подпишитесь на канал corefx myget, и вы сможете использовать его, System.Timeкак любой другой пакет. Это просто еще не "официально".
Мэтт Джонсон-
16

Я подозреваю, что нет специального чистого Dateкласса, потому что у вас уже есть тот, DateTimeкоторый может с ним справиться. Наличие Dateпривело бы к дублированию и путанице.

Если вам нужен стандартный подход, посмотрите на DateTime.Dateсвойство, которое дает только часть даты в a DateTimeсо значением времени, установленным на 12:00:00 полночь (00:00:00).

Роберт Маклин
источник
61
Большим преимуществом выделенного класса Date является то, что он не страдает сложностью часовых поясов и перехода на летнее время.
Dimitri C.
3
@DimitriC. Я не согласен - вы можете использовать DateTime с UTC, и вы не страдаете от описанных проблем, плюс с DateTime, даже если вам нужны только даты, вы все равно можете выполнять математику, которая включает время (т.е. дайте мне дату, если я вычту 20 x 2 часа с сегодняшнего дня).
Роберт Маклин,
10
Необходимость думать о UTC и обо всем, что связано с часовым поясом, - это пустая трата энергии, потому что этого можно легко избежать с помощью отдельного класса Date. И я не вижу путаницы между Date и DateTime.
maulik13
6
Согласитесь, что в C # действительно должен быть класс Date. Преобразование часового пояса не только является постоянным источником подводных ошибок, но и просто болезненно, когда имеешь дело с вещами, основанными на рабочем дне, а не на времени.
Джулиан Берч
1
@RobertMacLean: нет смысла рассматривать «дату, если я вычту 20 x 2 часа из сегодняшнего дня». Считаете ли вы в этом примере «сегодня в полночь»? «сегодня в полдень»? и т.д. Эта проблема плохо сформулирована. Если вы имеете в виду «сегодня в полночь», вы уже работаете с DateTime, а не с Date. Чтобы вычесть часы, вам нужен час, а не день.
Ксавье
12

Я написал по электронной почте refsrcfeedback@microsoft.com, и это их ответ

Маркос, это не лучшее место, чтобы задавать подобные вопросы. Попробуйте http://stackoverflow.com Короткий ответ заключается в том, что вам нужна модель для представления момента времени, и DateTime это делает, это самый полезный сценарий на практике. . Тот факт, что люди используют два понятия (дату и время) для обозначения моментов времени, произвольный и не имеет смысла разделять.

Разъединяйте только там, где это оправдано, не делайте чего-либо только ради того, чтобы делать что-то вслепую. Подумайте об этом так: какая у вас проблема, которую можно решить, разделив DateTime на Date и Time? И какие проблемы вы получите, которых у вас нет сейчас? Подсказка: если вы посмотрите на использование DateTime в .NET framework: http://referencesource.microsoft.com/#mscorlib/system/datetime.cs#df6b1eba7461813b#references, вы увидите, что большинство из них возвращается из метода. Если бы у нас не было единой концепции, такой как DateTime, вам пришлось бы использовать параметры out или кортежи для возврата пары даты и времени.

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

В своем электронном письме я спросил, было ли это из-за того, что DateTime использует TimeZoneInfo для получения времени машины - в соответствии с требованиями Now. Я бы сказал, что это потому, что «бизнес-правила» «слишком взаимосвязаны», они мне подтвердили это.

MVCDS
источник
Этот пост действительно дает представление о мыслях, лежащих в основе дизайнерского решения об отсутствии встроенного класса даты. Какой вопрос вы им прислали? Я не хочу сказать, что согласен с этим решением по тем самым причинам, которые @TheMathemagician перечислил выше.
Роберт Йоргенсгаард Энгдал
@ RobertJørgensgaardEngdahl, к сожалению, у меня больше нет доступа к этой учетной записи электронной почты. Но я думаю, что спросил их, почему они объединили время и дату в структуре DateTime. И подумал, что я согласен с TheMathemagician, я думаю, что MS приняла этот подход к дизайну, потому что как международная компания он окупает их потребности - а теперь уже слишком поздно его менять, - а разделение концепций - нет.
MVCDS
2
Может быть, MS могла бы просто пойти на все и реализовать SpaceTimeкласс! Эй, согласно Эйнштейну, пространство и время тесно связаны, так что нам не нужно различать их, верно? (!!!!!!!!!!!) Я своего рода новым для C #, но я должен сказать, что это минное поле , исходя из VB.NET , где есть, просто date, Today(), nowи т.д. Нет DateTimeпредваряя мусор, нет возиться. (И эти точки с запятой и эта чувствительность к регистру, конечно, утомительны! Просто стреляйте в меня сейчас!)
SteveCinq
2
И их собственный SQL Server имеет Dateтип, а результат должен быть типа Date- если это был Dateрезультат типа, ожидаемый как строка без времени. Например, Delphi также имеет Date как DateTime, но typeinfo отличается для Date и DateTime.
user2091150
1
Кирилл Осенков отвечает на вопрос «Почему бы не иметь отдельные классы даты и времени по сравнению с классом DateTime?». Фактический Q был «Почему не также иметь отдельные даты и классы времени?». Я понимаю, что дата и время должны быть объединены в один класс для множества вариантов использования концепции даты и времени . Однако, вероятно, существует как минимум столько же, если не больше, так же, как и допустимые варианты использования только концепции даты . И, конечно же, есть много правильных вариантов использования концепции времени .
Том
4

Если вам нужно выполнить сравнение дат, используйте

yourdatetime.Date;

Если вы показываете на экран, используйте

yourdatetime.ToShortDateString();
MattP
источник
Часть .Date - это то, что я искал.
Брендан Фогт
3

Позвольте мне порассуждать: может быть, это потому, что до SQL Server 2008 в SQL не было типа данных Date, поэтому было бы сложно хранить его на SQL-сервере ?? И это все-таки продукт Microsoft?

Pleun
источник
Datetime в базе данных отличается от datetime в C #. У db datetime нет часового пояса, поэтому они фактически не относятся к конкретному моменту. Но C # знает, что это момент, и хранит тики с эпохи UTC.
artsrc
2
обсуждение посвящено DATE, а не части datetime, поэтому я не понимаю, о чем вы пытаетесь сказать?
Pleun
Это не дает ответа на вопрос. Чтобы критиковать или запросить разъяснения у автора, оставьте комментарий под его сообщением.
Barranka
@Barranka - Вопрос содержит "Почему в C # нет класса Date?"
STLDev
2

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

сирида
источник
1
Я действительно не думаю, что это было плохим решением, хотите ли вы использовать только свидание или нет. Я не буду отрицать тебя, но это мое мнение.
JonH
Не думаю, что я это хорошо сформулировал. На самом деле, у меня нет особых проблем с этим, хотя я мог видеть, что наличие двух или трех типов было бы более подходящим с точки зрения абстракции / элегантности. Я действительно хотел сказать, что в .NET framework есть много вещей, которые могут заставить вас почесать голову, и не стоит сильно расстраиваться, особенно учитывая, что эта «проблема» довольно незначительна по сравнению с некоторыми вопиющими дизайнерскими решениями (общие ограничения).
siride
+1, потому что это правда ... это была единственная (или самая большая) проблема .NET :-) :-) Сколько версий SQL Server им нужно было для добавления типов DATE и TIME? И там они были НАМНОГО полезнее (по крайней мере, из соображений честности)
xanatos
Я должен также добавить, что я думаю, что «все начинается с -100 баллов» - это хороший способ создать убогий фреймворк, и это может быть одной из тех вещей, которые попали в этот мусор.
siride
2
Меня просто укусила эта проблема, потому что одна часть кода не использовала свойство .Date и, следовательно, не сравнивалась должным образом. Я определенно думаю, что нужен тип Date, который не хранит время, чтобы избежать ошибок такого типа
JoelFan
2

Зачем? Мы можем только строить предположения, и это мало помогает в решении инженерных проблем. Хорошее предположение состоит в том, что он DateTimeсодержит все функции, которые могла бы иметь такая структура.

Если это действительно важно для вас, просто оберните DateTimeсвою собственную неизменяемую структуру, которая отображает только дату (или посмотрите на DateTime.Dateсвойство).

Джейсон
источник
2

В дополнение к ответу Роберта у вас также есть DateTime.ToShortDateStringметод. Кроме того, если вам действительно нужен объект Date, вы всегда можете использовать шаблон адаптера и обернуть объект 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 без нового класса.

Ta01
источник
0

Если вы используете свойства Date или Today для получения только части даты из объекта DateTime.

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

Тогда вы получите компонент даты только с компонентом времени, установленным на полночь.

eph_tagh
источник
1
это определенно не то, что я хотел
Карло В. Данго
@ Карло В. Данго: Я не согласен. Я думаю, это именно то, что вы хотели.
siride
1
@Carlo V. Dango: Что вы конкретно хотите сделать, чего не позволяют эти свойства?
eph_tagh
5
Это довольно просто: объем памяти Date, вероятно, будет только половиной объема памяти DateTime (32 бита вместо 64). Вы были бы уверены, что ваш глупый коллега не .AddHours (1) к вашему свиданию не изменил его, но "сохранил то же самое" с точки зрения "только свидание". Если (из-за ошибки) DateTime установлен на DateTimeKind.Local, а время нормализовано по UTC, Date, вероятно, изменится (случилось со мной из-за использования XmlSerialization и плохо выполненного двустороннего обращения к JSON) ... Достаточно?
xanatos