2017-07-11 3 views
2

月の毎月の「月曜日」を取得するにはどうすればよいですか?月ごとに月曜日を取得するにはどうすればよいですか?

例です。
入力:11.July.2017(2017年7月11日)
出力:(3,10,17,24,31)
2017年7月3日月曜日
2017年7月10日月曜日
2017年7月17日月曜日
2017年7月24日月曜日
2017年7月31日

私は与えられた月の日数を得ることができるが(2017年7月の場合は、31日です)。次に、dayOfWeekが月曜日に等しい場合は繰り返しを記述します(forループa.e.)。リストに追加します。しかし、forループは31回動作するので、これは良いコードではありません。目標をアーカイブするにはより良いアルゴリズムが必要です。

私はC#の.NET Frameworkを使用してい4.6

UPDATE
のためのすべてのための 感謝はあなたの助け、私がこれまで持っているいくつかの答えの後、私は単純な&の汚れたベンチマークコードですべてのコードをテストし、より高速なアルゴリズムを見つけました。

私のベンチマークコードです。

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

    } 
} 

これは結果の表です。 Benchmarkdotnet Results

私はあなたが
をしたい場合、私はジョナサンの答えをマークし任意のコードと画像名を削除することができます。シンプルでクリーンで速く(興味深いことに)。ここで

+3

月の最初の節目に移動します。あなたが目標月にいる限り、7日間を追加します。 – litelite

+1

まあ、月曜日は7日ごとです。月の31日のすべてを通過する必要はありません。どの曜日が最初の月曜日であるかを決定することに基づいて、その月の最初の日であり、その後は毎週月曜日のループである。 –

+0

@litelite(と@Sam)あなたの迅速な対応に感謝します。しかし、私は月に何mondaysを知りません。例は2017年8月に4つの月曜日がありますが、2017年には5があります。私は最後の日付が実際には32.07.2017のように存在することを確認できました。あるいは、与えられた日付の月曜日にどれくらいの日数を数えれば、+7を加えることができます。しかし、私はより良いアプローチを探しています。 –

答えて

3

他の回答に動作しますが、私は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); 
     } 
    } 
からジョンスキートのAllDaysInMonth機能を利用することを好むだろう:のOPを解決するために

public static IEnumerable<DateTime> 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; 
} 

はそうは次のようにあなたは、このメソッドを呼び出します質問します

LINQで電話することができます。

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

しかし、私はそれが何回かあなたがそれを別の機能に分解する価値があるかどうかにかかわらず、それを使用しようとしているかどうかにかかっていると思います。

+0

あなたは既に、繰り返し繰り返すだけで、多くの不必要なDateTimeオブジェクトを作成する別の関数にしました。私はこれが非常に非効率であるように感じる – maccettura

+0

私は同意する、それができる最も効率的ではない。しかし、私の意見では、非常に読みやすく保守的です。私はそれがあなたがそれを使いたいかどうかという状況に依存していると思います。 – Jonathan

0

が、それは次のとおりです。

private static List<DateTime> 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; 
    } 
1

はこのような何か試してみてください:

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

デモフィドルhere

0

を私のバージョンでは、同じ結果を達成するがするまで、月の最初からループ回避します月の最初の日から最初の希望の日までのオフセットを計算して、最初の月曜日(または希望する週の任意の曜日)を指定します。

var mondays = GetDaysOfWeek(DateTime.Today, DayOfWeek.Monday); 
+0

ループの直前に 'workingDate.AddDays(offset);を配置しないのはなぜですか? – litelite

+0

@liteite良いスポット。コードを変更しました(バグがあったので修正しました!) – swatsonpicken

1

非科学的には、これは2年間の無作為な月の所与の曜日をチェックする数千回の反復で少し速く実行されます。

違いはごくわずかです。ミリ秒です。だから私は読むのが簡単なものは何でもするだろう。私はこれを少し読むのが簡単だと思いますが、他の答えでは関数名がそれを十分明確にしています。関数名が明確で単位がテストされている場合、残りの部分にはヘアを分割しません。月内の日の月の最初の日、及び数:

public class WeekdaysByMonth 
{ 
    public IEnumerable<DateTime> GetWeekdaysForMonth(DateTime month, DayOfWeek weekDay) 
    { 
     return GetDaysInMonth(month).Where(day => day.DayOfWeek == weekDay); 
    } 

    private IEnumerable<DateTime> GetDaysInMonth(DateTime date) 
    { 
     var dateLoop = new DateTime(date.Year,date.Month,1); 
     while (dateLoop.Month == date.Month) 
     { 
      yield return dateLoop; 
      dateLoop = dateLoop.AddDays(1); 
     } 
    } 
} 
+0

そして、別のコメントで参照されているJon Skeetの関数は、月よりも日数を得るために、私よりもほぼ確実に効率的です。私はまた、計算された月の日付を格納するバージョンをテストしました。それは速くても数ミリ秒も短い、ちょっとしたものでした。 –

0

あなたが技術的には2つだけ入力以外の情報を使用して独自のコード内でのすべての反復処理せずに全体の問題を解決することができます。それにもかかわらず、私は私の答えで単一のマイナーループを選んだ。

public List<DateTime> 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; 
    } 
関連する問題