0

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

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

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

例如

DateTime getStartOfFinancialQtr(DateTime date, int monthFinancialYearStartsOn)

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

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

8 月 15 日 FY 开始 4 月将是 7 月 1 日

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

但是 2013 年 1 月 15 日,当 FY 开始时 2 月将是 2012 年 11 月 1

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

4 回答 4

6

以下解决方案是我能想到的最简单的实现,并且无需任何 - 不必要的 - 循环即可工作:

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);
}
于 2013-07-16T18:49:24.987 回答
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
于 2013-07-16T18:00:50.813 回答
3

您可以使用.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
于 2013-07-31T13:13:37.200 回答
1

如前所述,您可以从Nearest Completed Quarter轻松获得答案。以下是您进行修改的方法:

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

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

于 2013-07-16T17:39:03.513 回答