Работа с датами и временем в формах Razor Pages
При работе с датами и временем в форме 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 управляют данными стандартным способом, отображая их в формате, с которым знаком пользователь, уменьшая зависимость разработчиков от сторонних компонентов.
Только полноправные пользователи могут оставлять комментарии. Аутентифицируйтесь пожалуйста, используя сервисы.