如何将连续日期范围列表切换为C#中的财务年度列表?

时间:2010-07-20 02:16:06

标签: c# .net datetime

示例:给出连续的日期范围列表

  

清单[0] = 2001年1月1日至2001年8月14日

     

列表[1] = 2001年8月15日至2002年7月10日

我们假设一个财政年度是从7月1日到6月30日(明年),所以产出应该是

  

AnotherList [0] = 2000年7月1日至2001年6月30日

  period: 2001 Jan 01 to 2001 Jun 30
     

AnotherList [1] = 2001年7月1日至2002年6月30日

  period: 2001 Jul 01 to 2001 Aug 14
  period: 2001 Aug 15 to 2002 Jun 30
     

AnotherList [2] = 2002年7月1日至2003年6月30日

  period: 2002 Jul 01 to 2002 Jul 10

再次手动锻炼非常容易,但我的方法包含近100行代码,if if的组合,对于每个和while循环,我觉得它很难看。我正在尝试简化算法,以便更容易维护和调试。提前谢谢。

3 个答案:

答案 0 :(得分:3)

你可以聪明地使用GroupBy

// Beginning of earliest financial year
var start = new DateTime(2000,7,1); 
var range = Enumerable.Range(0,365*2);

// Some random test data
var dates1 = range.Select(i => new DateTime(2001,1,1).AddDays(i) );
var dates2 = range.Select(i => new DateTime(2003,1,1).AddDays(i) );

// Group by distance in years from beginning of earliest financial year
var finYears =
    dates1
    .Concat(dates2)
    .GroupBy(d => d.Subtract(start).Days / 365 );

这为IEnumerable<IGrouping<int, DateTime>>提供了每个外部可枚举项,其中包含单个财政年度中2个列表中的所有日期。

答案 1 :(得分:1)

for each range in list
  // determine end of this fiscal year
  cut = new Date(range.start.year, 06, 31)
  if cut < range.start
    cut += year
  end

  if (range.end <= cut)
    // one fiscal year
    result.add range
    continue
  end

  result.add new Range(range.start, cut)

  // chop off whole fiscal years
  start = cut + day
  while (start + year <= range.end)
    result.add new Range(start, start + year - day)
    start += year
  end

  result.add new Range(start, range.end)
end

抱歉,混合使用ruby和java:)

答案 2 :(得分:1)

编辑:改为包括更清晰的要求。

给定包含连续日期范围的列表,代码不一定非常难。实际上,您甚至不必编写实际循环:

public const int FYBeginMonth = 7, FYBeginDay = 1;

public static int FiscalYearFromDate(DateTime date)
{
    return date.Month > FYBeginMonth ||
           date.Month == FYBeginMonth && date.Day >= FYBeginDay ?
        date.Year : date.Year - 1;
}

public static IEnumerable<DateRangeWithPeriods>
              FiscalYears(IEnumerable<DateRange> continuousDates)
{
    int startYear = FiscalYearFromDate(continuousDates.First().Begin),
        endYear = FiscalYearFromDate(continuousDates.Last().End);
    return from year in Enumerable.Range(startYear, endYear - startYear + 1)
           select new DateRangeWithPeriods {
               Range = new DateRange { Begin = FiscalYearBegin(year),
                                       End = FiscalYearEnd(year) },
      // start with the periods that began the previous FY and end in this FY
               Periods = (from range in continuousDates
                          where FiscalYearFromDate(range.Begin) < year
                             && FiscalYearFromDate(range.End) == year
                          select new DateRange { Begin = FiscalYearBegin(year),
                                                 End = range.End })
                          // add the periods that begin this FY
                  .Concat(from range in continuousDates
                          where FiscalYearFromDate(range.Begin) == year
                          select new DateRange { Begin = range.Begin,
                                 End = Min(range.End, FiscalYearEnd(year)) })
                          // add the periods that completely span this FY
                  .Concat(from range in continuousDates
                          where FiscalYearFromDate(range.Begin) < year
                             && FiscalYearFromDate(range.End) > year
                          select new DateRange { Begin = FiscalYearBegin(year),
                                                 End = FiscalYearEnd(year) })

           };
}

这假定了一些DateRange结构和辅助函数,如下所示:

public struct DateRange
{
    public DateTime Begin { get; set; }
    public DateTime End { get; set; }
}

public class DateRangeWithPeriods
{
    public DateRange Range { get; set; }
    public IEnumerable<DateRange> Periods { get; set; }
}
private static DateTime Min(DateTime a, DateTime b)
{
    return a < b ? a : b;
}

public static DateTime FiscalYearBegin(int year)
{
    return new DateTime(year, FYBeginMonth, FYBeginDay);
}

public static DateTime FiscalYearEnd(int year)
{
    return new DateTime(year + 1, FYBeginMonth, FYBeginDay).AddDays(-1);
}

此测试代码:

static void Main()
{
    foreach (var x in FiscalYears(new DateRange[] { 
        new DateRange { Begin = new DateTime(2001, 1, 1),
                        End = new DateTime(2001, 8, 14) },
        new DateRange { Begin = new DateTime(2001, 8, 15),
                        End = new DateTime(2002, 7, 10) } }))
    {
        Console.WriteLine("from {0:yyyy MMM dd} to {1:yyyy MMM dd}",
                          x.Range.Begin, x.Range.End);
        foreach (var p in x.Periods)
            Console.WriteLine(
            "    period: {0:yyyy MMM dd} to {1:yyyy MMM dd}", p.Begin, p.End);
    }
}

输出:

from 2000 Jul 01 to 2001 Jun 30
    period: 2001 Jan 01 to 2001 Jun 30
from 2001 Jul 01 to 2002 Jun 30
    period: 2001 Jul 01 to 2001 Aug 14
    period: 2001 Aug 15 to 2002 Jun 30
from 2002 Jul 01 to 2003 Jun 30
    period: 2002 Jul 01 to 2002 Jul 10