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


196

我想获取给定日期所在的月份的第一天和最后一天。日期来自UI字段中的值。

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

var maxDay = dtpAttendance.MaxDate.Day;

但是我正在尝试从DateTime对象获取它。所以如果我有这个...

DateTime dt = DateTime.today;

如何从中获得每月的第一天和最后一天dt


目前尚不清楚您要问什么。_Date变量中存储了一个值。您试图从该值中获得什么“最小和最大”?
大卫

它之所以令人沮丧,是因为人们想知道您为什么要进行这样的思考,以及在哪里使用这样的事情。你是不是在这里告诉我们您最初的问题什么
莫帕特尔

最好问你想做什么?不喜欢那样你想怎么办?其他用途可能会建议您正确。
2014年

2
@Chathuranga你知道什么月份的最短日期是什么..但是问题是当月的最后日期是什么..你可以这样..在当前日期加上1个月,然后减去1天date ..现在您将获得您当月的最后日期
2014年

Answers:


472

DateTime结构仅存储一个值,而不存储值的范围。MinValueMaxValue是静态字段,其中包含DateTime结构实例的可能值的范围。这些字段是静态的,与的特定实例无关DateTime。它们与DateTime类型本身有关。

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

更新:获取月份范围:

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

16
我知道我在这里很挑剔,但是不lastDayofMonth应该firstDayOfMonth.AddMonths(1).AddSeconds(-1);吗?
Karl Gjertsen '16

36
@KarlGjertsen您不够挑剔:)完美的解决方案是AddTicks(-1),但是如果我们不在乎时间部分而只考虑日期部分,那么日子就可以了
Sergey Berezovskiy 2016年

7
现在这很挑剔!;-)问题没有说明如何使用这些值,因此我倾向于防御性地编写代码。
Karl Gjertsen '16

@SergeyBerezovskiy我不想提出这一点,但是当您像这样新建一个DateTime时,您是否会丢失时区信息?当您创建这样的新实例时,附加到原始DateTime实例的任何时区信息都会丢失。
Marko

2
@KarlGjertsen,您想看到挑剔的人……我个人是< firstDayOfNextMonth代替的<= lastDayOfMonth。这样,无论粒度如何,它都将始终有效。(我相信tick虫会很好,但谁知道未来会带来什么……纳米...虫?)
adam0101

99

这是对@Sergey和@Steffen的答案的较长评论。过去我自己编写过类似的代码,所以我决定检查性能出色的产品,同时牢记清晰性也很重要。

结果

这是一千万次迭代的示例测试运行结果:

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()

我使用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();

}

分析

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

尽管其中没有太多内容,但FirstDayOfMonth_AddMethodFirstDayOfMonth_NewMethod大多数测试运行要快一些。但是,我认为后者的意图要更清晰一些,因此我对此偏爱。

LastDayOfMonth_AddMethod反对一个明显的失败者LastDayOfMonth_AddMethodWithDaysInMonthLastDayOfMonth_NewMethodLastDayOfMonth_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());
    }
}

如果您已经走了那么远,谢谢您的时间!它很有趣:¬)。如果您对这些算法还有其他建议,请发表评论。


5
不过,您为自己的努力而获得的信用却少得多。这很有用!
Dion V.

2
谢谢@DionV。-非常感谢!匆忙时,简短的答案是不错的选择,但我认为通常需要一些更深入的分析。
WooWaaBob 2015年

您错过了另一个选择:(LastDayOfMonth_AddMethod_SpecialCase或类似的东西)。以每月的第一天为参数,我认为最快的应该是什么LastDayOfMonth_AddMethod!这样就很简单了:return value.AddMonths(1).AddDays(-1);
Andrew

1
感谢@Andrew,这是我使用它的结果:LastDayOfMonth_SpecialCase()为2835毫秒,以及 LastDayOfMonth_AddMethod_SpecialCase()为4685毫秒。在考虑结构创建时间时,这可能是有道理的,并且DateTime的内部表示可能使增加几天成为简单的操作,而增加几个月成为更复杂的算法。
WooWaaBob '16

15

使用.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));

6

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);
}

注意:之所以使用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));

通常避免仅使用代码的答案。考虑添加description有助于说明您的代码的。谢谢
MickyD 2015年

3

在这里,您可以为当月的第一天添加一个月,而不是从该天开始删除1天。

DateTime now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);

2

如果你只在乎日期

var firstDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, 0, 0, 0, date.Kind).AddMonths(1).AddDays(-1);

如果您想节省时间

var firstDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind);
var lastDay = new DateTime(date.Year, date.Month, 1, date.Hour, date.Minute, date.Second, date.Kind).AddMonths(1).AddDays(-1);

12月dudeeeeeee将会完全失败。它将尝试使用“ 13”月创建日期时间,并抛出没有此类日期的异常。
Pawel

从12月13日开始?总共有12个月。您使用什么日历?
维塔利

如果您的日期是十二月,那么它将尝试添加一个月,这将导致异常。新的DateTime(date.Year,12+ 1,1,0,0,0,date.Kind).AddDays(-1); 从日期中减去一天将在创建日期时间之后进行,因此在12月尝试输入月份“ 13”时它将失败。你可以试试。
Pawel

1

此处接受的答案未考虑DateTime实例的种类。例如,如果您原始的DateTime实例是UTC类型,那么通过创建新的DateTime实例,您将创建一个Unknown Kind实例,然后将根据服务器设置将其视为本地时间。因此,获取月份的第一个和最后一个日期的更合适方法是:

var now = DateTime.UtcNow;
var first = now.Date.AddDays(-(now.Date.Day - 1));
var last = first.AddMonths(1).AddTicks(-1);

这样,保留了DateTime实例的原始种类。


1

试试这个:

string strDate = DateTime.Now.ToString("MM/01/yyyy");

1

我在脚本中使用了此命令(适用于我),但是我需要一个完整的日期,而无需将其修整为仅日期而不是时间。

public DateTime GetLastDayOfTheMonth()
{
    int daysFromNow = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) - (int)DateTime.Now.Day;
    return DateTime.Now.AddDays(daysFromNow);
}

1

试试看。它基本上计算经过的天数DateTime.Now,然后减去天数,然后使用新值来查找当前月份的第一天。从那里它使用DateTime并使用.AddMonths(-1)用来获取上个月的第一个月。

获取上个月的最后一天基本上可以做同样的事情,除了一个月的天数加1,然后从中减去该值DateTime.Now.AddDays,从而得到上个月的最后一天。

int NumberofDays = DateTime.Now.Day;
int FirstDay = NumberofDays - 1;
int LastDay = NumberofDays + 1;
DateTime FirstofThisMonth = DateTime.Now.AddDays(-FirstDay);
DateTime LastDayOfLastMonth = DateTime.Now.AddDays(-LastDay);
DateTime CheckLastMonth = FirstofThisMonth.AddMonths(-1);

0

对于波斯文化

PersianCalendar pc = new PersianCalendar();            

var today = pc.GetDayOfMonth(DateTime.Now);
var firstDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddDays(-(today-1)));
var lastDayOfMonth = pc.GetDayOfMonth(DateTime.Now.AddMonths(1).AddDays(-today));            
Console.WriteLine("First day "+ firstDayOfMonth);
Console.WriteLine("Last day " + lastDayOfMonth);

0

你能行的

DateTime dt = DateTime.Now; 
DateTime firstDayOfMonth = new DateTime(dt.Year, date.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

-1

简单的方法

Begin = new DateTime(DateTime.Now.Year, DateTime.Now.Month,1).ToShortDateString();
End = new DataFim.Text = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month)).ToShortDateString();

由于出现“ new DataFim.Text”,因此无法编译此代码。
Wazner '16

-1
DateTime dCalcDate = DateTime.Now;
var startDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), 1);
var endDate = new DateTime(Convert.ToInt32(Year), Convert.ToInt32(Month), DateTime.DaysInMonth((Convert.ToInt32(Year)), Convert.ToInt32(Month)));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.