使用给定的 DateTime 对象获取一个月的第一天和最后一天
Posted
技术标签:
【中文标题】使用给定的 DateTime 对象获取一个月的第一天和最后一天【英文标题】:Getting the first and last day of a month, using a given DateTime object 【发布时间】:2014-08-06 09:16:15 【问题描述】:我想获取给定日期所在月份的第一天和最后一天。日期来自 UI 字段中的值。
如果我使用时间选择器,我可以说
var maxDay = dtpAttendance.MaxDate.Day;
但我试图从 DateTime 对象中获取它。所以如果我有这个......
DateTime dt = DateTime.today;
如何从dt
获取当月的第一天和最后一天?
【问题讨论】:
不清楚你在问什么。_Date
变量中存储了一个值。你想从那个值中得到什么“最小值和最大值”?
它被否决了,因为人们想知道你为什么要这样做,以及你会在哪里使用这样的东西。您在这里没有告诉我们有关您最初的问题的任何信息
最好问你想做什么?不像那样你想怎么做?可能其他用途可以正确地建议你。
@Chathuranga 你知道任何月份的最短日期是什么......但问题是当月的最后日期是什么......你可以这样......添加1个月当前日期和从该日期起减去 1 天.. 现在您将获得您当前月份的最后日期
【参考方案1】:
DateTime
结构仅存储一个值,而不是值范围。 MinValue
和 MaxValue
是静态字段,它们包含 DateTime
结构实例的可能值范围。这些字段是静态的,与DateTime
的特定实例无关。它们与 DateTime
类型本身有关。
推荐阅读:static (C# Reference)
更新:获取月份范围:
DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
【讨论】:
我知道我在这里很挑剔,但lastDayofMonth
不应该是firstDayOfMonth.AddMonths(1).AddSeconds(-1);
吗?
@KarlGjertsen 你不够挑剔:) 完美的解决方案是AddTicks(-1)
,但如果我们不关心时间部分而只考虑日期部分,那么日子就可以了
现在这很挑剔! ;-) 这个问题并没有说明如何使用这些值,所以我倾向于防御性地编写代码。
@SergeyBerezovskiy 我不想提出这一点,但是当你像这样新建一个 DateTime 时,你不会丢失时区信息吗?当您像这样创建新实例时,附加到原始 DateTime 实例的任何时区信息都会丢失。
@KarlGjertsen,你想看挑剔的......我个人做< firstDayOfNextMonth
而不是<= lastDayOfMonth
。这样,无论粒度如何,它都将始终有效。 (我相信蜱会没事的,但谁知道未来会带来什么……纳米蜱?)【参考方案2】:
这是对@Sergey 和@Steffen 答案的长评论。在过去我自己编写过类似的代码后,我决定检查performant 最重要的是什么,同时记住清晰性也很重要。
结果
以下是 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()
代码
我使用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_AddMethod
在大多数测试运行中都比FirstDayOfMonth_NewMethod
稍快。但是,我认为后者的意图稍微明确一些,因此我更倾向于这样做。
LastDayOfMonth_AddMethod
明显输给了LastDayOfMonth_AddMethodWithDaysInMonth
、LastDayOfMonth_NewMethod
和LastDayOfMonth_NewMethodWithReuseOfExtMethod
。在最快的三个之间没有什么太多,所以这取决于你的个人喜好。我选择LastDayOfMonth_NewMethodWithReuseOfExtMethod
的清晰性,因为它重用了另一种有用的扩展方法。恕我直言,它的意图更明确,我愿意接受小的性能成本。
LastDayOfMonth_SpecialCase
假设您在特殊情况下提供月份的第一天,在这种情况下您可能已经计算了该日期,并且它使用带有 DateTime.DaysInMonth
的 add 方法来获得结果。正如您所期望的那样,这比其他版本更快,但除非您迫切需要速度,否则我认为在您的武器库中拥有这种特殊情况没有意义。
结论
这是我选择的扩展方法类,我相信与@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());
如果您已经走到了这一步,感谢您抽出宝贵的时间!它很有趣:¬)。如果您对这些算法有任何其他建议,请发表评论。
【讨论】:
不过,您的努力得到的荣誉太少了。很有用! 感谢@DionV。 - 很高兴被欣赏!当您赶时间时,简短的答案非常有用,但我认为进行一些更深入的分析通常很有用。 你错过了另一个选择:LastDayOfMonth_AddMethod_SpecialCase
(或类似的东西)。期望每月的第一天作为参数,我认为最快的应该是LastDayOfMonth_AddMethod
所做的!所以这很简单:return value.AddMonths(1).AddDays(-1);
感谢@Andrew,这是我使用它的结果:LastDayOfMonth_SpecialCase() 为 2835 毫秒,并且; LastDayOfMonth_AddMethod_SpecialCase() 为 4685 毫秒。在考虑结构创建时间时这可能是有道理的,并且 DateTime 的内部表示可能使添加天数成为一个简单的操作,但添加月数是一个更复杂的算法。【参考方案3】:
使用 .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));
【讨论】:
【参考方案4】:“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
。谢谢【参考方案6】:
您可以在此处为当月的第一天添加一个月,而不是从当天删除 1 天。
DateTime now = DateTime.Now;
var startDate = new DateTime(now.Year, now.Month, 1);
var endDate = startDate.AddMonths(1).AddDays(-1);
【讨论】:
【参考方案7】:如果你只关心日期
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”创建日期时间并抛出没有这样的日期的异常。 从 12 月 13 日开始?总共有12个月。你用什么日历? 如果您的日期是 12 月,那么它将尝试添加一个月,这将导致异常。 new DateTime(date.Year, 12+ 1, 1, 0, 0, 0, date.Kind).AddDays(-1);将在创建日期时间之后从中减去一天,因此在 12 月尝试“13”月的日期时会失败。你可以试试看。【参考方案8】:试试这个:
string strDate = DateTime.Now.ToString("MM/01/yyyy");
【讨论】:
【参考方案9】:此处接受的答案未考虑 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 实例的原始 Kind。
【讨论】:
【参考方案10】:我在我的脚本中使用了它(对我有用),但我需要一个完整的日期,而不需要将其修剪为仅日期而不是时间。
public DateTime GetLastDayOfTheMonth()
int daysFromNow = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) - (int)DateTime.Now.Day;
return DateTime.Now.AddDays(daysFromNow);
【讨论】:
【参考方案11】:波斯文化
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);
【讨论】:
【参考方案12】:你可以的
DateTime dt = DateTime.Now;
DateTime firstDayOfMonth = new DateTime(dt.Year, date.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);
【讨论】:
【参考方案13】:试试这个。它基本上计算DateTime.Now
已经过去的天数,然后从中减去一个并使用新值来查找当前月份的第一天。从那里它使用DateTime
并使用.AddMonths(-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);
【讨论】:
【参考方案14】:你可以试试这个来获取当前月份的第一天;
DateTime.Now.AddDays(-(DateTime.Now.Day-1))
并为其赋值。
像这样:
dateEndEdit.EditValue = DateTime.Now;
dateStartEdit.EditValue = DateTime.Now.AddDays(-(DateTime.Now.Day-1));
【讨论】:
【参考方案15】:创建一个 DateTime 类的实例
DateTime dateTime = DateTime.Now;
如果你想得到一个月的最后一天,你可以这样做
int lastDayOfMonth = DateTime.DaysInMonth(caducidadPuntos.Year, caducidadPuntos.Month);
如果你想得到当月的第一天,你可以这样做
DateTime firstDayMonth = new DateTime(dateTime.Year, dateTime.Month, 1);
【讨论】:
【参考方案16】:我们要求能够获取给定日期月份的开始和结束,包括时间,包括时间。我们最终使用了上述解决方案,非常感谢这里的每个人,并将其组合到一个 util 类中,以便能够获取给定月份和年份数字组合的开始和结束,直到最后一毫秒。包括我们在帮助其他人的情况下采取的行动。
实用程序:
public class DateUtil
public static (DateTime startOfMonth, DateTime endOfMonth) GetStartAndEndOfMonth(int month, int year)
DateTime startOfMonth = GetStartOfMonth(month, year);
DateTime endOfMonth = GetEndOfMonth(month, year);
return (startOfMonth, endOfMonth);
public static DateTime GetStartOfMonth(int month, int year)
return new DateTime(year, month, 1).Date;
public static DateTime GetEndOfMonth(int month, int year)
return new DateTime(year, month, 1).Date.AddMonths(1).AddMilliseconds(-1);
用法:
(DateTime startOfMonth, DateTime endOfMonth) = DateUtil.GetStartAndEndOfMonth(2, 2021); // February, 2021
【讨论】:
【参考方案17】:简单的方法
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”,此代码无法编译。【参考方案18】: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)));
【讨论】:
以上是关于使用给定的 DateTime 对象获取一个月的第一天和最后一天的主要内容,如果未能解决你的问题,请参考以下文章