书接上回,我们继续来分享一些关于特殊时间获取的常用扩展方法。
01、获取当前日期所在月的第一个指定星期几
该方法和前面介绍的获取当前日期所在周的第一天(周一)核心思想是一样的,只是把求周一改成求周几而已,当然其中有些小细节需要注意,比如求所在周的第一天则两天都在同一周,而求所在月第一个指定周则可能两天在不同周,具体代码如下:
//获取当前日期所在月的第一个指定星期几
public static DateTime GetFirstDayOfWeekDateTimeInMonth(this DateTime dateTime, DayOfWeek dayOfWeek)
{
//获取当前日期所在月的第一天
var firstDayOfMonth = dateTime.GetFirstDayDateTimeOfMonth();
//计算目标日期与当月第一天相差天数
var diff = ((int)dayOfWeek - (int)firstDayOfMonth.DayOfWeek + 7) % 7;
return firstDayOfMonth.AddDays(diff);
}
下面我们还需要做详细的单元测试,我们分别测试指定周一和周日两个特殊日期,然后再分别测试三种特殊情况:
指定周一测试:
(1) 验证当前日期是周五,而周一在下一周的情况;
(2) 验证当前日期是本月第一个周一的情况;
(3) 验证当前日期是周日,并且在本月第一个周一之后的情况;
指定周日测试:
(1) 验证当前日期是周五,并且在本月第一个周日之前的情况;
(2) 验证当前日期是本月第一个周日的情况;
(3) 验证当前日期是周一,并且在本月第一个周日之后的情况;
具体代码如下:
[Fact]
public void GetFirstDayOfWeekDateTimeInMonth()
{
//验证当前日期是周五,而周一在下一周的情况
var friday_monday = new DateTime(2024, 11, 1, 14, 10, 10);
var day_friday_monday = friday_monday.GetFirstDayOfWeekDateTimeInMonth(DayOfWeek.Monday);
Assert.Equal(new DateTime(2024, 11, 4), day_friday_monday);
//验证当前日期是本月第一个周一的情况
var monday_monday = new DateTime(2024, 11, 4, 4, 10, 10);
var day_monday_monday = monday_monday.GetFirstDayOfWeekDateTimeInMonth(DayOfWeek.Monday);
Assert.Equal(new DateTime(2024, 11, 4), day_monday_monday);
//验证当前日期是周日,并且在本月第一个周一之后的情况
var sunday_monday = new DateTime(2024, 11, 30, 4, 10, 10);
var day_sunday_monday = sunday_monday.GetFirstDayOfWeekDateTimeInMonth(DayOfWeek.Monday);
Assert.Equal(new DateTime(2024, 11, 4), day_sunday_monday);
//验证当前日期是周五,并且在本月第一个周日之前的情况
var friday_sunday = new DateTime(2024, 11, 1, 14, 10, 10);
var day_friday_sunday = friday_sunday.GetFirstDayOfWeekDateTimeInMonth(DayOfWeek.Sunday);
Assert.Equal(new DateTime(2024, 11, 3), day_friday_sunday);
//验证当前日期是本月第一个周日的情况
var sunday_sunday = new DateTime(2024, 11, 30, 4, 10, 10);
var day_sunday_sunday = sunday_sunday.GetFirstDayOfWeekDateTimeInMonth(DayOfWeek.Sunday);
Assert.Equal(new DateTime(2024, 11, 3), day_sunday_sunday);
//验证当前日期是周一,并且在本月第一个周日之后的情况
var monday_sunday = new DateTime(2024, 11, 4, 4, 10, 10);
var day_monday_sunday = monday_sunday.GetFirstDayOfWeekDateTimeInMonth(DayOfWeek.Sunday);
Assert.Equal(new DateTime(2024, 11, 3), day_monday_sunday);
}
02、获取当前日期所在月的最后一个指定星期几
该方法和上一个求第一个指定星期几核心思想是一样的,具体代码如下:
//获取当前日期所在月的最后一个指定星期几
public static DateTime GetLastDayOfWeekDateTimeInMonth(this DateTime dateTime, DayOfWeek dayOfWeek)
{
//获取当前日期所在月的最后一天
var lastDayOfMonth = dateTime.GetLastDayDateTimeOfMonth();
//计算目标日期与当月最后一天相差天数
var diff = ((int)lastDayOfMonth.DayOfWeek - (int)dayOfWeek + 7) % 7;
return lastDayOfMonth.AddDays(-diff);
}
单元测试可以参考求第一个指定星期几,这里就不赘述了。
03、获取当前日期上一个指定星期几
求上一个指定周几,其实也不复杂,首先计算出当前日期与目标星期几相差的天数,其中有个小细节需要注意,就是如果两个日期相同,则需要把相差天数改为7,具体代码如下:
//获取当前日期上一个指定星期几
public static DateTime GetPreviousDayDateTimeOfWeek(this DateTime dateTime, DayOfWeek dayOfWeek)
{
//计算当前日期与目标星期几相差天数
var diff = ((int)dateTime.DayOfWeek - (int)dayOfWeek + 7) % 7;
//如果相差0天表示当前日期和目标星期几相同,需要改为7
diff = diff == 0 ? 7 : diff;
return dateTime.AddDays(-diff).Date;
}
我们分别对以下四种情况做单元测试:
(1) 验证当前日期是周一,而上一个周一在上一月的情况;
(2) 验证当前日期是周一,而上一个周一在当月的情况;
(3) 验证当前日期是周日,而上一个周一在当月的情况;
(4) 验证当前日期是周六,并且是当月最后一天的情况;
具体代码如下:
[Fact]
public void GetPreviousDayDateTimeOfWeek()
{
//验证当前日期是周一,而上一个周一在上一月的情况
var monday = new DateTime(2024, 11, 1, 14, 10, 10);
var day_monday = monday.GetPreviousDayDateTimeOfWeek(DayOfWeek.Monday);
Assert.Equal(new DateTime(2024, 10, 28), day_monday);
//验证当前日期是周一,而上一个周一在当月的情况
var monday1 = new DateTime(2024, 11, 25, 14, 10, 10);
var day_monday1 = monday1.GetPreviousDayDateTimeOfWeek(DayOfWeek.Monday);
Assert.Equal(new DateTime(2024, 11, 18), day_monday1);
//验证当前日期是周日,而上一个周一在当月的情况
var sunday = new DateTime(2024, 11, 24, 4, 10, 10);
var day_sunday = sunday.GetPreviousDayDateTimeOfWeek(DayOfWeek.Monday);
Assert.Equal(new DateTime(2024, 11, 18), day_sunday);
//验证当前日期是周六,并且是当月最后一天的情况
var saturday = new DateTime(2024, 11, 30, 4, 10, 10);
var day_saturday = saturday.GetPreviousDayDateTimeOfWeek(DayOfWeek.Monday);
Assert.Equal(new DateTime(2024, 11, 25), day_saturday);
}
04、获取当前日期下一个指定星期几
该方法和上面获取上一个指定星期几核心思想相同,具体代码如下:
//获取当前日期下一个最近指定星期几
public static DateTime GetNextDayDateTimeOfWeek(this DateTime dateTime, DayOfWeek dayOfWeek)
{
//计算目标日期与当月最后一天相差天数
var diff = ((int)dayOfWeek - (int)dateTime.DayOfWeek + 7) % 7;
//如果相差0天表示当前日期和目标星期几相同,需要改为7
diff = diff == 0 ? 7 : diff;
return dateTime.AddDays(diff).Date;
}
单元测试也可以参考求上一个指定星期几,这里就不再赘述了。
05、获取当前日期是其所在月的第几周
该方法的核心思想是,获取当前日期和当月第一天相差多少天,然后用相差的天数除以7即可获得当前是第几周。
但是这里有个比较麻烦的事情是如果第一周不满一周呢,比如当月的第一周第一天是2024-11-01周五,而今天是2024-11-07周四,应该是当月的第二周,但是如果直接计算两天的差再除以7结果显然是不对的。
因此我们首先需要把第一周不满一周的天数补上,即前面还有4天。
如此就是(7+4)/7=1…4,即所在第二周,其中商表示完整的周,余数则表示不完整的周。如果转为公式则为:days/7 + (days%7 > 0 ? 1 : 0),我们对这个公式简化后得到:(days + 6)/7,具体实现代码如下:
//获取当前日期是其所在月的第几周
public static int GetWeekOfMonth(this DateTime dateTime)
{
//获取当前日期所在月的第一天
var firstDayOfMonth = dateTime.GetFirstDayDateTimeOfMonth();
//首先设定周一为一周的开始
//计算当前月第一天与周一相差天数
//即第一周如果不满一周还差多少天
var diff = ((int)firstDayOfMonth.DayOfWeek - (int)DayOfWeek.Monday + 7) % 7;
//用第一周不满的差值加上当前日期的天数之和计算当前为当月第几周
//然后计算 总天数/7的商,如果有余数则再加1
//公式为:n/7 + (n%7 > 0 ? 1 : 0)
//上面公式可以简化为 (n+6)/7
return (diff + dateTime.Day + 6) / 7;
}
下面我们对其进行以下几种情况详细的单元测试:
(1) 验证当前日期是周五,且是当月第一天的情况;
(2) 验证当前日期是周日,且在当月第一周的情况;
(3) 验证当前日期是周一,且在当月第三周的情况;
(4) 验证当前日期是周日,且在当月第三周的情况;
(5) 验证当前日期是周六,且是当月最后一天的情况;
具体代码如下:
[Fact]
public void GetWeekOfMonth()
{
//验证当前日期是周五,且是当月第一天的情况
var friday = new DateTime(2024, 11, 1, 14, 10, 10);
var day_friday = friday.GetWeekOfMonth();
Assert.Equal(1, day_friday);
//验证当前日期是周日,且在当月第一周的情况
var sunday = new DateTime(2024, 11, 3, 14, 10, 10);
var day_sunday = sunday.GetWeekOfMonth();
Assert.Equal(1, day_sunday);
//验证当前日期是周一,且在当月第三周的情况
var monday = new DateTime(2024, 11, 11, 4, 10, 10);
var day_monday = monday.GetWeekOfMonth();
Assert.Equal(3, day_monday);
//验证当前日期是周日,且在当月第三周的情况
var date17 = new DateTime(2024, 11, 17, 4, 10, 10);
var day17 = date17.GetWeekOfMonth();
Assert.Equal(3, day17);
//验证当前日期是周六,且是当月最后一天的情况
var sunday1 = new DateTime(2024, 11, 30, 4, 10, 10);
var day_sunday1 = sunday1.GetWeekOfMonth();
Assert.Equal(5, day_sunday1);
}
06、获取当前日期是其所在年的第几周(ISO 8601 标准)
在ISO 8601 标准规定中,每周从星期一开始,且每年最少有 52 周,每年的第一周是包含该年第一天的那一周,且该周必须至少有四天。
获取当然日期所在年的第几周可以通过调用C#中文化信息中日历组件中GetWeekOfYear方法,具体代码如下:
//获取当前日期是其所在年的第几周(ISO 8601 标准)
public static int GetWeekOfYear(this DateTime dateTime)
{
var currentCulture = CultureInfo.CurrentCulture;
return currentCulture.Calendar.GetWeekOfYear(dateTime, currentCulture.DateTimeFormat.CalendarWeekRule, currentCulture.DateTimeFormat.FirstDayOfWeek);
}
07、获取当前日期所在月份的周数
该方法实现的核心思想是首先获取当前日期所在月份的第一天和最后一天,然后分别计算其所在当年第几周,最后相减即可得到,具体代码如下:
//获取当前日期所在月份的周数
public static int GetWeeksInMonth(this DateTime dateTime)
{
//获取当前日期所在月的第一天
var firstDayOfMonth = dateTime.GetFirstDayDateTimeOfMonth();
//获取当前日期所在月的最后一天
var lastDayOfMonth = dateTime.GetLastDayDateTimeOfMonth();
//获取当月第一天在全年中的周数
var firstWeek = firstDayOfMonth.GetWeekOfYear();
//获取当月最后一天在全年中的周数
var lastWeek = lastDayOfMonth.GetWeekOfYear();
return lastWeek - firstWeek + 1;
}
08、判断当前日期是否是周末
该方法比较简单,只是判断当前是否是否为周六或周日,具体代码如下:
//判断当前日期是否是周末
public static bool IsWeekend(this DateTime dateTime)
{
return dateTime.DayOfWeek == DayOfWeek.Saturday || dateTime.DayOfWeek == DayOfWeek.Sunday;
}
09、判断当前日期所在年是否是闰年
该方法调用了C#内置方法IsLeapYear,具体代码如下:
//判断当前日期所在年是否是闰年
public static bool IsLeapYear(this DateTime dateTime)
{
return DateTime.IsLeapYear(dateTime.Year);
}
10、获取当前日期所在季度
该方法也比较简单,只需要应用一个小公式即可求的,具体代码如下:
//获取当前日期所在季度
public static int GetQuarter(this DateTime dateTime)
{
return (dateTime.Month - 1) / 3 + 1;
}
稍晚些时候我会把库上传至Nuget,大家可以直接使用Ideal.Core.Common。
注:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Ideal