Непоследовательное поведение toLocaleString() в разных браузерах

Я работаю над проектом, где мне приходится много работать с датой и временем. Серверная технология - это ASP.Net, а на стороне клиента я использую jQuery и jQuery Week Calendar (плагин jQuery).

Итак, вот описанная проблема, я получаю Data Time с сервера примерно как 2012-11-13T04:45:00.00 в формате GMT.

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

Для этого я использую метод JavaScript toLocaleString(). Это работает только в Chrome, в другом браузере работает непоследовательно.

Вот его выходы в разных браузерах:

Google Chrome (отлично работает):

Вызов:

new Date ("2012-11-13T04:45:00.00").toLocaleString();

Вывод:

Tue Nov 13 2012 10:15:00 GMT+0530 (India Standard Time)

Mozilla Firefox:

Вызов:

new Date ("2012-11-13T04:45:00.00").toLocaleString();

Вывод:

Tuesday, November 13, 2012 4:45:00 AM

Safari:

Вызов:

new Date ("2012-11-13T04:45:00.00").toLocaleString();

Вывод:

Invalid Date

Internet Explorer:

Вызов:

new Date ("2012-11-13T04:45:00.00").toLocaleString();

Вывод:

Tuesday, November 13, 2012 4:45:00 AM

Пока это браузеры, где я тестировал.

Вот Вопрос:

Мне нужен способ конвертировать Data Time (с форматом, подобным этому 2012-11-13T04:45:00.00), в "Дата и время локали", независимо от того, какой браузер использует браузер.

Ответ 1

Короткий ответ - нет. toLocaleString может быть реализовано, однако разработчики хотят. Что ваш вопрос подразумевает, так это то, что Chrome выводит нужную строку.

Если вы хотите последовательно выводить этот формат, вам нужно будет использовать отдельную библиотеку - например DateJS.

Для этого с DateJS понадобятся некоторые стандартные спецификаторы формата, доступные в core.js, и некоторые, которые доступны только в extras.js. В документации есть список всех спецификаторов формата.

Нужная строка:

Tue Nov 13 2012 10:15:00 GMT+0530 (India Standard Time)

Итак, чтобы получить это от DateJS, вам понадобится:

"D M d Y H:i:s \G\M\TO (e)"

Синтаксис DateJS:

new Date ("2012-11-13T04:45:00.00").format("D M d Y H:i:s \G\M\TO (e)");

Ответ 2

Вместо использования toLocaleString(), который устарел и неправильно реализован для всех веб-браузеров, я настоятельно рекомендую использовать Globalize для даты и форматирование времени.

Затем, чтобы отформатировать дату на стороне клиента, вам нужно всего лишь назначить правильную культуру и просто вызвать функцию формата:

Globalize.culture(theCulture);
Globalize.format( new Date(2012, 1, 20), 'd' ); // short date format
Globalize.format( new Date(2012, 1, 20), 'D' ); // long date format

Довольно просто, не так ли? Ну, вам также придется интегрировать его с вашим приложением ASP.Net, что немного усложняет ситуацию. Во-первых, вам нужно будет ссылаться на globalize.js обычным способом:

<script type="text/javascript" src="path_to/globalize.js"></script>

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

<script type="text/javscript" src="path_to/cultures/globalize.culture.<% = CultureInfo.CurrentCulture.ToString() %>.js"></script>

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

<script type="text/javscript">
    var theCulture = <% = CultureInfo.CurrentCulture.ToString() %>
</script>

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

public string IntegrateGlobalize(string pathToLibrary)
{
  var sb = new StringBuilder();
  sb.Append("<script type=\"text/javascript\" src=\"");
  sb.Append(pathToLibrary);
  sb.AppendLine("/globalize.js\"></script>");
  sb.Append("<script type=\"text/javascript\" src=\"");
  sb.Append(pathToLibrary);
  sb.AppendLine("/cultures/globalize.culture.");
  sb.Append(CultureInfo.CurrentCulture);
  sb.AppendLine(\"></script>");
  sb.Append("<script type=\"text/javascript\">");
  sb.Append("var theCulture = ");
  sb.Append(CultureInfo.CurrentCulture);
  sb.AppendLine(";</script>");

  return sb.ToString();
}

Затем все, что вам нужно сделать, это ссылка на этот метод в заголовке страницы (мастер?):

<head>
  <% = IntegrateGlobalize("path_to_globalize") %>
  ...
</head>

Некоторые проблемы

Если вы хотите сделать это на 100% правильно, вам нужно будет увеличить генератор культуры глобализации, включив в него переключатель формата 'g', а затем используйте этот точный переключатель на стороне клиента для форматирования даты:

Globalize.format( new Date(2012, 1, 20), 'g' ); // default date format

Почему? Потому что 'g' - формат даты по умолчанию. Это то, что вы получите, когда просто вызываете DateTime ToString() метод без параметров (что будет означать CultureInfo.CurrentCulture как единственный параметр...). Формат по умолчанию лучше всего, он будет либо коротким, либо длинным, либо любым другим, но наиболее часто используемым людьми, использующими эту культуру.

Я сказал, что toLocaleString() неверно для всех веб-браузеров. Почему это? Это потому, что он будет использовать настройки веб-браузеров, а не серверную обнаруженную культуру. Это означает, что у вас могут быть смешанные культуры на одной и той же веб-странице. Это может произойти, если некоторые ваши даты отформатированы на стороне сервера и некоторые другие на стороне клиента. Вот почему нам нужно было пройти (обнаруженную) культуру со стороны сервера.
КСТАТИ. Если вы решите включить диалог региональных настроек в свое веб-приложение, рассогласование будет еще более заметным, так как toLocaleString() не будет следовать настройкам пользователя...

Ответ 3

Чтобы преобразовать время в конкретную строку на сервере, вы можете использовать метод DateTime.ToLongDateString. На этой странице см. Примечание о "текущем объекте культуры" (на сервере) класса DateTimeFormatInfo. Убедитесь, что установлены правильно.

Ответ 4

Основная причина этой проблемы никогда не решалась ни одним из ответов. ОП сказал:

Я получаю Data Time от сервера что-то вроде этого 2012-11-13T04:45:00.00 в формате GMT.

GMT не является форматом. Эта строка находится в расширенном формате ISO 8601 без указания указанного часового пояса. Спецификация ISO 8601 гласит, что без квалификатора это предназначено для представления локального времени. Чтобы указать GMT, вы должны добавить конец Z в конец или добавить смещение, например +00:00.

Проблема заключается в том, что ECMAScript (v1 - v5.1) не соблюдал это положение в спецификации. Фактически он сказал, что его следует интерпретировать как UTC вместо локального времени. Некоторые браузеры удостоились спецификации ISO, некоторые почитали спецификацию ECMA. Это исправлено для версии 6, и большинство браузеров выполнили.

Итак, если вы собираетесь передавать отметки времени на основе UTC/GMT, то на стороне сервера вы всегда должны отправлять Z, чтобы не было двусмысленности.

Тем не менее, даже если значение правильно интерпретировано, нет никакой гарантии, что строки будут отформатированы одинаково во всех браузерах. Для этого вам действительно нужна библиотека. Я рекомендую moment.js, но есть и другие.