计算日期所在的财务季度的开始日期

本文关键字:日期 季度 开始 计算 | 更新日期: 2023-09-27 18:22:31

假设财务季度总是从一个月的第一天开始,并且总是有3个日历月长。

不同的组织在不同的月份开始他们的财政年度(FY)——有些可能是4月1日,有些可能是7月1日或可能只是1月1日(这将与正常的日历季度相匹配)。

给定财政年度开始的日期和月份,如何确定该日期所在季度的开始。

例如

DateTime getStartOfFinancialQtr(DateTime date, int monthFinancialYearStartsOn)

财政年度开始时的1月15日=1月1日

getStartOfFinancialQtr(new DateTime(2013,1,15), 1) == new DateTime(2013,1,1)

财政年度4月开始的8月15日将是的7月1日

getStartOfFinancialQtr(new DateTime(2013,8,15), 4) == new DateTime(2013,7,1)

但2013年1月15日财政年度2月开始时将是2012年11月1日

getStartOfFinancialQtr(new DateTime(2013,1,15), 2) == new DateTime(2012,11,1)

计算日期所在的财务季度的开始日期

下面的解决方案是我能想到的最简单的实现,并且没有任何不必要的循环:

DateTime getStartOfFinancialQtr(DateTime date, int monthFinancialYearStartsOn)
{
    var actualMonth = date.Month;
    var financialYear = date.Year;
    var difference = actualMonth - monthFinancialYearStartsOn;
    if(difference < 0)
    {
        --financialYear;
        difference += 12;
    }
    var quarter = difference / 3;
    return new DateTime(financialYear, monthFinancialYearStartsOn, 1).AddMonths(quarter * 3);
}

这不是这么简单吗?我是不是错过了什么?一个季度被定义为三个月的时间段,所以你只需要找到给定的日期在哪里,然后根据该日期的给定月份计算该季度的开始位置。

public DateTime GetStartOfFinancialQtr(DateTime dtGiven, int startMonth) {
    DateTime dtQuarter = new DateTime(dtGiven.Year, startMonth, 1);
    // Start Q is less than the given date
    if(startMonth > dtGiven.Month) {
        while(dtQuarter > dtGiven) {
            dtQuarter = dtQuarter.AddMonths(-3);
        }
    }
    // Start Q is larger than the given date
    else {
        while(dtQuarter.Month + 3 <= dtGiven.Month) {
            dtQuarter = dtQuarter.AddMonths(3);
        }
    }
    return dtQuarter;
}

以下是我运行的测试:

Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 1, 15), 1).ToString());
Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 8, 15), 4).ToString());
Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 1, 15), 2).ToString());

控制台输出:

01/01/2013 000000
07/01/2013 000000
11/01/2012 000000

您可以使用.NET:的时间段库的Year

// ----------------------------------------------------------------------
public void FiscalYearRange()
{
  // calendar
  TimeCalendar fiscalYearCalendar = new TimeCalendar(
    new TimeCalendarConfig
      {
        YearBaseMonth = YearMonth.April,
        YearType = YearType.FiscalYear
      } );
  // time range
  TimeRange timeRange = new TimeRange( new DateTime( 2007, 10, 1 ), new DateTime( 2012, 2, 25 ) );
  Console.WriteLine( "Time range: " + timeRange );
  Console.WriteLine();
  // fiscal quarter
  Console.WriteLine( "Start Quarter: " + new Quarter( timeRange.Start, fiscalYearCalendar ) );
  Console.WriteLine( "End Quarter: " + new Quarter( timeRange.End, fiscalYearCalendar ) );
  Console.WriteLine();
  // fiscal year
  Year year = new Year( timeRange.Start, fiscalYearCalendar );
  while ( year.Start < timeRange.End )
  {
    Console.WriteLine( "Fiscal Year: " + year );
    year = year.GetNextYear();
  }
} // FiscalYearRange

如上所述,您可以轻松地从最近完成季度获得答案。以下是如何进行修改:

public static class DateTimeExtensions {
    public static DateTime NearestQuarterEnd(
        this DateTime date,
        int firstMonthOfFiscalYear
    ) {
        IEnumerable<DateTime> candidates =
            QuartersInYear(date.Year, firstMonthOfFiscalYear)
                .Concat(QuartersInYear(date.Year - 1, firstMonthOfFiscalYear));
        return candidates.SkipWhile(d => d > date).First();
    }
    static Dictionary<Tuple<int, int>, List<DateTime>> dict =
        new Dictionary<Tuple<int, int>, List<DateTime>>();
    static IEnumerable<DateTime> QuartersInYear(
        int year,
        int firstMonthOfFiscalYear
    ) {
        Contract.Requires(firstMonthOfFiscalYear >= 1 
            && firstMonthOfFiscalYear <= 12);
        var key = Tuple.Create(year, firstMonthOfFiscalYear);
        if(dict.ContainsKey(key)) {
            return dict[key];
        }
        else {
            var value =
                Enumerable
                  .Range(0, 4)
                  .Select(k => firstMonthOfFiscalYear + 3 * k)
                  .Select(m => m <= 12 ? m : m % 12)
                  .Select(m => new DateTime(year, m, 1))
                  .OrderByDescending(d => d)
                  .ToList();
            dict.Add(key, value);
            return value;
        }
    }
}

用法:

 Console.WriteLine(new DateTime(2013, 1, 15).NearestQuarterEnd(1));
 Console.WriteLine(new DateTime(2013, 8, 15).NearestQuarterEnd(4));
 Console.WriteLine(new DateTime(2013, 1, 15).NearestQuarterEnd(2));

输出:

1/1/2013 12:00:00 AM
7/1/2013 12:00:00 AM
11/1/2012 12:00:00 AM

这通过了您的所有三个测试用例。