Работа с датами и временем в формах Razor Pages

  • Михаил
  • 12 мин. на прочтение
  • 137
  • 05 Dec 2022
  • 05 Dec 2022

При работе с датами и временем в форме Razor Pages вам необходимо отобразить подходящий элемент управления в соответствии с требованиями задачи. До HTML5 разработчики в значительной степени зависели от сторонних библиотек выбора даты. В настоящее время существует множество вариантов нативных браузеров, хотя они и поддерживаются в разных современных браузерах по -разному . К ним относятся параметры для управления датой и временем, просто датой или временем, а также для работы с месяцем или неделей года.

 

Входы даты и времени

В Razor Pages помощник входного тега отображает соответствующее значение для type атрибута на основе типа данных свойства модели, указанного через asp-forатрибут.

[BindProperty]

public DateTime DateTime { get; set; }

 

<input type="date" class="form-control" asp-for="DateTime" />

Тип ввода по умолчанию, созданный для DateTime свойств, находится datetime-localв .NET Core 2.0 и более поздних версиях, когда были представлены Razor Pages. В ASP.NET Core 1.x и в MVC 5 и более ранних версиях вспомогательные функции тегов и строго типизированные вспомогательные функции Html будут отображать тип ввода как datetime, но это было исключено из спецификации HTML5, поскольку оно никогда не было реализовано ни одним из поставщиков браузеров.

В Chrome, Edge и Opera datetime-local отображает элемент управления, который позволяет пользователю выбрать дату и время. Формат отображения даты и времени в элементе управления определяется языковыми настройками базовой операционной системы, и предполагается, что само значение представляет локальную дату и время, а не универсальное время:

<input type="date" class="form-control" asp-for="DateTime" />

Если вы используете другой браузер (IE 11, Firefox, Safari), элемент управления отображает простой ввод, который ведет себя как ввод текста.

Изучая визуализированную разметку, вы видите, что value помощник тега ввода отформатировал ее в представление, основанное на стандарте ISO 8601, как указано в RFC3339 :

Это формат, который требуется элементу управления HTML5. Вам следует помнить об этом, если вы попытаетесь применить значение к элементу управления самостоятельно, например, с помощью скрипта. Если вам нужно сгенерировать значение в подходящем формате с помощью .NET, вы можете использовать строку формата «O» (или «o») , хотя вам нужно будет установить значение Kind, Unspecifiedчтобы убедиться, что смещение часового пояса не включено в вывод. потому что datetime-localэлемент управления не поддерживает это:

var dt = new DateTime(DateTime.Now.Ticks, DateTimeKind.Unspecified);
var isoDateString = dt.ToString("O");

По умолчанию отформатированная строка включает время с точностью до миллисекунды, поэтому часть пользовательского интерфейса средства выбора времени предоставляет параметры для установки часов, минут, секунд и миллисекунд:

Чаще всего вам потребуется только разрешить пользователю указывать время с точностью до минуты. Вы управляете этим посредством форматирования временной части значения, передаваемого элементу управления. Вы можете сделать это одним из двух способов. Вы можете использовать DisplayFormatатрибут аннотации данных в свойстве модели, чтобы указать формат и убедиться, что формат также применяется, когда значение находится в «режиме редактирования» (элемент управления формы):

[BindProperty, DisplayFormat(DataFormatString = "{0:yyyy-MM-ddTHH:mm}", ApplyFormatInEditMode = true)]
public DateTime DateTime { get; set; }

В качестве альтернативы вы можете использовать asp-formatатрибут самого помощника входного тега:

<input type="date" class="form-control" asp-for="DateTime" />

 

Значением по умолчанию для a DateTimeв .NET является DateTime.MinValue, представленное 0001-01-01T00:00:00в элементе управления. Если вы не хотите, чтобы какое-либо значение появлялось изначально, вы можете сделать связанное свойство обнуляемым:

[BindProperty]
public DateTime? DateTime { get; set; }

Затем элемент управления отобразит свои настройки по умолчанию:

Ввод даты и времени

Чтобы поддерживать более широкий спектр браузеров, использующих собственные элементы управления, а не сторонние библиотеки, вы можете использовать отдельные элементы управления date и time. Требуется немного больше настроек, чтобы вспомогательная функция входного тега отображала правильные элементы управления:

[BindProperty, DataType(DataType.Date)]
public DateTime Date { get; set; }
[BindProperty, DataType(DataType.Time)]
public DateTime Time { get; set; }

Оба свойства являются DateTimeтипами, но DataType атрибут применяется к ним, чтобы установить правильный тип в отображаемом вводе. Помощник входного тега поддерживает параметры DataType.Dateи DataType.Timeи будет отображаться соответствующим образом:

Еще раз, вы можете отформатировать время, применив строку формата либо к DisplayFormatатрибуту свойства модели, либо через asp-formatатрибут вспомогательной функции тега. Когда значения публикуются, связыватель модели успешно создает DateTimeтипы с частью времени, установленной на полночь в случае date входного значения, и датой, установленной на сегодняшний день в случае timeвходного значения. Вы можете комбинировать значения для создания нового DateTime:

DateTime dt = Date.Add(Time.TimeOfDay);

Всемирное координированное время

Универсальное скоординированное время (или UTC) рекомендуется для использования в приложениях, требующих хранения или представления даты и времени в независимом от часового пояса виде. Более подробно об этом вы можете прочитать в подробном посте Рика Страла на эту тему . Ни один из элементов управления датой или временем не поддерживает представление ISO 8601 значения времени UTC, например yyyy-MM-ddTHH:mm:ssZ(где Zнаходится информация о часовом поясе, представляющая смещение нулевого часового пояса, и поэтому идентифицирует это значение как время UTC). Однако вам, возможно, придется работать с приложениями, в которых этот стандартный формат используется для обмена информацией о времени.

В приложениях .NET Core 3.0 или более ранних версиях связыватель модели успешно создаст DateTimeзначение из допустимой строки времени в формате UTC ISO 8601, но будет генерировать местное время на основе параметров сервера, на котором выполняется приложение.

Например, возьмем значение, представляющее 02:15 утра 30 октября 2020 года, UTC: 2020-10-30T02:15:00Z. На следующем изображении показано, как средство связывания модели анализирует это значение, когда сервер настроен на тихоокеанский часовой пояс:

По умолчанию DateTimeModelBinderон может распознавать и обрабатывать строки даты и времени, содержащие информацию о часовом поясе. Если часовой пояс отсутствует, связующее задает для Kindсвойства результирующего DateTimeзначения значение DateTimeKind.Unspecified. Другими DateTimeKindзначениями являются Local(представляющие местное время) и Utc(время UTC). Обратите внимание, что Kindна изображении выше установлено значение Localвместо Utc, несмотря на то, что это явно время UTC, учитывая наличие Zв конце строки. Связыватель преобразовал время UTC в местное время на основе настроек сервера. Когда я пишу это сегодня (30 октября), применяется тихоокеанское летнее время, которое на 7 часов раньше значения UTC, т.е. прошлой ночью. Завтра, когда на западном побережье США закончится переход на летнее время, сгенерированное значение будет на 8 часов раньше UTC, поэтому проанализированное значение снова будет представлять другое время. Чтобы получить значение UTC, вам нужно либо использовать ToUniversalTime()метод для проанализированного результата:

Или вы можете реализовать свою собственную привязку модели для глобальной обработки строк времени UTC в приложении. Я рассмотрю пользовательские связыватели моделей в своей следующей статье для связанной задачи.

Хорошей новостью является то, что начиная с .NET 5 это было решено, так что время в формате UTC правильно обрабатывается связывателем модели, не требуя дополнительной обработки связанного значения:

Ни одно из значений не было скорректировано, и Kindустанавливается Utcавтоматически.

Месяц и неделя

Типы ввода месяца и недели в настоящее время реализованы в Edge, Chrome и Opera. Там, где он поддерживается, этот monthтип предоставляет пользователю возможность выбрать конкретный месяц и год:

Помощник входного тега отобразит элемент управления с type="month" небольшой настройкой. Это достигается с помощью DataType перегрузки атрибута, которая принимает строковый параметр, представляющий пользовательский тип данных:

[BindProperty, DataType("month")] 
public DateTime Month { get; set; }

Формат входного значения месяца: yyyy-MM.Помощник входного тега успешно генерирует подходящее значение из DateTimeтипа. Таким образом, вам не нужно применять какие-либо строки формата, чтобы селектор месяца отображался правильно:

 

 

Когда значения отправляются обратно, по умолчанию DateTimeModelBinder это значение привязывается к a DateTimeс правильным месяцем и годом, а день установлен на 1.

Тип weekввода также будет успешно отображаться, просто установив пользовательский тип данных для DateTimeсвойства:

[BindProperty, DataType("week")] 
public DateTime Week { get; set; }

Допустимый формат значения: yyyy-Www, где заглавная буква Wпредставляет собой букву «W» ww, представляющую неделю выбранного года по стандарту ISO 8601. В .NET нет строки формата для недельной части a DateTime, но помощник тега успешно генерирует правильно отформатированное значение из DateTimeтипа:

Однако по умолчанию DateTimeModelBinderневозможно привязать это значение обратно к файлу DateTime. У вас есть несколько вариантов. Самый грубый вариант — получить доступ к значению напрямую из Request.Formколлекции, разобрать его как строку и сгенерировать DateTimeсамостоятельно:

public void OnPost()
{
   var week = Request.Form["Week"].First().Split("-W");
   Week = ISOWeek.ToDateTime(Convert.ToInt32(week[0]), Convert.ToInt32(week[1]), DayOfWeek.Monday);
}

В этом примере используется служебный класс ISOWeek, добавленный в .NET Core 3.0 . Если вы работаете над проектом .NET Core 2, вы можете использовать Calendar.GetWeekOfYear(метод ), но имейте в виду, что в некоторых крайних случаях он не возвращает неделю года по стандарту ISO 8601 .

Вы также можете привязаться к stringвместо DateTime. Вам нужно будет сгенерировать правильно отформатированное значение, а также проанализировать результат:

[BindProperty, DataType("week")]
public string StringWeek { get; set; }

public void OnGet()
{
   StringWeek = $"{DateTime.Now.Year}-W{ISOWeek.GetWeekOfYear(DateTime.Now)}";
}

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

Резюме

В подавляющем большинстве случаев HTML5, помощник тега ввода и тег по умолчанию DateTimeModelBinderсочетаются друг с другом, чтобы упростить работу с датами и временем в форме Razor Pages. Браузерные реализации входных данных HTML5 управляют данными стандартным способом, отображая их в формате, с которым знаком пользователь, уменьшая зависимость разработчиков от сторонних компонентов.