String to DateTime, TryParse было недостаточно - PullRequest
0 голосов
/ 17 июня 2020

Вот метод, который работает:

        /// <summary>
        /// Checks date validity.
        /// </summary>
        /// <param name="candidate">String to check</param>
        /// <returns>Whether valid and result as a date</returns>
        private static Tuple<bool, DateTime?> ParseDate(string candidate)
        {
            DateTime result;
            if(DateTime.TryParseExact(candidate, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd.M.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "d.M.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd/M/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "d/M/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd.MM.yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd.M.yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd.MM.yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "d.M.yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd/MM/yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd/M/yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "dd/MM/yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result) ||
                DateTime.TryParseExact(candidate, "d/M/yy", CultureInfo.InvariantCulture, DateTimeStyles.None, out result)
            )
            {
                return new Tuple<bool, DateTime?>(true, result);
            }

            return new Tuple<bool, DateTime?>(false, null);
        }

Мне кажется довольно бесполезным добавлять туда тонны or-веток. Что было бы более разумным способом сделать это? Я думал, что DateTime.TryParse сделает это, но ему удалось каким-то образом выйти из строя с 15.6.2020 (в прошлый понедельник) и вернуть, что это недействительная дата.

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

Ответы [ 3 ]

2 голосов
/ 17 июня 2020

Существует множество форматов даты, некоторые из которых настраиваются, поэтому вам нужно указать, какой допустимый формат вы примете. Будет полезно, если вы сможете проверить формат или ограничить принятый формат в пользовательском интерфейсе (если дата получена из какого-либо ручного ввода). В DateTime.TryParseExact есть перегрузка, которая принимает массив строковых форматов, см. здесь

0 голосов
/ 17 июня 2020

Сохраняя тот же лог c, я написал это:

public static bool TryParseDateTimeManyFormats(string toParse, out DateTime result)
{
    result = default;
    foreach (var format in DateFormats)
    {
        if (DateTime.TryParseExact(toParse, format, CultureInfo.InvariantCulture, DateTimeStyles.None,
            out result))
        {
            return true;
        }
    }

    return false;
}

в сопровождении вашего списка форматов даты. Затем я продолжал устранять их, пока не получил минимальный набор:

private static readonly string[] DateFormats =
{
    //"dd.MM.yyyy",
    //"dd.M.yyyy",
    //"dd.MM.yyyy",
    "d.M.yyyy",
    //"dd/MM/yyyy",
    //"dd/M/yyyy",
    //"dd/MM/yyyy",
    "d/M/yyyy",
    //"dd.MM.yy",
    //"dd.M.yy",
    //"dd.MM.yy",
    "d.M.yy",
    //"dd/MM/yy",
    //"dd/M/yy",
    //"dd/MM/yy",
    "d/M/yy",
};

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

public static void TestDateParsing()
{
    string[] dates =
    {
        "31.1.1919",
        "22/01/1957",
        "1/2/2020",
        "03/04/1812",
        "1/2/03",
        "04/05/06",
        "30/2/2010",
        "15.6.2020",
    };

    foreach (var date in dates)
    {
        if (TryParseDateTimeManyFormats(date, out var result))
        {
            Debug.WriteLine(($"[{date}] correctly parsed as {result}"));
        }
        else
        {
            Debug.WriteLine($"Parsing [{date}] failed");
        }
    }
}

и получил этот результат (я в США, поэтому по умолчанию мы используем безумный формат месяц-день-год):

[31.1.1919] correctly parsed as 1/31/1919 12:00:00 AM
[22/01/1957] correctly parsed as 1/22/1957 12:00:00 AM
[1/2/2020] correctly parsed as 2/1/2020 12:00:00 AM
[03/04/1812] correctly parsed as 4/3/1812 12:00:00 AM
[1/2/03] correctly parsed as 2/1/2003 12:00:00 AM
[04/05/06] correctly parsed as 5/4/2006 12:00:00 AM
[15.6.2020] correctly parsed as 6/15/2020 12:00:00 AM
Parsing [30/2/2010] failed

Это кажется немного менее безумным.

0 голосов
/ 17 июня 2020

Используйте регулярное выражение для сопоставления формата строки. Что-то вроде

public bool IsStringDate(string candidate){

    var dateRegex = new Regex("^([0]\d|[1][0-2])\/([0-2]\d|[3][0-1])\/([2][01]|[1][6-9])\d{2}(\s([0-1]\d|[2][0-3])(\:[0-5]\d){1,2})?$");

    return dateRegex.Match(candidate).Success;
}
...