2016-07-11 21 views
1

私が取り組んでいるシステムは、定期支払いを設定する際にうるう年を選択できないような方法で構築され、設定されています。これは、閏年を無視しなければならないシーンの背後にあるすべての日付計算を結果としてもたらします。 (私はこれを選択しませんでしたが、これは書かれた方法です)日付アルゴリズム - うるう年を考慮しない日数を追加する

DateTimeの値を取り、日付に閏年を無視してメソッドを書く必要があります。これは本質的には2月29日それが存在しないふりをする。

たとえば、2010年1月1日に365日を追加すると、2013年12月31日ではなく、2011年1月1日に発生します。

私は.NETを使用しているので、DateTime.IsLeapYearやその他のヘルパーメソッドを利用できます。

これは進行中の作業であり、ここまではこれまでの作業です。私はより簡単なルートをとったので、もっと複雑なアルゴリズムが必要になることを認識しています。

public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd) 
{ 
    // Nothing to do 
    if (daysToAdd == 0) 
    { 
     return date; 
    } 

    // NOTE: This is an invalid approach; using DateTime.AddDays will take leap years into account 
    DateTime dateWithAddedDays = date.AddDays(daysToAdd); 

    const int FEB_28_DAY_OF_YEAR = 59; 

    int daysToSubtractForLeapYearConsideration = 0; 

    // The year is a leap year, which is under the feb 28 day threshold, and we're adding enough days to push it over the feb 28 day threshold 
    // This will result in .NET taking into account the feb 29th (the leap year day), but we have to subtract that leap year day since the system doesn't take feb 29th into account 
    if (DateTime.IsLeapYear(date.Year) && date.DayOfYear < FEB_28_DAY_OF_YEAR && (date.DayOfYear + daysToAdd > FEB_28_DAY_OF_YEAR)) 
    { 
     daysToSubtractForLeapYearConsideration++; 
    } 

    // The resulting date (after the days are added or subtracted) is a leap year, whose day is past the feburary 28 day threshold, and it's not the same year as the date (i.e. it spans across "n" years) 
    if (DateTime.IsLeapYear(dateWithAddedDays.Year) && dateWithAddedDays.DayOfYear > FEB_28_DAY_OF_YEAR && dateWithAddedDays.Year != date.Year) 
    { 
     daysToSubtractForLeapYearConsideration++; 
    } 

    // We determined if the original date should be leap year considered, as well as the resulting date/year with the days added. Now see if there are any years in between 
    // that we should consider 
    bool isThereAYearRangeThatWeNeedToEvaluateLeapYearsFor = Math.Abs(date.Year - dateWithAddedDays.Year) > 0; 

    if (isThereAYearRangeThatWeNeedToEvaluateLeapYearsFor) 
    { 
     for (int leapYearEvalIndex = Math.Min(date.Year, dateWithAddedDays.Year); leapYearEvalIndex <= Math.Max(date.Year, dateWithAddedDays.Year); leapYearEvalIndex++) 
     { 
      bool isYearPartOfTheYearsThatWeveAlreadyChecked = leapYearEvalIndex == date.Year || leapYearEvalIndex == dateWithAddedDays.Year; 

      if (!isYearPartOfTheYearsThatWeveAlreadyChecked && DateTime.IsLeapYear(leapYearEvalIndex)) 
      { 
       daysToSubtractForLeapYearConsideration++; 
      } 
     } 
    } 

    DateTime dateResult = date.AddDays(daysToAdd - daysToSubtractForLeapYearConsideration); 

    // The system does not allow 2/29 days, hence all this crazy date math 
    if (dateResult.Month == 2 && dateResult.Day == 29) 
    { 
     dateResult = dateResult.AddDays(1); 
    } 

    return dateResult; 
} 

ロジックは、上記のコードは、上で失敗する、ならびに(すなわち減算)口座負の数に入れなければなりません。

上記のコードは機能しませんが、私は問題に取り組もうとしていて、何も試してみないことを求めているわけではありません。

私はDavidのアプローチにかなり近いアルゴリズムを考え出しました。 (私はそれを書いて、そしてStackOverflowに戻り、応答をチェックしました)。

 public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd) 
    { 
     // Nothing to do 
     if (daysToAdd == 0) 
     { 
      return date; 
     } 

     DateTime dateResult = date; 

     // Are we adding or subtracting 
     bool areWeAddingDays = daysToAdd > 0; 

     int daysToAccountForInRegardToLeapYearDates = 0, 
      absDaysToAdd = Math.Abs(daysToAdd); 

     for (int i = 1; i <= absDaysToAdd; i++) 
     { 
      dateResult = dateResult.AddDays(areWeAddingDays ? 1 : -1); 

      if (dateResult.Month == 2 && dateResult.Day == 29) 
      { 
       daysToAccountForInRegardToLeapYearDates++; 
      } 
     } 

     dateResult = dateResult.AddDays(areWeAddingDays ? daysToAccountForInRegardToLeapYearDates : -daysToAccountForInRegardToLeapYearDates); 

     return dateResult; 
    } 
+0

をあなたは '= 1 int型の増分を行うことができます。 if(daysToAdd <0){increment = -1; daysToAdd = -daysToAdd; } for(int i = 0; i juharr

+1

偉大な心は似ていると思う –

答えて

1

これは機能する拡張メソッドです。複数のうるう年にまたがるのに十分な日数を加算または減算する場合にも機能します。

public static DateTime AddDaysWithoutLeapYear(this DateTime input, int days) 
    { 
     var output = input; 

     if (days != 0) 
     { 
      var increment = days > 0 ? 1 : -1; //this will be used to increment or decrement the date. 
      var daysAbs = Math.Abs(days); //get the absolute value of days to add 
      var daysAdded = 0; // save the number of days added here 
      while (daysAdded < daysAbs) 
      { 
       output = output.AddDays(increment); 
       if (!(output.Month == 2 && output.Day == 29)) //don't increment the days added if it is a leap year day 
       { 
        daysAdded++; 
       } 
      } 
     } 
     return output; 
    } 
+0

答えとしてマークしてください。他の読者にとっては、自分の実装を見るために私の質問を見てください。これはDavidのものとかなり似ています。 – contactmatt

0

いくつかのより多くのテストが必要になる場合がありますが、日時Add...機能またはあまりにも多くのループ、可能なカスタム実装使用せず:

public static DateTime AddDaysToDateWithLeapYearConsideration(DateTime date, int daysToAdd) 
{  
    int year = date.Year + daysToAdd/365, month = date.Month - 1, dir = Math.Sign(daysToAdd); 
    daysToAdd = (daysToAdd % 365) + date.Day; 
    int[] months = {31,28,31,30,31,30,31,31,30,31,30,31}; 
    while(daysToAdd > months[month] || daysToAdd < 0){  
     if(dir ==1) daysToAdd -= months[month]; 
     month += dir; 
     if(month == 12 || month == -1){ 
      year += dir; 
      month = dir == -1 ? 11 : 0; 
     } 
     if(dir ==-1) daysToAdd += months[month]; //for reverse direction, add previous month 
    } 
    return new DateTime(year, ++month,daysToAdd); 
} 
関連する問題