2

I have string "Mon-Thu, Sun".

I need to convert it into new List<DayOfWeek>{DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Sunday}

I think about spliting this string into string array and then parse strings into DateTime with dateformat "ddd". But I need to detect somehow where is "-" symbol and where is ",".

But next code fails

        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());
        }

With error "String was not recognized as a valid DateTime because the day of week was incorrect."

STLDev
  • 5,950
  • 25
  • 36
demo
  • 6,038
  • 19
  • 75
  • 149

5 Answers5

3

It turns out that C# libraries do indeed maintain a list of day abbreviations, and if you don't like them, you can even change them. Specifically, I'm referring to CultureInfo.[culture].DateTimeFormat.AbbreviatedDayNames.

The InvariantCulture uses the same abbreviations for Monday, Thursday, and Sunday as you've listed in your question.

Given an abbreviation for a day name, you should be able to derive the index of the abbreviated name in the AbbreviatedDayNames array, which matches the index used by DayOfWeek.

To me, this approach seems superior than embedding literal strings into your code.

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;
}

EDIT

As stated above, if you don't like the day abbreviations used by a particular culture, you can temporarily change them. To see how, have a look at this Stack Overflow question: How to change DateTimeFormatInfo.CurrentInfo AbbreviatedDayNames collection.

STLDev
  • 5,950
  • 25
  • 36
  • @RufusL of course! That's what I get for being in a hurry! Thank you for the suggestion, I'll edit the answer. – STLDev Aug 09 '17 at 09:26
1

The following code works for the format you mentioned.

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;
    }
}
Praveen Reddy
  • 7,295
  • 2
  • 21
  • 43
1

One way to do this would be to first split the string into "chunks", which I'm defining as a range of one or more days, which are separated by the comma character. Then, for each chunk, grab the start day, add it to the list, and then increment it until we get to the end day.

We can write the code to the increment the days such that they will "wrap around" the week. For example, if we were representing some vacation time that we were going to take from "Fri-Mon", the days would be Friday, Saturday, Sunday, and Monday. Just incrementing alone would end up with an invalid value, since Sunday is 0.

We can use Enum.GetValues combined with the System.Linq Cast method to get the string values of the days of week, and then just compare to find which day of the week starts with our input.

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();
}

Output

enter image description here

Rufus L
  • 36,127
  • 5
  • 30
  • 43
-1

This is because when you specify only day of week when parsing, it defaults to DateTime.Now as in the day you are running the program. So if you pass a day different than what it is today you get an error. You would have to parse it yourself e.g. by doing

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);
}

EDIT: added answer :)

Jakub Dąbek
  • 1,044
  • 1
  • 8
  • 17
  • I didn't downvote your question, but it doesn't answer the OPs question, which is probably why it was downvoted. – STLDev Aug 09 '17 at 00:28
-3

Mon is not standard day input for C#. First you will have to convert it to correct equivalent day value in DayOfWeek enum manually based on what all formats you would like to support. Like Mon should be Monday etc. Once you have correct equivalent, you can easily map it to the DayOfWeek Enum.

PM.
  • 1,735
  • 1
  • 30
  • 38
  • 1
    I think that must be the reason, under the category `No code` @ I downvote because website. Though I am wondering if same things applied to the answers as well. Some times just pointing towards solution should be enough. Anyways, I will avoid posting questions/answers which won't help anybody. Thanks... – PM. Aug 18 '17 at 06:35