最近的完整季度

13

有没有一个 C# 函数可以在给定日期的情况下,提供最近一季度结束的最后一天?

例如:

var lastDayOfLastQuarter = SomeFunction(jan 3, 2010);

将 lastDayOfLastQuarter 设置为 2009 年 12 月 31 日。


你需要能够定义季度的开始和结束时间。 - Paddy
2
哦,那个可能只是DoMagic()方法的一个特例,在任何情况下都可以神奇地解决你想要的问题 :-)。说真的,在基类库中没有这样的方法,但你肯定可以自己编写。 - Joey
7个回答

30
public static DateTime NearestQuarterEnd(this DateTime date) {
    IEnumerable<DateTime> candidates = 
        QuartersInYear(date.Year).Union(QuartersInYear(date.Year - 1));
    return candidates.Where(d => d < date.Date).OrderBy(d => d).Last();
}

static IEnumerable<DateTime> QuartersInYear(int year) {
    return new List<DateTime>() {
        new DateTime(year, 3, 31),
        new DateTime(year, 6, 30),
        new DateTime(year, 9, 30),
        new DateTime(year, 12, 31),
    };
}

使用方法:

DateTime date = new DateTime(2010, 1, 3);
DateTime quarterEnd = date.NearestQuarterEnd();

这种方法的优点在于,如果您对季度的定义有异议(例如,财年与日历年不同),则可以轻松修改QuartersInYear方法以处理此类情况。


3
+1 - 简单、可扩展、易于验证和理解。喜欢它。 - LBushkin
1
请注意,当您传入最后一天时,此方法将假定该季度已结束,也就是说,new DateTime(2012, 9, 30) 将返回 "2012-09-30",尽管有人可能会争辩说在最后一天,该季度尚未结束。 此外,该方法的名称有些误导,因为它将返回最近的季度,而不是如果最近的季度在未来则返回。 - R. Schreurs
喜欢聪明地使用LINQ,所以+1,但是对于任何人来说,这似乎有点低效,因为可以在不设置数据集并使用LINQ搜索它们的情况下完成此操作。请参见@Greg的答案。 - Ryan
静态 IEnumerable<DateTime> QuartersStarts(int year) { return new List<DateTime>() { new DateTime(year, 1, 1), new DateTime(year, 3, 1), new DateTime(year, 6, 1), new DateTime(year, 10, 1), }; }public static DateTime NearestQuarterStart(this DateTime date) { IEnumerable<DateTime> candidates = QuartersStarts(date.Year).Union(QuartersStarts(date.Year - 1)); return candidates.Where(d => d <= NearestQuarterEnd(date)).OrderBy(d => d).Last(); } - Boris Gappov
公共静态 DateTime QuarterStart(this DateTime date) { IEnumerable<DateTime> candidates = QuartersStarts(date.Year); return candidates.Where(d => d <= date).OrderBy(d => d).Last(); }公共静态 DateTime QuarterEnd(this DateTime date) { IEnumerable<DateTime> candidates = QuartersEnds(date.Year); return candidates.Where(d => d >= date).OrderBy(d => d).First(); } - Boris Gappov
鉴于该方法应返回上个季度的结束日期,如果给定日期为2015年9月30日,则返回2015年9月30日不正确。请将代码行“return candidates.Where(d => d <= date).OrderBy(d => d).Last();”修改为“return candidates.Where(d => d < date).OrderBy(d => d).Last();”。 - Rad

3
尝试这个: AddMonths(-(d.Month-1) % 3))将日期值移动到该季度第一个月的等效日,然后AddDays(-day)回到上一个月的最后一天。
  DateTime d = Datetime.Now;
  DateTime lastDayOfLastQuarter = d.AddMonths(-((d.Month-1)%3)).AddDays(-d.Day);

这在Day = 1Month = 12Year = 2009的情况下会失败。 - jason
好的,我认为这个方法可行。取消了踩票。然而,这种方法的缺点是它严重依赖于季度被定义为每年第三、第六、第九和第十二个月的最后几天。如果使用不同的定义,这种方法就不容易适应(公司更改其财务报表报告日期的频率比你想象的要高)。 - jason
1
这是我第一次听说“重新定义季度,而不是标准的日历季度1-3、4-6、7-9、10-12”。但我刚刚做了一些研究,发现对于估计税款,一些C-Corps被允许使用1-3、4-5、6-8和9-12...非常有趣... - Charles Bretana
1
但是如果使用了这种奇怪的模式,那么任何执行 OP 要求的操作的功能都必须传递有关如何重新定义 quarters 的信息... 这与 OP 所要求的完全不同。 (他指定标准季度) - Charles Bretana
是的,这对于季节性现金流的公司来说是可能发生的。 - jason
显示剩余3条评论

3

一个简单的函数可以计算最近完成月份的最后一天:

public static DateTime LastQuarter(DateTime date)
{
    return new DateTime(date.Year, date.Month - ((date.Month - 1) % 3), 1).AddDays(-1);
}

2
假设季度总是以3个月的间隔结束,您可以这样做:
也许不是最好的解决方案,但与其他提供的解决方案相比,非常易于阅读和修改。
public DateTime LastDayOfLastQuarter(DateTime date)
{
    int result = (int)(date.Month/3)

    switch (result)
    {
        // January - March
        case 0:
            return new DateTime(date.Year - 1, 12, 31);
        // April - June
        case 1:
            return new DateTime(date.Year, 3, 31);
        // July - September
        case 2:
            return new DateTime(date.Year, 6, 30);
        // October - December
        case 3:
            return new DateTime(date.Year, 9, 30);
    }
}

1
这段代码有很多错误:
  1. 每个语句缺少分号;
  2. 返回类型应该是 DateTime 而不是 void;
  3. 缺少默认情况,使用默认编译器设置,你会得到“并非所有的代码路径都返回值”的错误。
- R. Schreurs
@R.Schreurs,我已经为你修复了除编译器警告之外的所有问题。对于一个逻辑上永远不会被执行的返回,你有什么建议? - Brett Allen
1
在这种情况下,我会抛出一个异常: default: throw new ApplicationException(string.Format("{0} 不是一个有效的季度数字。", result)); 编译器会很高兴,你的代码也清楚地表明了它接受什么样的有效输入。还缺少一个分号……只是出于好奇,为什么不在VS.Net中编写代码并在发布之前进行编译呢? 任何寻找此功能的人都会很高兴,如果可以直接复制和粘贴而不需要调试。 - R. Schreurs
对此有两个最终的评论:1. 我修改了表达式以获取季度号变为(int)((date.Month - 1) / 3)。否则,三月中的任何一天都将给出年份-03-31。一个季度的最后一天也会产生之前一个季度的最后一天,如果不想要这样,必须添加一天,如下所示:(int)((date.AddDays(1).Month - 1) / 3)。2. 我费了心思来调试它,因为我喜欢它的原因就像你所说的:“非常容易阅读和修改”。 - R. Schreurs

1
Func<int, int> q = (i) => { return ((i - 1) / 3) + 1; };

测试:

Enumerable.Range(1, 12).Select(i => q(i));

1
这是一个简单的函数,可以为您提供当前季度的最后一天(假设您使用的是标准日历)。
DateTime LastDayOfQuarter(DateTime today)
{
    int quarter = (today.Month-1) / 3;
    int lastMonthInQuarter = (quarter +1) * 3;
    int lastDayInMonth =  DateTime.DaysInMonth(today.Year, lastMonthInQuarter);
    return new DateTime(today.Year, lastMonthInQuarter, lastDayInMonth); 
}

希望这有所帮助。

0
您可以使用简单的 switch 语句来检查给定日期属于哪个季度,并返回该季度的最后一天。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接