65.9K
CodeProject is changing. Read more.
Home

Converting Days of the Week to and from a BitFlag

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1 vote)

Oct 7, 2011

CPOL
viewsIcon

14929

A utility class that lets you store a set of days of the week in a single int value

I thought I'd share this bit of code with you. It's basically a way of converting the System.DayOfWeek enum into a bitfield enum. Because I'm working on a transit database, I need a column that lets me store specific combinations of days, like Monday to Friday, or Saturday and Sunday. I'd like to translate those combinations into ints and back into combinations. So here's the utility class to do it:

public static class DaysOfWeekUtils
{
    #region Nested Enums
    [Flags]
    private enum DayOfWeekBitFlag
    {
        Sunday = 0x1,
        Monday = 0x2,
        Tuesday = 0x4,
        Wednesday = 0x8,
        Thursday = 0x10,
        Friday = 0x20,
        Saturday = 0x40
    }
    #endregion

    #region Private Fields
    private static Dictionary<DayOfWeekBitFlag, DayOfWeek> systemDayOfWeek;
    private static Dictionary<DayOfWeek, DayOfWeekBitFlag> bitFlagDayOfWeek;
    #endregion

    #region Constructors
    static DaysOfWeekUtils()
    {
        systemDayOfWeek = new Dictionary<DayOfWeekBitFlag, DayOfWeek>();
        systemDayOfWeek[DayOfWeekBitFlag.Sunday] = DayOfWeek.Sunday;
        systemDayOfWeek[DayOfWeekBitFlag.Monday] = DayOfWeek.Monday;
        systemDayOfWeek[DayOfWeekBitFlag.Tuesday] = DayOfWeek.Tuesday;
        systemDayOfWeek[DayOfWeekBitFlag.Wednesday] = DayOfWeek.Wednesday;
        systemDayOfWeek[DayOfWeekBitFlag.Thursday] = DayOfWeek.Thursday;
        systemDayOfWeek[DayOfWeekBitFlag.Friday] = DayOfWeek.Friday;
        systemDayOfWeek[DayOfWeekBitFlag.Saturday] = DayOfWeek.Saturday;

        bitFlagDayOfWeek = new Dictionary<DayOfWeek, DayOfWeekBitFlag>();
        bitFlagDayOfWeek[DayOfWeek.Sunday] = DayOfWeekBitFlag.Sunday;
        bitFlagDayOfWeek[DayOfWeek.Monday] = DayOfWeekBitFlag.Monday;
        bitFlagDayOfWeek[DayOfWeek.Tuesday] = DayOfWeekBitFlag.Tuesday;
        bitFlagDayOfWeek[DayOfWeek.Wednesday] = DayOfWeekBitFlag.Wednesday;
        bitFlagDayOfWeek[DayOfWeek.Thursday] = DayOfWeekBitFlag.Thursday;
        bitFlagDayOfWeek[DayOfWeek.Friday] = DayOfWeekBitFlag.Friday;
        bitFlagDayOfWeek[DayOfWeek.Saturday] = DayOfWeekBitFlag.Saturday;

        MondayToFridayCode = valueOf(DayOfWeek.Monday, DayOfWeek.Tuesday, 
            DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday);
    }
    #endregion

    #region Public Properties
    public static int MondayToFridayCode { get; private set; }

    public static IEnumerable<DayOfWeek> EachDayOfTheWeek
    {
        get
        {
            yield return DayOfWeek.Sunday;
            yield return DayOfWeek.Monday;
            yield return DayOfWeek.Tuesday;
            yield return DayOfWeek.Wednesday;
            yield return DayOfWeek.Thursday;
            yield return DayOfWeek.Friday;
            yield return DayOfWeek.Saturday;
        }
    }

    public static IEnumerable<DayOfWeek> MondayToFriday
    {
        get
        {
            return daysOfWeek(MondayToFridayCode);
        }
    }
    #endregion

    #region Public Methods
    public static IEnumerable<DayOfWeek> daysOfWeek(int val)
    {
        foreach (DayOfWeekBitFlag bitFlag in systemDayOfWeek.Keys)
            if ((val & (int)bitFlag) == (int)bitFlag)
                yield return systemDayOfWeek[bitFlag];
    }

    public static int valueOf(params DayOfWeek[] daysOfWeek)
    {
        return daysOfWeek.Distinct().Sum(dayOfWeek => (int)bitFlagDayOfWeek[dayOfWeek]);
    }
    #endregion
}

and here is its test class:

[TestClass]
public class DayOfWeekUtilsUnitTests
{
    #region Test Methods
    [TestMethod]
    public void testDaysOfTheWeek()
    {
        // Not doing this in a nested loop just
        // in order to see the structure clearly.
        foreach (DayOfWeek[] combination in combinations(7))
            testDaysOfWeek(combination);

        foreach (DayOfWeek[] combination in combinations(6))
            testDaysOfWeek(combination);

        foreach (DayOfWeek[] combination in combinations(5))
            testDaysOfWeek(combination);

        foreach (DayOfWeek[] combination in combinations(4))
            testDaysOfWeek(combination);

        foreach (DayOfWeek[] combination in combinations(3))
            testDaysOfWeek(combination);

        foreach (DayOfWeek[] combination in combinations(2))
            testDaysOfWeek(combination);

        foreach (DayOfWeek[] combination in combinations(1))
            testDaysOfWeek(combination);
    }

    private IEnumerable<DayOfWeek[]> combinations(int arrayLength)
    {
        if (arrayLength == 0)
            yield return new DayOfWeek[0];
        else
            foreach (DayOfWeek dayOfWeek in DaysOfWeekUtils.EachDayOfTheWeek)
                foreach (DayOfWeek[] combination in combinations(arrayLength - 1))
                    yield return combination.Concat(Enumerable.Repeat(dayOfWeek, 1)).ToArray();
    }

    [TestMethod]
    public void testDaysOfTheWeekUtilsProperty()
    {
        IEnumerable<DayOfWeek> daysOfTheWeek = DaysOfWeekUtils.EachDayOfTheWeek;
        Assert.AreEqual(daysOfTheWeek.Count(), 7);
        Assert.IsTrue(daysOfTheWeek.Contains(DayOfWeek.Sunday));
        Assert.IsTrue(daysOfTheWeek.Contains(DayOfWeek.Monday));
        Assert.IsTrue(daysOfTheWeek.Contains(DayOfWeek.Tuesday));
        Assert.IsTrue(daysOfTheWeek.Contains(DayOfWeek.Wednesday));
        Assert.IsTrue(daysOfTheWeek.Contains(DayOfWeek.Thursday));
        Assert.IsTrue(daysOfTheWeek.Contains(DayOfWeek.Friday));
        Assert.IsTrue(daysOfTheWeek.Contains(DayOfWeek.Saturday));
    }

    [TestMethod]
    public void testMondayToFriday()
    {
        IEnumerable<DayOfWeek> mondayToFriday = DaysOfWeekUtils.MondayToFriday;
        Assert.IsTrue(mondayToFriday.Count() == 5);

        Assert.IsTrue(mondayToFriday.Contains(DayOfWeek.Monday));
        Assert.IsTrue(mondayToFriday.Contains(DayOfWeek.Tuesday));
        Assert.IsTrue(mondayToFriday.Contains(DayOfWeek.Wednesday));
        Assert.IsTrue(mondayToFriday.Contains(DayOfWeek.Thursday));
        Assert.IsTrue(mondayToFriday.Contains(DayOfWeek.Friday));
    }

    [TestMethod]
    public void testNonExistentDays()
    {
        IEnumerable<DayOfWeek> noDays = DaysOfWeekUtils.daysOfWeek(256);
        Assert.AreEqual(noDays.Count(), 0);
    }
    #endregion

    #region Private Methods
    private void testDaysOfWeek(params DayOfWeek[] daysOfWeek)
    {
        int val = DaysOfWeekUtils.valueOf(daysOfWeek);
        IEnumerable<DayOfWeek> computedEnumeration = DaysOfWeekUtils.daysOfWeek(val);
        Assert.AreEqual(daysOfWeek.Distinct().Count(), computedEnumeration.Count());
        foreach (DayOfWeek dayOfWeek in computedEnumeration)
            Assert.IsTrue(daysOfWeek.Contains(dayOfWeek));
    }
    #endregion
}

To convert an int into an IEnumerable<DayOfWeek>, use the daysOfWeek() method; to go the other way, use the valueOf() method. I've included ModayToFriday and ModayToFridayCode shortcuts; feel free to add your own as and when the need arises.

Enjoy!