根据“ DayOfWeek,日/月”查找实际日期

时间:2018-07-02 09:46:12

标签: c# datetime

上下文:

在奇怪的Csv导入中,我们需要一些缺少格式的日期,需要进行一些搜索。 Csv无法修复,无法对其进行控制,我们必须将其导入。

在Csv行中:

  

“ FooBarName ;; Mer 30/05; Kbr5 08h00-13h00(-00h10)Kbr5 13h15-16h55; e 07h59 S 13h00 e 13h12 s 16h02; 08h00-13h00 13h15-16h55; 6:30; 6:30 ;; 6:30 ;;;;“

我们有一个奇怪的日期格式:它看起来像是手抓的R / r格式,就像dd/MM/yyyy一样被切成12个字符。

  

“ ddd dd / MM”

我知道最后的日期是最近的日期。并被订购。

采样日期:

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 :(得分:1)

日期时间中的年份会怎样?您是否有特定的年份,或者您是否尝试查找例如“哪个年的7月1日是星期日”?如果后一种情况不可靠,例如7月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;
}

我希望这对您有帮助

答案 1 :(得分:1)

我以与Svetoslav Petrov非常相似的方式进行处理,但是我没有假设所有给定的日期都在同一年内,而是以相反的顺序并在循环的每次迭代中处理了日期字符串集确定最近的年份(a)等于或早于最近转换的日期的年份,以及(b)在一周中的给定日期有效的年份。只要在该系列的任何两个连续日期之间,或该系列的最后一个日期与当前日期之间没有多年的差距,此方法就应该有效。

var myApp = InitializeComponent();

输出:

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 的原始实现中存在一个错误:尝试在2月29日的非year年将导致构造函数抛出异常。我为for年添加了一个测试来解决此问题。如果您输入的输入在任何年(例如2月30日)都无效,那么YearIsValid仍然会抛出异常,但是在这种情况下,预期的行为是例外。

答案 2 :(得分:1)

首先,您似乎需要处理“一年中的某天”(本地化为法国文化)的概念。 “一年中的一天”的概念与年份无关,并且应具有产生所有可能的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