Греческий месяц без знаков препинания преобразовать в дату с помощью Date.ParseExact - PullRequest
1 голос
/ 20 июня 2020

У меня есть греческая дата "08 Ιουνιου 2021", которую я хочу преобразовать в Date. Это невозможно, потому что в месяце отсутствуют некоторые знаки препинания. Правильный месяц - "Ιουνίου", который на самом деле работает правильно с помощью кода ниже

var greek = new CultureInfo("el-GR").DateTimeFormat;
var dtFormat = DateTime.ParseExact("08 Ιουνίου 2021", "dd' 'MMMM' 'yyyy", greek, DateTimeStyles.None);

Есть идеи, как правильно отформатировать месяц?

Ответы [ 2 ]

1 голос
/ 21 июня 2020

Вы можете попытаться нормализовать название месяца, используя диакритическое c нечувствительное сравнение, чтобы получить правильное название месяца из коллекции CultureInfo.DateTimeFormat.MonthGenitiveNames , поскольку в некоторых культурах родительное имя отличается от именительного падежа (дата представлена ​​в форме: 8th of June, 2021).

A String.Compare () перегрузка принимает как аргументы CultureInfo, так и CompareOptions . Флаг IgnoreNonSpace: Указывает, что при сравнении строк должны игнорироваться непробельные объединяющие символы, например диакритические знаки. [...] . Это позволяет искать название месяца, игнорируя отсутствующие диакритические знаки, в коллекциях MonthGenitiveNames и возвращать соответствующее собственное имя.

Затем нормализованная дата может быть передана в DateTime.TryParse () вместе с предоставленной CultureInfo для извлечения объекта DateTime.

Пример вызова метода:

var normalizedDateTime = NormalizeMonthDiacritics(new CultureInfo("el-GR"), "08 Ιουνιου 2021");
using System.Globalization;
using System.Linq;

internal DateTime NormalizeMonthDiacritics(CultureInfo culture, string date)
{
    string[] inputDateParts = date.Split();

    inputDateParts[1] = culture.DateTimeFormat.MonthGenitiveNames.FirstOrDefault(month => 
        string.Compare(inputDateParts[1], month, culture, CompareOptions.IgnoreNonSpace) == 0) 
        ?? inputDateParts[1];

    string normalizedDate = string.Join(" ", inputDateParts);

    if (DateTime.TryParse(normalizedDate, culture, DateTimeStyles.None, out DateTime dtm)) {
        return dtm;
    }
    else {
        throw new ArgumentException("The provided date cannot be normalized", 
              new Exception("Month Genitive form not available"));
    }
}

Поскольку по крайней мере 37 культур используют имена месяцев, состоящие из 2 или более частей, чтобы сделать метод более универсальным c, а также применить ту же процедуру к сокращенным родительным падежам именам, этот метод может быть изменен, чтобы анализировать дату ввода немного иначе, а также позволяет указать, предоставляется ли название месяца в коротком формате.

Формат даты может состоять из этих 3 частей:

  1. Numeri c значение, представляющее либо день месяца, либо год
  2. часть месяца, родительное имя, либо в длинной строке формата (MMMM), либо в коротком формате (MMM)
  3. Numeri c значение, представляющее год или день месяца

DateTime.TryParse() может обрабатывать формат DateTime обоими способами.

Вызовите этот измененный метод следующим образом :

var culture = new CultureInfo("sah-RU");
string date = "2020 атырдьах ыиын 08";  // <= Should be атырдьах ыйын
var normalizedDateTime = NormalizeMonthDiacritics(culture, date, false);

Модифицированный метод:

using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;

internal DateTime NormalizeMonthDiacritics(CultureInfo culture, string date, bool monthShortForm = false)
{
    string pattern = @"(\d+)\s+(.*?)\s+(\d+)";
    var parts = Regex.Match(date, pattern, RegexOptions.CultureInvariant | RegexOptions.Singleline)
                     .Groups.OfType<Capture>().Skip(1).Take(3).Select(c => c.Value).ToArray();

    var monthNames = monthShortForm 
                   ? culture.DateTimeFormat.AbbreviatedMonthGenitiveNames 
                   : culture.DateTimeFormat.MonthGenitiveNames;

    parts[1] = monthNames.FirstOrDefault(month =>
        string.Compare(parts[1], month, culture, CompareOptions.IgnoreNonSpace) == 0)
        ?? parts[1];

    string normalizedDate = string.Join(" ", parts);

    if (DateTime.TryParse(normalizedDate, culture, DateTimeStyles.None, out DateTime dtm)) {
        return dtm;
    }
    else {
        throw new ArgumentException("The provided date cannnot be normalized",
              new Exception("Month Genitive form not available"));
    }
}
0 голосов
/ 20 июня 2020

Я не знаю точно, в чем заключается проблема - берутся ли значения из базы данных, или это всего лишь единичный случай, или требуется больше, чем было установлено ранее -

Идея состоит в том, чтобы создать сопоставление диакритических слов (в данном случае месяцев) и недиакритических знаков.

Когда передается значение даты 08 Ιουνιου 2021, мы сравниваем его с сопоставлением месяцев, которое содержит диакритические знаки.

сравнение выполняется с помощью

string.Compare(firstString, secondString, new CultureInfo("el-GR"), CompareOptions.IgnoreNonSpace);

Из документации Msdn - IgnoreNonSpace :

Указывает, что при сравнении строк должны игнорироваться объединяющиеся символы без пробелов, такие как диакритические знаки. Стандарт Unicode определяет комбинирование символов как символов, которые комбинируются с базовыми символами для создания нового символа. Комбинируемые символы без интервала сами по себе не занимают позицию интервала при рендеринге.

Это могло / могло бы быть потенциальным исправлением:

    static string originalValue = "08 Ιουνιου 2021";
    static string[] currentMonth = originalValue.Split(' ');

    static List<string> monthMapping = new List<string>
        {
            //add others months mapping - I dont know how they are spelt -
            "Ιουνίου"
        };

    static void Main(string[] args)
    {
        DateTimeFormatInfo greek = new CultureInfo("el-GR").DateTimeFormat;

        foreach (string month in monthMapping)
        {
            if( string.Compare(currentMonth[1], month, new CultureInfo("el-GR") , CompareOptions.IgnoreNonSpace) == 0)
            {
                var dtFormat = DateTime.ParseExact($"08 {month} 2021", "dd' 'MMMM' 'yyyy", greek, DateTimeStyles.None);
            }

        }
    }
...