Подтвердить что ты не робот

Почему AD3AD08 представляет допустимую дату в платформе .NET?

DateTime.Parse("AD3AD08")

[2017-08-03 12:00:00 AM]

Почему эта строка (которая выглядит как обычная шестнадцатеричная строка для меня) успешно анализируется как дата? Я вижу, что 3 и 8 получают парсы в виде месяцев и дней. Но в остальном это не имеет смысла для меня.

4b9b3361

Ответ 1

tl; dr: вы можете использовать DateTimeFormatInfo.GetEraName/GetAbbreviatedEraName возвращает как разделитель, игнорируя случай. Заказ: день, месяц, год (необязательно).


Кажется, вы всегда можете использовать календарь сокращенное название эры или full era-name в качестве разделителя для токенов DateTime. Для английских культур это AD или A.D., например. для немецких культур n. Chr..

var enCulture = new CultureInfo("en-GB");
System.Threading.Thread.CurrentThread.CurrentCulture = enCulture;
var fi = enCulture.DateTimeFormat;
int currentEra = enCulture.Calendar.GetEra(DateTime.Now);
var eraName = fi.GetEraName(currentEra);
var shortEra = fi.GetAbbreviatedEraName(currentEra);
var date = DateTime.Parse($"{shortEra}3{shortEra}08"); // AD or A.D. works

var deCulture = new CultureInfo("de-DE");
System.Threading.Thread.CurrentThread.CurrentCulture = deCulture;
fi = deCulture.DateTimeFormat;
currentEra = deCulture.Calendar.GetEra(DateTime.Now);
eraName = fi.GetEraName(currentEra);
shortEra = fi.GetAbbreviatedEraName(currentEra);
date = DateTime.Parse($"{shortEra}3{shortEra}08");  // n. Chr. works

Интересно, что он нечувствителен к регистру, поэтому работает AD. Это описано в DateTimeFormatInfo.GetEra:

Название эпохи - это имя, которое календарь использует для обозначения периода времени считаются с фиксированной точки или события. Например, "A.D." или "C.E." является текущая эпоха в григорианском календаре. Сравнение с ageName без учета регистра, например, "A.D." эквивалентно "a.d.".

Графический календарь имеет только одну эпоху, поэтому Calendar.GetEra(DateTime.Now) на самом деле не требуется. Я еще не нашел никакой документации.

Вот несколько примеров, которые все работают и будут анализироваться на рождество 2017:

DateTime christmas  = DateTime.Parse("ad25ad12ad2017ad");
christmas = DateTime.Parse("AD25ad12ad2017");
christmas = DateTime.Parse("25ad12ad2017AD");
christmas = DateTime.Parse("25ad12ad2017");
christmas = DateTime.Parse("A.D.25ad12ad2017");
christmas = DateTime.Parse("A.D.25ad12ad");  // current year is used
christmas = DateTime.Parse("A.D.25ad12");    // current year is used

Ответ 2

Вы можете подтвердить, что это эпоха, а не какой-то кодированный символ UTF, изменяя название сокращенной наименования культуры (имя эпохи хранится в закрытых полях DateTimeFormatInfo.m_abbrevEraNames и DateTimeFormatInfo.m_abbrevEnglishEraNames), а для инвариантной культуры сокращенное название эпохи - это строковый массив с просто одно значение - "AD" ). Поле m_eraNames также хранит полное (не сокращенное) название эпохи ( "A.D." для инвариантной культуры), которое также может использоваться вместо "AD".

var cul = (CultureInfo) CultureInfo.InvariantCulture.Clone();                        
// set DateTimeFormatInfo.AbbreviatedEraNames to "BLA"
typeof(DateTimeFormatInfo).GetField("m_abbrevEraNames", BindingFlags.Instance | BindingFlags.NonPublic)
    .SetValue(cul.DateTimeFormat, new string[] {"BLA"});
// set DateTimeFormatInfo.AbbreviatedEnglishEraNames to "BLA"
typeof(DateTimeFormatInfo).GetField("m_abbrevEnglishEraNames", BindingFlags.Instance | BindingFlags.NonPublic)
    .SetValue(cul.DateTimeFormat, new string[] { "BLA" });

var date = DateTime.Parse("AD03AD08", cul); // now it fails
var date = DateTime.Parse("A.D.03A.D.08", cul); // still works because we
// did not modify non-abbreviated era name
var date = DateTime.Parse("BLA03BLA08", cul); // this one works

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

Этот метод пытается полностью проанализировать строку и не FormatException. Он игнорирует непризнанные данные, если это возможно, и заполняет отсутствие информации о месяце, денье и году с текущей датой

В то время как это не упоминает ничего о eras - такое поведение выравнивается с "избегать бросать FormatException, когда это возможно" дизайн.