使用给定的DateTime对象获取一个月的第一天和最后一天

CAD*_*CAD 178 .net c# datetime winforms

我希望获得给定日期所在月份的第一天和最后一天.日期来自UI字段中的值.

如果我使用时间选择器,我可以说

var maxDay = dtpAttendance.MaxDate.Day;
Run Code Online (Sandbox Code Playgroud)

但我正试图从DateTime对象中获取它.所以,如果我有这个......

DateTime dt = DateTime.today;
Run Code Online (Sandbox Code Playgroud)

如何获得本月的第一天和最后一天dt

Ser*_*kiy 432

DateTime结构只存储一个值,而不存储值范围.MinValue并且MaxValue是静态字段,它保存DateTime结构实例的可能值范围.这些字段是静态的,与特定的实例无关DateTime.它们与DateTime类型本身有关.

建议阅读:静态(C#参考)

更新:获得月份范围:

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
Run Code Online (Sandbox Code Playgroud)

  • @KarlGjertsen你不够挑剔:)完美的解决方案将是`AddTicks(-1)`,但如果我们不关心时间部分而只考虑日期部分,那么天工作正常 (30认同)
  • 我知道我被挑剔这里,但不应该`lastDayofMonth`是`firstDayOfMonth.AddMonths(1).AddSeconds(-1);`? (13认同)
  • 现在这很挑剔!;-)问题并没有说明如何使用这些值,所以我倾向于采用防御性编码. (6认同)
  • @KarlGjertsen,你想看到挑剔的......我个人做`<firstDayOfNextMonth`而不是`<=lastDayOfMonth`。这样,无论粒度如何,它都将始终有效。(我确信蜱虫会没事,但谁知道未来会带来什么……纳米蜱虫?) (3认同)

Woo*_*Bob 89

这是对@Sergey和@ Steffen的答案的长篇评论.在过去我自己编写了类似的代码后,我决定检查什么是最高效的,同时记住清晰度也很重要.

结果

以下是1000万次迭代的示例测试运行结果:

2257 ms for FirstDayOfMonth_AddMethod()
2406 ms for FirstDayOfMonth_NewMethod()
6342 ms for LastDayOfMonth_AddMethod()
4037 ms for LastDayOfMonth_AddMethodWithDaysInMonth()
4160 ms for LastDayOfMonth_NewMethod()
4212 ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()
2491 ms for LastDayOfMonth_SpecialCase()
Run Code Online (Sandbox Code Playgroud)

我使用LINQPad 4(在C#程序模式下)在打开编译器优化的情况下运行测试.为了清晰和方便,以下是作为扩展方法考虑的经过测试的代码:

public static class DateTimeDayOfMonthExtensions
{
    public static DateTime FirstDayOfMonth_AddMethod(this DateTime value)
    {
        return value.Date.AddDays(1 - value.Day);
    }

    public static DateTime FirstDayOfMonth_NewMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, 1);
    }

    public static DateTime LastDayOfMonth_AddMethod(this DateTime value)
    {
        return value.FirstDayOfMonth_AddMethod().AddMonths(1).AddDays(-1);
    }

    public static DateTime LastDayOfMonth_AddMethodWithDaysInMonth(this DateTime value)
    {
        return value.Date.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - value.Day);
    }

    public static DateTime LastDayOfMonth_SpecialCase(this DateTime value)
    {
        return value.AddDays(DateTime.DaysInMonth(value.Year, value.Month) - 1);
    }

    public static int DaysInMonth(this DateTime value)
    {
        return DateTime.DaysInMonth(value.Year, value.Month);
    }

    public static DateTime LastDayOfMonth_NewMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, DateTime.DaysInMonth(value.Year, value.Month));
    }

    public static DateTime LastDayOfMonth_NewMethodWithReuseOfExtMethod(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, value.DaysInMonth());
    }
}

void Main()
{
    Random rnd = new Random();
    DateTime[] sampleData = new DateTime[10000000];

    for(int i = 0; i < sampleData.Length; i++) {
        sampleData[i] = new DateTime(1970, 1, 1).AddDays(rnd.Next(0, 365 * 50));
    }

    GC.Collect();
    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].FirstDayOfMonth_AddMethod();
    }
    string.Format("{0} ms for FirstDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].FirstDayOfMonth_NewMethod();
    }
    string.Format("{0} ms for FirstDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_AddMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_AddMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_AddMethodWithDaysInMonth();
    }
    string.Format("{0} ms for LastDayOfMonth_AddMethodWithDaysInMonth()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_NewMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_NewMethod()", sw.ElapsedMilliseconds).Dump();

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_NewMethodWithReuseOfExtMethod();
    }
    string.Format("{0} ms for LastDayOfMonth_NewMethodWithReuseOfExtMethod()", sw.ElapsedMilliseconds).Dump();

    for(int i = 0; i < sampleData.Length; i++) {
        sampleData[i] = sampleData[i].FirstDayOfMonth_AddMethod();
    }

    GC.Collect();
    sw.Restart();
    for(int i = 0; i < sampleData.Length; i++) {
        DateTime test = sampleData[i].LastDayOfMonth_SpecialCase();
    }
    string.Format("{0} ms for LastDayOfMonth_SpecialCase()", sw.ElapsedMilliseconds).Dump();

}
Run Code Online (Sandbox Code Playgroud)

分析

我对其中一些结果感到惊讶.

尽管其中没有多少,但是FirstDayOfMonth_AddMethodFirstDayOfMonth_NewMethod大多数测试运行的速度略快.但是,我认为后者有一个更清晰的意图,所以我更喜欢这个.

LastDayOfMonth_AddMethod是一个明显的失败者LastDayOfMonth_AddMethodWithDaysInMonth,LastDayOfMonth_NewMethod并且LastDayOfMonth_NewMethodWithReuseOfExtMethod.在最快的三个之间没有任何内容,因此它取决于您的个人偏好.我选择LastDayOfMonth_NewMethodWithReuseOfExtMethod其重用另一个有用的扩展方法的清晰度.恕我直言,它的意图更清晰,我愿意接受小的性能成本.

LastDayOfMonth_SpecialCase假设您在特殊情况下提供了本月的第一个月,您可能已经计算了该日期,并使用add方法DateTime.DaysInMonth获取结果.这比其他版本更快,正如您所期望的那样,但除非您迫切需要速度,否则我不认为在您的武器库中有这种特殊情况.

结论

这是一个扩展方法类,我的选择和与@Steffen的一般协议我相信:

public static class DateTimeDayOfMonthExtensions
{
    public static DateTime FirstDayOfMonth(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, 1);
    }

    public static int DaysInMonth(this DateTime value)
    {
        return DateTime.DaysInMonth(value.Year, value.Month);
    }

    public static DateTime LastDayOfMonth(this DateTime value)
    {
        return new DateTime(value.Year, value.Month, value.DaysInMonth());
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你有这个目的,谢谢你的时间!很有趣:¬).如果您对这些算法有任何其他建议,请发表评论.

  • 尽管你的努力太少了.这很有用! (5认同)
  • 谢谢@DionV. - 很高兴被赞赏!当你匆忙时,简短的答案是很好的,但我认为一些更深入的分析通常是有用的. (2认同)

Ste*_*old 13

使用.Net API获取月份范围(只是另一种方式):

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));
Run Code Online (Sandbox Code Playgroud)


jaz*_*cat 8

Last day of month”实际上是“ First day of *next* month, minus 1”。所以这是我使用的,不需要“DaysInMonth”方法:

public static DateTime FirstDayOfMonth(this DateTime value)
{
    return new DateTime(value.Year, value.Month, 1);
}

public static DateTime LastDayOfMonth(this DateTime value)
{
    return value.FirstDayOfMonth()
        .AddMonths(1)
        .AddMinutes(-1);
}
Run Code Online (Sandbox Code Playgroud)

注意:我使用AddMinutes(-1), 而不是AddDays(-1)这里的原因是因为通常您需要这些日期函数来报告某个日期期间的报告,并且当您为某个期间构建报告时,“结束日期”实际上应该是这样的,Oct 31 2015 23:59:59以便您的报告正常工作- 包括每月最后一天的所有数据。

也就是说,您实际上在这里获得了“本月的最后一刻”。不是最后一天。

好的,我现在要闭嘴了。


小智 5

DateTime dCalcDate = DateTime.Now;
dtpFromEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, 1);
dptToEffDate.Value = new DateTime(dCalcDate.Year, dCalcDate.Month, DateTime.DaysInMonth(dCalcDate.Year, dCalcDate.Month));
Run Code Online (Sandbox Code Playgroud)