Определение логики даты на основе заданной строки в c#

#c#

#c#

Вопрос:

Я написал логику на c #, которая определяет nextCallDate на основе заданной cobDate. cobDate — это текущая дата -1.

Итак, если в данной строке есть более одной будущей даты, тогда она должна возвращать ближайшую будущую дату в cobdate и игнорировать остальные

Например, если cobdate равен 2020/02/12, а строка ;2;4;2;5;20180328;3;103.3750;5;20190328;3;102.250;5; 20200328;3;101.1250;5;20210328;3;100.00;

Тогда NextCallDate будет 2020/03/28 .

Мне нужно вернуть пробел для дат в прошлом. Скажем, в примере, если данная строка содержит все даты в прошлом, тогда она должна возвращать пустое значение. Заданная строка ;2;1;2;5;20120918;3;100.000000;

Вот что я написал

  private DateTime? GetNextCallDate(string nextCallDate)
        {
            DateTime cobDate = DateTime.Now.Date.AddDays(-1);
            var parts = nextCallDate.Split(';');
            

            foreach (var part in parts)
            {
                DateTime parsedNextCallDate = DateTime.Parse(part);
                if (parsedNextCallDate.Date > cobDate.Date)
                {
                    return parsedNextCallDate;
                }
            }
            return null;
        }
  

Комментарии:

1. Что возвращает ваша функция?

2. это возвращает дату

3. Что вы подразумеваете под «пустым»? Вы имеете в виду null ?

4. Вероятно, вы должны использовать DateTime.TryParse вместо Parse , поскольку некоторые значения не являются датами. Кроме того, похоже, что вы возвращаете первую дату, которая больше, чем cobDate , а не ближайшую.

5. Я имел в виду необходимость возврата null

Ответ №1:

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

Чтобы решить эту проблему, мы сначала устанавливаем parsedNextCallDate значение null , и это будет возвращаемое значение по умолчанию. Затем мы можем проверить каждую часть, является ли она a DateTime , используя возвращаемое значение from TryParse , а затем сравнить значение с обоими cobDate и parsedNextCallDate . Если дата больше cobDate и меньше parasedNextCallDate (или если parasedNextCallDate она еще не установлена), то мы обновляем parasedNextCallDate до нового значения. В конце мы просто возвращаем parasedNextCallDate :

 public static DateTime? GetNextCallDate(string input)
{
    DateTime? nextCallDate = null;
    if (string.IsNullOrWhiteSpace(input)) return nextCallDate;

    var yesterday = DateTime.Today.AddDays(-1);
    var inputItems = input.Split(';');

    foreach (var inputItem in inputItems)
    {
        DateTime itemDate;

        // If inputItem is a DateTime and it's greater than yesterday
        if (DateTime.TryParseExact(inputItem.Trim(), "yyyyMMdd", null, 
            DateTimeStyles.None, out itemDate) amp;amp;
            itemDate.Date > yesterday)
        {
            // and if nextCallDate doesn't have a value or the parsed value
            // is less than nextCallDate, assign nextCallDate to this value
            if (!nextCallDate.HasValue || itemDate < nextCallDate)
            {
                nextCallDate = itemDate;
            }
        }
    }

    return nextCallDate;
}
  

Ответ №2:

Вот один из способов решения вашей проблемы. Разбиение вещей на этапы часто упрощает рассуждения и упрощает тестирование. Я часто работаю над серверными приложениями, поэтому мне нравятся новые классы span / memory. Итак, первое, что нужно разбить нашу входную строку на куски:

     static IEnumerable<ReadOnlyMemory<char>> ReduceToPossibleDates(string source)
    {
        const int ExpectedDateLen = 9; // includes separator

        int last = 0;
        var mem = source.AsMemory();
        for (int i = 0; i < source.Length;   i)
        {
            if (';' == mem.Span[i])
            {
                int length = i - last;

                if (length == ExpectedDateLen)
                {
                    yield return mem.Slice(last 1,length-1);
                }

                last = i;
            }
        }
    }
  

Это дает нам поток ReadOnlyMemory, который содержит все, что, по нашему мнению, должно быть датами. Далее мы можем использовать другой метод для использования этих фрагментов и превращения их в даты.

     static IEnumerable<DateTime> ToDateTime(IEnumerable<ReadOnlyMemory<char>> rawDates)
    {
        foreach (var rawDate in rawDates)
        {
            if (DateTime.TryParseExact(rawDate.Span,"yyyyMMdd".AsSpan(), 
                                            CultureInfo.InvariantCulture,
                                            DateTimeStyles.None, out var date))
                yield return date;
        }
    }
  

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

     static void Main(string[] _)
    {
        const string GoodData = ";2;4;2;5;20180328;3;103.3750;5;20190328;3;102.250;" 
                                 "5;20200328;3;101.1250;5;20210328;3;100.00;";
        const string NoDateData = ";2;1;2;5;20120918;3;100.000000;";

        var cobDate = new DateTime(2020, 2,12); // some actual close of business date...

        var nextCallDate = ToDateTime(ReduceToPossibleDates(GoodData))
                                .FirstOrDefault(x => x >= cobDate);
        var noDateExpected = ToDateTime(ReduceToPossibleDates(NoDateData))
                                .FirstOrDefault(x => x >= cobDate);

        if (nextCallDate != default(DateTime))
            Console.WriteLine(nextCallDate);
        else
            Console.WriteLine("no call date.");

        if (noDateExpected != default(DateTime))
            Console.WriteLine(nextCallDate);
        else
            Console.WriteLine("no call date.");
    }
  

Это было бы немного чище с методами расширения, но вы поняли идею.