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

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

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

var maxDay = dtpAttendance.MaxDate.Day;

但我试图从DateTime对象中获取它。如果我有这个。

DateTime dt = DateTime.today;

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

382051 次浏览

DateTime结构只存储一个值,而不是一个值范围。MinValueMaxValue是静态字段,它们保存了DateTime结构实例的可能值范围。这些字段是静态的,与DateTime的特定实例无关。它们与DateTime类型本身相关。

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

更新:获取月份范围:

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

更新:来自评论(@KarlGjertsen &@SergeyBerezovskiy)

DateTime date = ...
var firstDayOfMonth = new DateTime(date.Year, date.Month, 1);
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddSeconds(-1);
//OR
var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddTicks(-1);

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

这是对@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()

代码

我使用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_AddMethodWithDaysInMonthLastDayOfMonth_NewMethodLastDayOfMonth_NewMethodWithReuseOfExtMethod的竞争中,LastDayOfMonth_AddMethod明显是输家。在最快的三个之间,没有太多的东西,所以这取决于你的个人偏好。我选择了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());
}
}

如果你已经读到这里,感谢你的时间!这很有趣:¬)。如果你对这些算法有任何其他建议,请评论。

很简单

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

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

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

"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);
}
< p >注意: 我在这里使用AddMinutes(-1)而不是AddDays(-1)的原因是,通常你需要为某些日期-期间的报告使用这些日期函数,而当你为某个时期构建报告时,“结束日期”实际上应该是类似Oct 31 2015 23:59:59的东西,这样你的报告才能正确工作-包括每月最后一天的所有数据

也就是说,你实际上得到了“本月最后时刻”。不是最后一天。

好了,我要闭嘴了。

这里接受的答案没有考虑到DateTime实例的Kind。例如,如果您的原始DateTime实例是UTC Kind,那么通过创建一个新的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。

如果你只关心日期的话

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

试试这个:

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

对于波斯文化来说

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

我在我的脚本中使用了这个(适用于我),但我需要一个完整的日期,而不需要将其修剪为只有日期而没有时间。

public DateTime GetLastDayOfTheMonth()
{
int daysFromNow = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month) - (int)DateTime.Now.Day;
return DateTime.Now.AddDays(daysFromNow);
}
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 dt = DateTime.Now;
DateTime firstDayOfMonth = new DateTime(dt.Year, date.Month, 1);
DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1);

试试吧。它基本上计算出DateTime.Now已经过去的天数,然后从中减去1,并使用新值找到当前月份的第一天。从那里,它使用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);

你可以试着得到本月的第一天;

DateTime.Now.AddDays(-(DateTime.Now.Day-1))

给它赋一个值。

是这样的:

dateEndEdit.EditValue = DateTime.Now;
dateStartEdit.EditValue = DateTime.Now.AddDays(-(DateTime.Now.Day-1));

创建一个DateTime类的实例

DateTime dateTime = DateTime.Now;

如果你想要得到这个月的最后一天,你可以这样做

int lastDayOfMonth = DateTime.DaysInMonth(caducidadPuntos.Year, caducidadPuntos.Month);

如果你想得到每月的第一天,你可以这样做

DateTime firstDayMonth = new DateTime(dateTime.Year, dateTime.Month, 1);

我们需要能够获得给定日期月的开始和结束,包括时间,包括时间。我们最终使用了前面提到的解决方案,非常感谢这里的每个人,并将其组合到一个util类中,以便能够获得给定月份和年份的开始和结束,直到最后一毫秒。包括我们做了些什么,以帮助其他人。

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