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

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

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

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

Ответ 1

Позвольте мне добавить обновление к этому классическому вопросу:

  • Jon Skeet Теперь библиотека Noda Time довольно зрелая и имеет тип даты, называемый LocalDate. (Локальный в этом случае означает только локальный для кого-то, не обязательно локальный для компьютера, на котором выполняется код.)

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

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

  • Существует логическое несоответствие между датой и значением date-at-halfnight.

    • Не каждый локальный день имеет полночь в каждом часовом поясе. Пример: Бразилия spring -forward переход на летнее время перемещает часы с 11:59:59 до 01:00:00.

    • Дата-дата всегда относится к определенному времени в течение дня, а дата-только может относиться к началу дня, концу дня или всему диапазону дня.

  • Прикрепление времени к дате может привести к изменению даты, поскольку значение передается из одной среды в другую, если часовые пояса не наблюдаются очень осторожно. Это обычно происходит в JavaScript (чей объект Date действительно является датой + временем), но может легко произойти и в .NET, или в сериализации, поскольку данные передаются между JavaScript и .NET.

  • Сериализация a DateTime с помощью XML или JSON (и других) всегда будет включать время, даже если это не важно. Это очень сбивает с толку, особенно учитывая такие вещи, как даты рождения и юбилеи, где время не имеет значения.

  • Архитектурно, DateTime является DDD value- объект, но он нарушает принцип одиночной ответственности несколькими способами:

    • Он предназначен как тип даты + времени, но часто используется как только дата (игнорирование времени) или время суток (без учета даты). (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 !!!
      

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

Ответ 2

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

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

Ответ 3

Я написал по электронной почте [email protected] и что их ответ

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

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

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

В своем электронном письме я спрашивал, было ли это из-за того, что DateTime использует TimeZoneInfo, чтобы узнать время машины - в разделе "Сейчас". Так что я бы сказал, потому что "бизнес-правила" "слишком связаны", они признались мне в этом.

Ответ 5

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

yourdatetime.Date;

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

yourdatetime.ToShortDateString();

Ответ 6

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

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

Ответ 7

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

И на вопрос, почему, ну, я думаю, вам придется спросить Андерса Хелсберга.

Ответ 8

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

Ответ 9

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

Ответ 10

В дополнение к ответу Роберта вы также имеете метод DateTime.ToShortDateString. Кроме того, если вам действительно нужен объект Date, вы всегда можете использовать шаблон адаптера и обернуть объект DateTime, показывая только то, что вы хотите (например, месяц, день, год).

Ответ 11

Поскольку для того, чтобы знать дату, вы должны знать системное время (в тиках), которое включает в себя время - так зачем выбрасывать эту информацию?

DateTime имеет свойство Date, если вам не все равно о времени.

Ответ 12

Да, также 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 без нового класса

Ответ 13

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

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

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