Найти реальную дату на основе "DayOfWeek, Day / Month" - PullRequest
0 голосов
/ 02 июля 2018

Контекст:

В странном импорте Csv у нас есть некоторые отформатированные даты, которые нужно искать. CSV не может быть исправлено, нет контроля над ним, мы должны импортировать его.

В строке CSV:

"FooBarName ;; Mer 30/05; Kbr5 08: 00-13: 00 (-00: 10) Kbr5 13: 15-16: 55; E: 07: 59 S: 13: 00, 13: 12: 16: 02; 08: 00-13: 00, 13: 15-16: 55; 6: 30; 6: 30; 6:30 ;;;; "

У нас есть дата в странном формате: она выглядит как R / r-формат ручной работы, который сокращается до 12 символов, как если бы это был dd/MM/yyyy.

"ддд дд / мм"

Я знаю, что последнее свидание недавно. И заказаны.

Дата выборки:

new string[] {
"Mer 15/06","Jeu 16/06","Ven 17/06","Sam 18/06","Dim 19/06","Lun 20/06","Mar 21/06",
"Jeu 23/06","Ven 24/06","Sam 25/06","Dim 26/06","Lun 27/06","Mar 28/06","Mer 29/06",
"Jeu 30/06","Ven 01/07","Sam 02/07","Dim 03/07","Lun 04/07","Mar 05/07","Mer 06/07"
}

Как мне преобразовать строку типа "ddd dd / MM" в List<DateTime>?

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

CultureInfo fr = new CultureInfo("fr-FR");

var endDate = DateTime.Now;
var startDate = new DateTime(DateTime.Now.Year-15,1,1);

var allDates = Enumerable.Range(0, 1 + endDate.Subtract(startDate).Days)
                  .Select(x => startDate.AddDays(x))
                  .Select(x => new { key = x, strDay = x.ToString("ddd"), strOther = x.ToString(" dd/MM", fr) })
                  // French format for ddd has a dot at the end. Let's remove it.               
                  .Select(x =>new { key = x.key, str = x.strDay.Remove(x.strDay.Length - 1) + x.strOther})
                  .ToArray();

foreach (var wDate in datesToFind)
{
    var results = dates.Where(x => x.str == wDate);

    // etc..
}

Ответы [ 3 ]

0 голосов
/ 02 июля 2018

Я сделал это очень похожим образом на Святослава Петрова, но вместо того, чтобы предполагать, что все данные даты попадают в один и тот же год, я работаю с набором строк дат в обратном порядке и на каждой итерации цикла. определить самый последний год (а), который равен или более ранний, чем год самой последней преобразованной даты, и (б) в котором действителен данный день недели. Этот подход должен быть действительным до тех пор, пока не существует многолетнего разрыва между любыми двумя последовательными датами в серии или между последней датой в серии и текущей датой.

void Test()
{
    var dateStrings = new string[] {
        "Mer 15/06","Jeu 16/06","Ven 17/06","Sam 18/06","Dim 19/06","Lun 20/06","Mar 21/06",
        "Jeu 23/06","Ven 24/06","Sam 25/06","Dim 26/06","Lun 27/06","Mar 28/06","Mer 29/06",
        "Jeu 30/06","Ven 01/07","Sam 02/07","Dim 03/07","Lun 04/07","Mar 05/07","Mer 06/07"
        };

    var parsedDates = ParseDateStrings(dateStrings);
    foreach (var date in parsedDates)
        Console.WriteLine(date);
}

// Takes a set of date strings in the format described by the question and returns
// the analogous set of DateTime objects. This method assumes that the supplied
// dates are in chronological order.
List<DateTime> ParseDateStrings(IEnumerable<string> dateStrings)
{
    var year = DateTime.Today.Year;
    var parsedDates = new List<DateTime>();

    // Since we can't know at first how many years are represented in the given
    // data set, we can't really make any assumptions about the year in which the
    // data begins. Instead we assume that the most recent date occurs in either
    // the current year or the latest previous year in which that date was valid,
    // and work through the set backwards.
    foreach (var dateString in dateStrings.Reverse())
    {
        var dayOfWeek = GetDayOfWeek(dateString.Substring(0, 3));
        var day = int.Parse(dateString.Substring(4, 2));
        var month = int.Parse(dateString.Substring(7, 2));
        year = GetMostRecentValidYear(year, month, day, dayOfWeek);
        parsedDates.Add(new DateTime(year, month, day));
    }

    // Reversing our output again at this point puts the results back into the
    // same order as the inputs.
    parsedDates.Reverse();
    return parsedDates;
}

// Gets the appropriate DayOfWeek value for the given three-character abbreviation.
DayOfWeek GetDayOfWeek(string abbreviation)
{
    switch (abbreviation.ToLower())
    {
        case "dim": return DayOfWeek.Sunday;
        case "lun": return DayOfWeek.Monday;
        case "mar": return DayOfWeek.Tuesday;
        case "mer": return DayOfWeek.Wednesday;
        case "jeu": return DayOfWeek.Thursday;
        case "ven": return DayOfWeek.Friday;
        case "sam": return DayOfWeek.Saturday;
        default: throw new ArgumentException();
    }
}

// Gets the latest year that is equal to or earlier than the given year, and in
// which the given day of the given month fell on the given day of the week.
int GetMostRecentValidYear(int year, int month, int day, DayOfWeek dayOfWeek)
{
    while (!YearIsValid(year, month, day, dayOfWeek))
        --year;

    return year;
}

// Returns a flag indicating whether the given day of the given month fell on the
// given day of the week in the given year.
bool YearIsValid(int year, int month, int day, DayOfWeek dayOfWeek) =>
    (month != 2 || day != 29 || IsLeapYear(year)) &&
    new DateTime(year, month, day).DayOfWeek == dayOfWeek;

// Returns a flag indicating whether the given year was a leap year.
bool IsLeapYear(int year) =>
    (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0);

Выход:

2016-06-15 00:00:00
2016-06-16 00:00:00
2016-06-17 00:00:00
2016-06-18 00:00:00
2016-06-19 00:00:00
2016-06-20 00:00:00
2016-06-21 00:00:00
2016-06-23 00:00:00
2016-06-24 00:00:00
2016-06-25 00:00:00
2016-06-26 00:00:00
2016-06-27 00:00:00
2016-06-28 00:00:00
2016-06-29 00:00:00
2016-06-30 00:00:00
2016-07-01 00:00:00
2016-07-02 00:00:00
2016-07-03 00:00:00
2016-07-04 00:00:00
2016-07-05 00:00:00
2016-07-06 00:00:00

Редактировать : Я снова посмотрел на это и обнаружил ошибку в моей первоначальной реализации YearIsValid: попытка построить DateTime для 29 февраля в не високосный год приведет к конструктор, чтобы бросить. Я добавил тест на високосные годы, чтобы обойти эту проблему. YearIsValid будет по-прежнему выдавать, если вы дадите ему ввод, который недопустим в любой год, например, 30 февраля, но в этом случае исключением является предполагаемое поведение.

0 голосов
/ 03 июля 2018

Во-первых, похоже, вам нужно разобраться с понятием «день в году» (локализовано во французской культуре). Это понятие «день в году» не зависит от года и должно иметь возможность давать все возможные DateTime, которые действительны (начиная с начального года?).

Вы можете придумать что-то подобное для реализации этой концепции:

sealed class FrenchDayInYear
{
    private readonly string _dayOfYear;
    private readonly DateTimeFormatInfo _fr;
    public FrenchDayInYear(string dayOfYear)
    {
        _dayOfYear = dayOfYear;
        _fr = new CultureInfo("fr-FR").DateTimeFormat;
        _fr.AbbreviatedDayNames = new[] { "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim" };
    }
    public IReadOnlyList<DateTime> PossibleDates(int startYear)
    {
        return Enumerable.Range(startYear, DateTime.Now.Year - startYear)
                         .Select(WithYear)
                         .OfType<DateTime>()
                         .ToList();
    }
    private DateTime? WithYear(int year)
    {
        if (DateTime.TryParseExact(_dayOfYear + year, "ddd dd/MMyyyy", _fr, DateTimeStyles.None, out var result))
        {
            return result;
        }
        else
        {
            return null;
        }
    }
}

Несколько замечаний по поводу этого кода:

  • Я должен был определить пользовательский AbbreviatedDayNames, так как стандарт ожидает точку в конце, а ваша не содержит ее
  • Я запросил startYear, поскольку вы точно не указали, в каком году следует пытаться найти возможную дату. Я также предположил, что максимальный год - это текущий год
  • Хитрость для анализа неполного DateTime без получения исключений состоит в том, чтобы отложить синтаксический анализ до тех пор, пока у вас не будет достаточно информации (в этом случае в WithYear предоставляется информация о пропущенном году)
  • OfType отфильтровывает нулевые значения
  • Я вернул IReadOnlyCollection в PossibleDates(), чтобы прояснить, что набор результатов был вычислен и завершен

Возможное использование:

        var inputs = new string[]
        {
            "Mer 15/06","Jeu 16/06","Ven 17/06","Sam 18/06","Dim 19/06","Lun 20/06","Mar 21/06",
            "Jeu 23/06","Ven 24/06","Sam 25/06","Dim 26/06","Lun 27/06","Mar 28/06","Mer 29/06",
            "Jeu 30/06","Ven 01/07","Sam 02/07","Dim 03/07","Lun 04/07","Mar 05/07","Mer 06/07"
        };
        var output = inputs.ToDictionary(input => input, input => new FrenchDayInYear(input).PossibleDates(2000));
        foreach (var kv in output)
        {
            Console.WriteLine("{0}=[{1}]", kv.Key, string.Join(",", kv.Value));
        }

В приведенном выше коде будут указаны даты 2004 и 2010 годов, двух возможных лет с 2000 года по настоящее время (2018 год), когда эти DayInYear были возможны.

0 голосов
/ 02 июля 2018

Что происходит с годом в дате и времени? У вас есть какой-либо конкретный год или вы пытаетесь найти, например, «В каком году 1 июля было воскресенье»? Если последний случай не надежен, как, например, 1 июля было в воскресенье в 2018, 2012 и т. Д. Можете ли вы сделать что-то вроде:

    var dateStrings = new string[] {
    "Mer 15/06","Jeu 16/06","Ven 17/06","Sam 18/06","Dim 19/06","Lun 20/06","Mar 21/06",
    "Jeu 23/06","Ven 24/06","Sam 25/06","Dim 26/06","Lun 27/06","Mar 28/06","Mer 29/06",
    "Jeu 30/06","Ven 01/07","Sam 02/07","Dim 03/07","Lun 04/07","Mar 05/07","Mer 06/07"
    };
var year = GetYear(dateStrings[0]);
var listDates = dateStrings
    .Select(x => {
        var input = x.Split(' ')[1].Split('/');

        return new DateTime(year, Convert.ToInt32(input[1]), Convert.ToInt32(input[0])); 
    }).ToList();

Технически, вам нужен всего один день, чтобы получить год (если вы знаете, что список дней относится к тому же году):

private int GetYear(string dateString){
    var year = DateTime.Today.Year;
    var tempString = dateString.Split(' ');
    var weekDay = tempString[0];

    var monthDate = tempString[1].Split('/');

    var month = Convert.ToInt32(monthDate[1]);
    var date = Convert.ToInt32(monthDate[0]);

    DayOfWeek dayOfWeek;

    switch (weekDay) {
        case "Lun":
            dayOfWeek = DayOfWeek.Monday;
            break;
        case "Mar":
            dayOfWeek = DayOfWeek.Tuesday;
            break;
        case "Mer":
            dayOfWeek = DayOfWeek.Wednesday;
            break;
        case "Jeu":
            dayOfWeek = DayOfWeek.Thursday;
            break;
        case "Ven":
            dayOfWeek = DayOfWeek.Friday;
            break;
        case "Sam":
            dayOfWeek = DayOfWeek.Saturday;
            break;
        default:
            dayOfWeek = DayOfWeek.Sunday;
            break;

    }

    while (year > 2003) {
        var temp = new DateTime(year, month, date); 
        if (temp.DayOfWeek == dayOfWeek) {
            return year;
        }

        year--;
    }
    return year;
}

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...