将工作日缩写字符串(包括范围)转换为列表<DayOfWeek>

Convert a string of weekday abbreviations, including ranges, into a List<DayOfWeek>

我有字符串“Mon-Thu, Sun”。

我需要把它转换成new List<DayOfWeek>{DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Sunday}

我考虑将此字符串拆分为字符串数组,然后将字符串解析为具有日期格式 "ddd" 的 DateTime。但我需要以某种方式检测“-”符号在哪里,“,”在哪里。

但是下一个代码失败了

        var str = "Mon-Thu, Sun";
        var split = str.Split(new []{',', '-'});

        foreach(var item in split){
            Console.WriteLine(item.Trim());
            var day = DateTime.ParseExact(item.Trim(), "ddd", CultureInfo.InvariantCulture);
            Console.WriteLine(day.ToShortDateString());
        }

有错误"String was not recognized as a valid DateTime because the day of week was incorrect."

Mon 不是 C# 的标准日期输入。首先,您必须根据您希望支持的所有格式,手动将其转换为 DayOfWeek 枚举中正确的等效日期值。比如 Mon 应该是 Monday 等等。一旦你有了正确的等价物,你就可以轻松地将它映射到 DayOfWeek 枚举。

这是因为当您在解析时仅指定星期几时,它默认为 DateTime.Now,因为您是 运行 程序的那一天。因此,如果您度过了与今天今天不同的一天,您会收到错误消息。你必须自己解析它,例如通过

Dictionary<string, DayOfWeek> days = new Dictionary<string, DayOfWeek>
{
    ["Mon"] = DayOfWeek.Monday,
    ["Tue"] = DayOfWeek.Tuesday,
    ["Wed"] = DayOfWeek.Wednesday,
    ["Thu"] = DayOfWeek.Thursday,
    ["Fri"] = DayOfWeek.Friday,
    ["Sat"] = DayOfWeek.Saturday,
    ["Sun"] = DayOfWeek.Sunday
};

//Get the next day in the week by calculating modulo 7
DayOfWeek NextDay(DayOfWeek day) => (DayOfWeek)(((int)day + 1) % 7);

List<DayOfWeek> GetDays(string input)
{
    var ranges = input.Split(',');
    var daysList = new List<DayOfWeek>();

    foreach(var range in ranges)
    {
        var bounds = range.Split('-').Select(s => s.Trim()).ToList();
        if(bounds.Count == 1)
        {
            if(days.TryGetValue(bounds[0], out var day))
                daysList.Add(day);
            else
                throw new FormatException("Couldn't find day");
        }
        else if(bounds.Count == 2)
        {
            if(days.TryGetValue(bounds[0], out var begin) && days.TryGetValue(bounds[1], out var end))
            {
                if(begin == NextDay(end)) // whole week in one range
                {
                    daysList.AddRange(days.Values);
                    break;
                }

                for(var i = begin; i != NextDay(end); i = NextDay(i))
                {
                    daysList.Add(i);
                }
            }
            else
                throw new FormatException("Couldn't find day");
        }
        else
            throw new FormatException("Too many hyphens in one range");
    }

    var set = new SortedSet<DayOfWeek>(daysList); //remove duplicates and sort
    return set.ToList();
}

var input = "Mon-Thu, Sun";
foreach(var day in GetDays(input))
{
    Console.WriteLine(day);
}

编辑:添加答案:)

以下代码适用于您提到的格式。

Input : "Mon-Thu, Sun" 
OutPut: Monday, Tuesday, Wednesday, Thursday, Sunday

Input : "Mon, Wed-Thu, Sun" 
OutPut: Monday, Wednesday, Thursday, Sunday

List<DayOfWeek> ListOfDays()
{
    var str = "Mon-Thu, Sun";
    string[] split = str.Split(',');

    var days = new List<DayOfWeek>();   
    foreach (var item in split)
    {
        if (item.IndexOf('-') < 0)
        {
            days.Add(GetDayOfWeek(item.Trim()));
            continue;
        }

        var consecutiveDays = item.Split('-');
        DayOfWeek startDay = GetDayOfWeek(consecutiveDays[0].Trim());
        DayOfWeek endDay = GetDayOfWeek(consecutiveDays[1].Trim());

        for (DayOfWeek day = startDay; day <= endDay; day++)
            days.Add(day);
    }

    return days;
}

DayOfWeek GetDayOfWeek(string day)
{
    switch (day.ToUpper())
    {
        case "MON":
            return DayOfWeek.Monday;
            break;
        case "TUE":
            return DayOfWeek.Tuesday;
            break;
        case "WED":
            return DayOfWeek.Wednesday;
            break;
        case "THU":
            return DayOfWeek.Thursday;
            break;
        case "FRI":
            return DayOfWeek.Friday;
            break;
        case "SAT":
            return DayOfWeek.Saturday;
            break;
        case "SUN":
            return DayOfWeek.Sunday;
            break;
        default:
            throw new ArgumentException("Invalid day");
            break;
    }
}

一种方法是首先将字符串拆分为 "chunks",我将其定义为一天或多天的范围,以逗号分隔。然后,对于每个块,获取开始日期,将其添加到列表中,然后递增直到我们到达结束日期。

我们可以编写代码来增加天数,这样它们将 "wrap around" 一周。例如,如果我们要表示我们将从 "Fri-Mon" 开始休假的时间,那么这些天将是星期五、星期六、星期日和星期一。仅仅递增将以无效值结束,因为星期日是 0.

我们可以使用Enum.GetValues结合System.LinqCast的方法得到星期几的字符串值,然后比较一下星期几开始根据我们的意见。

static void Main(string[] args)
{
    var input = "Fri-Thu, Sun";
    var consecutiveChunks = input.Split(new[] { ',' }, 
        StringSplitOptions.RemoveEmptyEntries);

    var output = new List<DayOfWeek>();
    var daysOfWeek = Enum.GetValues(typeof(DayOfWeek)).Cast<DayOfWeek>();

    foreach (var chunk in consecutiveChunks)
    {
        var chunkRange = chunk.Split('-').Select(i => i.Trim()).ToList();

        DayOfWeek currentDay = daysOfWeek
            .First(d => d.ToString().StartsWith(chunkRange[0]));

        DayOfWeek lastDay = chunkRange.Count > 1
            ? daysOfWeek.First(d => d.ToString().StartsWith(chunkRange[1])) 
            : currentDay;

        output.Add(currentDay);

        // If we have a range of days, add the rest of them
        while (currentDay != lastDay)
        {
            // Increment to the next day
            if (currentDay == DayOfWeek.Saturday)
            {
                currentDay = DayOfWeek.Sunday;
            }
            else
            {
                currentDay++;
            }

            output.Add(currentDay);
        }
    }

    // Output our results:
    Console.WriteLine($"The ranges, \"{input}\" resolve to:");
    output.ForEach(i => Console.WriteLine(i.ToString()));

    Console.Write("\nDone!\nPress any key to exit...");
    Console.ReadKey();
}

输出

事实证明,C# 库确实维护了一个日期缩写列表,如果您不喜欢它们,您甚至可以更改它们。具体来说,我指的是 CultureInfo.[culture].DateTimeFormat.AbbreviatedDayNames.

InvariantCulture 对星期一、星期四和星期日使用的缩写与您在问题中列出的相同。

给定日期名称的缩写,您应该能够导出 AbbreviatedDayNames 数组中缩写名称的索引,该索引与 DayOfWeek.[=16= 使用的索引相匹配]

对我来说,这种方法似乎比将文字字符串嵌入代码要好。

public static void Main()
{
    var dayList = new List<DayOfWeek>();
    var str = "Mon-Thu, Sun";

    str = str.Replace(" ", string.Empty);   // remove spaces

    // split groups by comma
    var split = str.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

    foreach (var item in split) // process each group
    {
        // split ranges by hyphen
        var elements = item.Split(new[] {'-'}, StringSplitOptions.RemoveEmptyEntries);  // split group into elements
        switch (elements.Length)
        {
            case 1:
                // add single element
                dayList.Add((DayOfWeek) GetDayIndex(elements[0]));
                break;
            case 2:
                // add range of elements
                dayList.AddRange(GetDayRange(elements[0], elements[1]));
                break;
            default:
                Console.WriteLine($"Input line does not match required format: \"{str}\"");
                break;
        }
    }
    // prove it works
    Console.WriteLine(string.Join(", ", dayList));
}

private static int GetDayIndex(string dayNameAbbreviation)
{
    return Array.IndexOf(CultureInfo.InvariantCulture.DateTimeFormat.AbbreviatedDayNames, dayNameAbbreviation);
}

private static IEnumerable<DayOfWeek> GetDayRange(string beginDayNameAbbrev, string endDayNameAbbrev)
{
    var dayRange = new List<DayOfWeek>();

    for (var i = GetDayIndex(beginDayNameAbbrev); i <= GetDayIndex(endDayNameAbbrev); i++)
    {
        dayRange.Add((DayOfWeek) i);
    }
    return dayRange;
}

编辑

如上所述,如果您不喜欢特定文化使用的日期缩写,您可以暂时更改它们。要了解如何操作,请查看这个 Stack Overflow 问题:How to change DateTimeFormatInfo.CurrentInfo AbbreviatedDayNames collection.