Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get every monday of given month?

Tags:

c#

datetime

How to get every "monday" day of given month ?

An example;
Input: 11.July.2017 (11.07.2017)
Output: ( 3,10,17,24,31 )
3.7.2017 Monday
10.7.2017 Monday
17.7.2017 Monday
24.7.2017 Monday
31.7.2017

I could get number of days of given month(For july 2017, it's 31 days). Then write an iteration(for loop a.e.) if dayOfWeek equals Monday, add to a list. But this is not good code because for loop will work 31 times. There should be a better algorithm to archive the goal.

I'm using C# .net framework 4.6

UPDATE
Thanks for all for your helps, after some answers I've got so far; I tested all codes with a simple & dirty benchmark codes to find faster algorithm.

Here is my benchmark code;

using System;
using System.Collections.Generic;
using System.Linq;

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Engines;

using X.Core.Helpers;

namespace X.ConsoleBenchmark
{
    [SimpleJob(RunStrategy.ColdStart, targetCount: 5)]
    [MinColumn, MaxColumn, MeanColumn, MedianColumn]
    public class LoopTest
    {
        [Benchmark]
        public void CalculateNextSalaryDateWithLoopAllDays()
        {
            DateTime date = new DateTime(2017, 7, 3);
            const int oneMillion = 1000000;
            for (int i = 0; i < oneMillion; i++)
            {
                List<DateTime> allXDaysInMonth = date.GetAllXDaysInMonthWithLoopAllDays(DayOfWeek.Tuesday);
                if (allXDaysInMonth != null && allXDaysInMonth.FirstOrDefault().Day != 4)
                {
                    throw new ApplicationException("Calculate method has errors.");
                }
            }
        }

        [Benchmark]
        public void CalculateNextSalaryDate()
        {
            DateTime date = new DateTime(2017, 7, 3);
            const int oneMillion = 1000000;
            for (int i = 0; i < oneMillion; i++)
            {
                List<DateTime> allXDaysInMonth = date.GetAllXDaysInMonth(DayOfWeek.Tuesday);
                if (allXDaysInMonth != null && allXDaysInMonth.FirstOrDefault().Day != 4)
                {
                    throw new ApplicationException("Calculate method has errors.");
                }
            }
        }

        [Benchmark]
        public void Maccettura_GetAllDayOfWeekPerMonth()
        {
            DateTime exampleDate = new DateTime(2017, 7, 3);
            const int oneMillion = 1000000;
            for (int i = 0; i < oneMillion; i++)
            {
                var date = new DateTime(exampleDate.Year, exampleDate.Month, 1);

                if (date.DayOfWeek != DayOfWeek.Thursday)
                {
                    int daysUntilDayOfWeek = ((int)DayOfWeek.Thursday - (int)date.DayOfWeek + 7) % 7;
                    date = date.AddDays(daysUntilDayOfWeek);
                }

                List<DateTime> days = new List<DateTime>();

                while (date.Month == exampleDate.Month)
                {
                    days.Add(date);
                    date = date.AddDays(7);
                }

                if (days.FirstOrDefault().Day != 6)
                {
                    throw new ApplicationException("Calculate method has errors.");
                }
            }
        }

        [Benchmark]
        public void ScottHannen_GetWeekdaysForMonth()
        {
            DateTime exampleDate = new DateTime(2017, 7, 3);
            const int oneMillion = 1000000;
            for (int i = 0; i < oneMillion; i++)
            {
                IEnumerable<DateTime> days = ScottHannen_GetDaysInMonth(exampleDate).Where(day => day.DayOfWeek == DayOfWeek.Thursday);

                if (days.FirstOrDefault().Day != 6)
                {
                    throw new ApplicationException("Calculate method has errors.");
                }
            }
        }

        private IEnumerable<DateTime> ScottHannen_GetDaysInMonth(DateTime date)
        {
            var dateLoop = new DateTime(date.Year, date.Month, 1);

            while (dateLoop.Month == date.Month)
            {
                yield return dateLoop;
                dateLoop = dateLoop.AddDays(1);
            }
        }

        [Benchmark]
        public void Trioj_GetWeekdaysForMonth()
        {
            DateTime exampleDate = new DateTime(2017, 7, 3);
            const int oneMillion = 1000000;
            for (int i = 0; i < oneMillion; i++)
            {
                IEnumerable<DateTime> days = Trioj_GetDatesInMonthByWeekday(exampleDate, DayOfWeek.Thursday);

                if (days.FirstOrDefault().Day != 6)
                {
                    throw new ApplicationException("Calculate method has errors.");
                }
            }
        }

        private List<DateTime> Trioj_GetDatesInMonthByWeekday(DateTime date, DayOfWeek dayOfWeek)
        {
            // We know the first of the month falls on, well, the first.
            var first = new DateTime(date.Year, date.Month, 1);
            int daysInMonth = DateTime.DaysInMonth(date.Year, date.Month);

            // Find the first day of the week that matches the requested day of week.
            if (first.DayOfWeek != dayOfWeek)
            {
                first = first.AddDays(((((int)dayOfWeek - (int)first.DayOfWeek) + 7) % 7));
            }

            // A weekday in a 31 day month will only occur five times if it is one of the first three weekdays.
            // A weekday in a 30 day month will only occur five times if it is one of the first two weekdays.
            // A weekday in February will only occur five times if it is the first weekday and it is a leap year.
            // Incidentally, this means that if we subtract the day of the first occurrence of our weekday from the 
            // days in month, then if that results in an integer greater than 27, there will be 5 occurrences.
            int maxOccurrences = (daysInMonth - first.Day) > 27 ? 5 : 4;
            var list = new List<DateTime>(maxOccurrences);

            for (int i = 0; i < maxOccurrences; i++)
            {
                list.Add(new DateTime(first.Year, first.Month, (first.Day + (7 * i))));
            }

            return list;
        }

        [Benchmark]
        public void Jonathan_GetWeekdaysForMonth()
        {
            DateTime exampleDate = new DateTime(2017, 7, 3);
            const int oneMillion = 1000000;
            for (int i = 0; i < oneMillion; i++)
            {
                IEnumerable<DateTime> days = Jonathan_AllDatesInMonth(exampleDate.Year, exampleDate.Month).Where(x => x.DayOfWeek == DayOfWeek.Thursday);

                if (days.FirstOrDefault().Day != 6)
                {
                    throw new ApplicationException("Calculate method has errors.");
                }
            }
        }

        private static IEnumerable<DateTime> Jonathan_AllDatesInMonth(int year, int month)
        {
            int days = DateTime.DaysInMonth(year, month);
            for (int day = 1; day <= days; day++)
            {
                yield return new DateTime(year, month, day);
            }
        }

        [Benchmark]
        public void Swatsonpicken_GetWeekdaysForMonth()
        {
            DateTime exampleDate = new DateTime(2017, 7, 3);
            const int oneMillion = 1000000;
            for (int i = 0; i < oneMillion; i++)
            {
                IEnumerable<DateTime> days = Swatsonpicken_GetDaysOfWeek(exampleDate, DayOfWeek.Thursday);

                if (days.FirstOrDefault().Day != 6)
                {
                    throw new ApplicationException("Calculate method has errors.");
                }
            }
        }

        private static IEnumerable<DateTime> Swatsonpicken_GetDaysOfWeek(DateTime startDate, DayOfWeek desiredDayOfWeek)
        {
            var daysOfWeek = new List<DateTime>();
            var workingDate = new DateTime(startDate.Year, startDate.Month, 1);
            var offset = ((int)desiredDayOfWeek - (int)workingDate.DayOfWeek + 7) % 7;

            // Jump to the first desired day of week.
            workingDate = workingDate.AddDays(offset);

            do
            {
                daysOfWeek.Add(workingDate);

                // Jump forward seven days to get the next desired day of week.
                workingDate = workingDate.AddDays(7);
            } while (workingDate.Month == startDate.Month);

            return daysOfWeek;
        }

        [Benchmark]
        public void AliaksandrHmyrak_GetWeekdaysForMonth()
        {
            DateTime exampleDate = new DateTime(2017, 7, 3);
            const int oneMillion = 1000000;
            for (int i = 0; i < oneMillion; i++)
            {
                IEnumerable<DateTime> days = AliaksandrHmyrak_GetDaysOfWeek(exampleDate, DayOfWeek.Thursday);

                if (days.FirstOrDefault().Day != 6)
                {
                    throw new ApplicationException("Calculate method has errors.");
                }
            }
        }

        private static List<DateTime> AliaksandrHmyrak_GetDaysOfWeek(DateTime date, DayOfWeek dayOfWeek)
        {
            var daysInMonth = DateTime.DaysInMonth(date.Year, date.Month);
            var i = 1;

            List<DateTime> result = new List<DateTime>(5);

            do
            {
                var testDate = new DateTime(date.Year, date.Month, i);

                if (testDate.DayOfWeek == dayOfWeek)
                {
                    result.Add(testDate);
                    i += 7;
                }
                else
                {
                    i++;
                }

            } while (i <= daysInMonth);

            return result;
        }

    }
}

And this is the results table; Benchmarkdotnet Results

I can remove any code and picture-name if you want
I marked Jonathan's answer. Simple, clean and faster (interestingly).

like image 900
Lost_In_Library Avatar asked Jul 11 '17 16:07

Lost_In_Library


People also ask

How do you get every Monday of the YEAR in Excel?

Enter this formula: =DATE(YEAR(A2),1,8)-WEEKDAY(DATE(YEAR(A2),1,6)) into a blank cell where you want to locate the result, and then drag the fill handle down to the cells you want to apply this formula, and the date of the first Monday from the given date has been displayed at once, see screenshot: Notes: 1.

How do I get the Monday of the week in Excel?

(1) This formula =A2-MOD(A2-2,7) will return Monday as the beginning of week based on the given date. For returning Sunday as the start of a week, please apply this formula =A2-MOD(A2-1,7).

How do I get the first Monday of the month in Google Sheets?

To get the first Monday of month in google sheets, you can use the EOMONTH formula with the WEEKDAY formula.


2 Answers

Other answers work, but I would prefer to make use of Jon Skeet's AllDaysInMonth function from foreach day in month

public static IEnumerable<DateTime> AllDatesInMonth(int year, int month)
    {
        int days = DateTime.DaysInMonth(year, month);
        for (int day = 1; day <= days; day++)
        {
            yield return new DateTime(year, month, day);
        }
    }

And then you can call with LINQ like so:

var mondays = AllDatesInMonth(2017, 7).Where(i => i.DayOfWeek == DayOfWeek.Monday);

But I guess it depends on how many times you're going to use it as to wherther or not it's worth breaking out into a separate function.

like image 66
Jonathan Avatar answered Sep 30 '22 04:09

Jonathan


Try something like this:

public static IEnumerable<DateTime> GetAllDayOfWeekPerMonth(int month, int year, DayOfWeek dayOfWeek)
{
    var date = new DateTime(year, month, 1);

    if(date.DayOfWeek != dayOfWeek)
    {
        int daysUntilDayOfWeek = ((int) dayOfWeek - (int) date.DayOfWeek + 7) % 7;
        date = date.AddDays(daysUntilDayOfWeek);
    }

    List<DateTime> days = new List<DateTime>();

    while(date.Month == month)
    {
        days.Add(date);
        date = date.AddDays(7);         
    }

    return days;
}

Demo fiddle here

like image 30
maccettura Avatar answered Sep 30 '22 05:09

maccettura