从周数计算日期

任何人都知道一个简单的方法来获得一周中第一天的日期(在欧洲是星期一)。我知道年份和星期号吗?我要用 C # 做这个。

167098 次浏览

最简单的方法可能是找到一年中的第一个星期一,然后加上相应的星期数。下面是一些示例代码。顺便说一下,它假设一个从1开始的周数:

using System;


class Test
{
static void Main()
{
// Show the third Tuesday in 2009. Should be January 20th
Console.WriteLine(YearWeekDayToDateTime(2009, DayOfWeek.Tuesday, 3));
}


static DateTime YearWeekDayToDateTime(int year, DayOfWeek day, int week)
{
DateTime startOfYear = new DateTime (year, 1, 1);


// The +7 and %7 stuff is to avoid negative numbers etc.
int daysToFirstCorrectDay = (((int)day - (int)startOfYear.DayOfWeek) + 7) % 7;


return startOfYear.AddDays(7 * (week-1) + daysToFirstCorrectDay);
}
}

第一周被定义为从星期一开始的一周,包含一年中的第一个星期四。

注意

下面的答案使用。NET 日历规则。它不承诺符合 ISO8601。当你需要的时候,看看这里的其他答案。星期编号是一团糟,总是试着找出你需要遵守的规则。


下面的代码正确地将2009年第1周的开始时间设置为29-12-2008。

注意,WeeNum 应该 > = 1

static DateTime FirstDateOfWeek(int year, int weekNum, CalendarWeekRule rule)
{
Debug.Assert(weekNum >= 1);


DateTime jan1 = new DateTime(year, 1, 1);


int daysOffset = DayOfWeek.Monday - jan1.DayOfWeek;
DateTime firstMonday = jan1.AddDays(daysOffset);
Debug.Assert(firstMonday.DayOfWeek == DayOfWeek.Monday);


var cal = CultureInfo.CurrentCulture.Calendar;
int firstWeek = cal.GetWeekOfYear(firstMonday, rule, DayOfWeek.Monday);


if (firstWeek <= 1)
{
weekNum -= 1;
}


DateTime result = firstMonday.AddDays(weekNum * 7);


return result;
}

就我个人而言,我会利用文化信息获得一周的一天,并循环到文化的第一天的一周。我不确定我是否正确地解释了它,这里有一个例子:

    public DateTime GetFirstDayOfWeek(int year, int weekNumber)
{
return GetFirstDayOfWeek(year, weekNumber, Application.CurrentCulture);
}


public DateTime GetFirstDayOfWeek(int year, int weekNumber,
System.Globalization.CultureInfo culture)
{
System.Globalization.Calendar calendar = culture.Calendar;
DateTime firstOfYear = new DateTime(year, 1, 1, calendar);
DateTime targetDay = calendar.AddWeeks(firstOfYear, weekNumber);
DayOfWeek firstDayOfWeek = culture.DateTimeFormat.FirstDayOfWeek;


while (targetDay.DayOfWeek != firstDayOfWeek)
{
targetDay = targetDay.AddDays(-1);
}


return targetDay;
}

根据 ISO8601:1988 在瑞典使用标准,一年中的第一个星期是在新的一年中至少有四天的第一个星期。

因此,如果你的一周从星期一开始,那么任何一年的第一个星期四都是在第一个星期之内。 可以从中选择 DateAdd 或 DateDiff。

我喜欢亨克 · 霍尔特曼提供的解决方案。但是,要想更加独立于文化,你必须在一周的第一天适应当前的文化(并不总是在星期一) :

using System.Globalization;


static DateTime FirstDateOfWeek(int year, int weekOfYear)
{
DateTime jan1 = new DateTime(year, 1, 1);


int daysOffset = (int)CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek - (int)jan1.DayOfWeek;


DateTime firstMonday = jan1.AddDays(daysOffset);


int firstWeek = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(jan1, CultureInfo.CurrentCulture.DateTimeFormat.CalendarWeekRule, CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek);


if (firstWeek <= 1)
{
weekOfYear -= 1;
}


return firstMonday.AddDays(weekOfYear * 7);
}

使用 Fluent DateTime http://fluentdatetime.codeplex.com/

        var year = 2009;
var firstDayOfYear = new DateTime(year, 1, 1);
var firstMonday = firstDayOfYear.Next(DayOfWeek.Monday);
var weeksDateTime = 12.Weeks().Since(firstMonday);

要在两个方向上进行转换,请参见: 关于 ISO 周日期的维基百科文章

假设周数从1开始

DateTime dt =  new DateTime(YearNumber, 1, 1).AddDays((WeekNumber - 1) * 7 - (WeekNumber == 1 ? 0 : 1));
return dt.AddDays(-(int)dt.DayOfWeek);

这应该给你在任何一个星期的第一天。我还没有做过很多测试,但看起来很有效。它比我在网上找到的大多数解决方案都要小,所以想和大家分享一下。

我改进了一下托马斯的解决方案:

   public static DateTime FirstDateOfWeek(int year, int weekOfYear)
{
return Timer.FirstDateOfWeekOfMonth(year, 1, weekOfYear);
}


public static DateTime FirstDateOfWeekOfMonth(int year, int month,
int weekOfYear)
{
DateTime dtFirstDayOfMonth = new DateTime(year, month, 1);


//I also commented out this part:
/*
if (firstWeek <= 1)
{
weekOfYear -= 1;
}
*/

否则,日期提前了一个星期. 。

谢谢你,托马斯,帮了大忙。

免费的 用于.NET 的时间周期库包括 ISO 8601符合类 一周:

// ----------------------------------------------------------------------
public static DateTime GetFirstDayOfWeek( int year, int weekOfYear )
{
return new Week( year, weekOfYear ).FirstDayStart;
} // GetFirstDayOfWeek

我尝试了上面的一些代码和一些有小错误,当你尝试不同的年份和不同的开始日期一周你会看到他们,我采取了 Jon Skeet 的代码,修复它和它的工作,非常简单的代码。

Public Function YearWeekDayToDateTime(ByVal year As Integer, ByVal weekDay As Integer, ByVal week As Integer) As DateTime
' weekDay, day you want
Dim startOfYear As New DateTime(year, 1, 1)
Dim startOfYearFixDay As Integer


If startOfYear.DayOfWeek <> DayOfWeek.Sunday Then
startOfYearFixDay = startOfYear.DayOfWeek
Else
startOfYearFixDay = 7
End If


Return startOfYear.AddDays((7 * (week)) - startOfYearFixDay + weekDay)
End Function

我对 HenkHolterman 的解决方案有疑问,甚至对 RobinAndersson 的解决方案也有疑问。

仔细阅读 ISO 8601标准可以很好地解决这个问题。以第一个星期四为目标,而不是星期一。下面的代码也适用于2009年的第53周。

public static DateTime FirstDateOfWeekISO8601(int year, int weekOfYear)
{
DateTime jan1 = new DateTime(year, 1, 1);
int daysOffset = DayOfWeek.Thursday - jan1.DayOfWeek;


// Use first Thursday in January to get first week of the year as
// it will never be in Week 52/53
DateTime firstThursday = jan1.AddDays(daysOffset);
var cal = CultureInfo.CurrentCulture.Calendar;
int firstWeek = cal.GetWeekOfYear(firstThursday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);


var weekNum = weekOfYear;
// As we're adding days to a date in Week 1,
// we need to subtract 1 in order to get the right date for week #1
if (firstWeek == 1)
{
weekNum -= 1;
}


// Using the first Thursday as starting week ensures that we are starting in the right year
// then we add number of weeks multiplied with days
var result = firstThursday.AddDays(weekNum * 7);


// Subtract 3 days from Thursday to get Monday, which is the first weekday in ISO8601
return result.AddDays(-3);
}

我使用了其中一个解决方案,但它给了我错误的结果,仅仅因为它把星期天算作一周的第一天。

我变了:

var firstDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber - 1) * 7);
var lastDay = firstDay.AddDays(6);

致:

var lastDay = new DateTime(DateTime.Now.Year, 1, 1).AddDays((weekNumber) * 7);
var firstDay = lastDay.AddDays(-6);

现在它起作用了。

提议的解决方案并不完整——它只适用于 CalendarWeekRule。第一个完整周。其他类型的周规则不起作用。这可以通过这个测试用例看到:

foreach (CalendarWeekRule rule in Enum.GetValues(typeof(CalendarWeekRule)))
{
for (int year = 1900; year < 2000; year++)
{
DateTime date = FirstDateOfWeek(year, 1, rule);
Assert(CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, rule, DayOfWeek.Monday) == 1);
Assert(CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date.AddDays(-1), rule, DayOfWeek.Monday) != 1);
}
}

我对提议的解决方案进行了改进,它更简单,并参数化了第一个 DayOfWeek:

public static DateTime GetFirstDayOfWeek(int year, int week, DayOfWeek firstDayOfWeek)
{
return GetWeek1Day1(year, firstDayOfWeek).AddDays(7 * (week - 1));
}


public static DateTime GetWeek1Day1(int year, DayOfWeek firstDayOfWeek)
{
DateTime date = new DateTime(year, 1, 1);


// Move towards firstDayOfWeek
date = date.AddDays(firstDayOfWeek - date.DayOfWeek);


// Either 1 or 52 or 53
int weekOfYear = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFullWeek, firstDayOfWeek);


// Move forwards 1 week if week is 52 or 53
date = date.AddDays(7 * System.Math.Sign(weekOfYear - 1));


return date;
}

这个方法对我很有用,它还有一个优点,那就是需要一个 cultureinfo 作为参数来测试不同文化的公式。如果为空,则获取当前区域性信息... 有效值如下: “ it”、“ en-us”、“ fr”、 ... 等等。 诀窍是减去一年中第一天的周数,这可能是1,表示第一天是在第一周内。希望这个能帮上忙。

Public Shared Function FirstDayOfWeek(ByVal year As Integer, ByVal weekNumber As Integer, ByVal culture As String) As Date
Dim cInfo As System.Globalization.CultureInfo
If culture = "" Then
cInfo = System.Globalization.CultureInfo.CurrentCulture
Else
cInfo = System.Globalization.CultureInfo.CreateSpecificCulture(culture)
End If
Dim calendar As System.Globalization.Calendar = cInfo.Calendar
Dim firstOfYear As DateTime = New DateTime(year, 1, 1, calendar)
Dim firstDayWeek As Integer = calendar.GetWeekOfYear(firstOfYear, cInfo.DateTimeFormat.CalendarWeekRule, cInfo.DateTimeFormat.FirstDayOfWeek)
weekNumber -= firstDayWeek
Dim targetDay As DateTime = calendar.AddWeeks(firstOfYear, weekNumber)
Dim fDayOfWeek As DayOfWeek = cInfo.DateTimeFormat.FirstDayOfWeek


While (targetDay.DayOfWeek <> fDayOfWeek)
targetDay = targetDay.AddDays(-1)
End While
Return targetDay
End Function

这里有一个方法可以兼容谷歌分析的周数,也可以兼容我们在英特尔内部使用的编号,我相信这个方法在很多其他情况下也可以使用。

// Google Analytics does not follow ISO standards for date.
// It numbers week 1 starting on Jan. 1, regardless what day of week it starts on.
// It treats Sunday as the first day of the week.
// The first and last weeks of a year are usually not complete weeks.
public static DateTime GetStartDateTimeFromWeekNumberInYear(int year, uint weekOfYear)
{
if (weekOfYear == 0 || weekOfYear > 54) throw new ArgumentException("Week number must be between 1 and 54! (Yes, 54... Year 2000 had Jan. 1 on a Saturday plus 53 Sundays.)");


// January 1 -- first week.
DateTime firstDayInWeek = new DateTime(year, 1, 1);
if (weekOfYear == 1) return firstDayInWeek;


// Get second week, starting on the following Sunday.
do
{
firstDayInWeek = firstDayInWeek.AddDays(1);
} while (firstDayInWeek.DayOfWeek != DayOfWeek.Sunday);


if (weekOfYear == 2) return firstDayInWeek;


// Now get the Sunday of whichever week we're looking for.
return firstDayInWeek.AddDays((weekOfYear - 2)*7);
}

这是我的解决方案,当我们想要计算一个给定的日期,年,周数和一周的日子。

int Year = 2014;
int Week = 48;
int DayOfWeek = 4;


DateTime FecIni = new DateTime(Year, 1, 1);
FecIni = FecIni.AddDays(7 * (Week - 1));
if ((int)FecIni.DayOfWeek > DayOfWeek)
{
while ((int)FecIni.DayOfWeek != DayOfWeek) FecIni = FecIni.AddDays(-1);
}
else
{
while ((int)FecIni.DayOfWeek != DayOfWeek) FecIni = FecIni.AddDays(1);
}

我简化了 Mikael Svensson 提供的代码,这对欧洲的许多国家都是正确的。

public static DateTime FirstDateOfWeekIso8601(int year, int week)
{
var firstThursdayOfYear = new DateTime(year, 1, 1);
while (firstThursdayOfYear.DayOfWeek != DayOfWeek.Thursday)
{
firstThursdayOfYear = firstThursdayOfYear.AddDays(1);
}


var startDateOfWeekOne = firstThursdayOfYear.AddDays(-(DayOfWeek.Thursday - DayOfWeek.Monday));


return startDateOfWeekOne.AddDays(7 * (week - 1));
}

我已经编写并测试了以下代码,现在它们对我来说运行得非常好。请让我知道,如果有人面临这个问题,我已经发布了一个问题,以便得到最好的可能的答案。也许有人会觉得有用。

public static DateTime GetFirstDateOfWeekByWeekNumber(int year, int weekNumber)
{
var date = new DateTime(year, 01, 01);
var firstDayOfYear = date.DayOfWeek;
var result = date.AddDays(weekNumber * 7);


if (firstDayOfYear == DayOfWeek.Monday)
return result.Date;
if (firstDayOfYear == DayOfWeek.Tuesday)
return result.AddDays(-1).Date;
if (firstDayOfYear == DayOfWeek.Wednesday)
return result.AddDays(-2).Date;
if (firstDayOfYear == DayOfWeek.Thursday)
return result.AddDays(-3).Date;
if (firstDayOfYear == DayOfWeek.Friday)
return result.AddDays(-4).Date;
if (firstDayOfYear == DayOfWeek.Saturday)
return result.AddDays(-5).Date;
return result.AddDays(-6).Date;
}

目前,还没有 C # 类能够正确处理 ISO 8601周编号。即使你可以举例说明一种文化,寻找最接近的东西并纠正它,我认为最好还是自己完整地计算一下:

    /// <summary>
/// Converts a date to a week number.
/// ISO 8601 week 1 is the week that contains the first Thursday that year.
/// </summary>
public static int ToIso8601Weeknumber(this DateTime date)
{
var thursday = date.AddDays(3 - date.DayOfWeek.DayOffset());
return (thursday.DayOfYear - 1) / 7 + 1;
}


/// <summary>
/// Converts a week number to a date.
/// Note: Week 1 of a year may start in the previous year.
/// ISO 8601 week 1 is the week that contains the first Thursday that year, so
/// if December 28 is a Monday, December 31 is a Thursday,
/// and week 1 starts January 4.
/// If December 28 is a later day in the week, week 1 starts earlier.
/// If December 28 is a Sunday, it is in the same week as Thursday January 1.
/// </summary>
public static DateTime FromIso8601Weeknumber(int weekNumber, int? year = null, DayOfWeek day = DayOfWeek.Monday)
{
var dec28 = new DateTime((year ?? DateTime.Today.Year) - 1, 12, 28);
var monday = dec28.AddDays(7 * weekNumber - dec28.DayOfWeek.DayOffset());
return monday.AddDays(day.DayOffset());
}


/// <summary>
/// Iso8601 weeks start on Monday. This returns 0 for Monday.
/// </summary>
private static int DayOffset(this DayOfWeek weekDay)
{
return ((int)weekDay + 6) % 7;
}

我找到了第一个星期一的星期,并且适当地改变了星期号码。

 DateTime GetFirstWeekDay(int year, int weekNum)
{
Calendar calendar = CultureInfo.CurrentCulture.Calendar;


DateTime jan1 = new DateTime(year, 1, 1);


int daysOffset = DayOfWeek.Monday - jan1.DayOfWeek;
DateTime firstMonday = jan1.AddDays(daysOffset);
int firstMondayWeekNum = calendar.GetWeekOfYear(firstMonday, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);


DateTime firstWeekDay = firstMonday.AddDays((weekNum-firstMondayWeekNum) * 7);


return firstWeekDay;
}

更新 : . NET Core 3.0和. NET Standard 2.1已经提供了这种类型。

好消息!将 System.Globalization.ISOWeek添加到。NET Core 刚刚合并,目前正在计划发布3.0版本。希望它能传播到其他地方。NET 平台在不久的将来。

您应该能够使用 ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)方法来计算这个值。

您可以找到源代码 给你

我发现最大的问题之一就是从星期转换为日期,然后从日期转换为星期。

主要的问题是,当试图从属于前一年的一周的日期中获取正确的周年时。幸运的是 System.Globalization.ISOWeek.GetYear解决了这个问题。

我的解决办法是:

public class WeekOfYear
{
public static (int Year, int Week) DateToWeekOfYear(DateTime date) =>
(ISOWeek.GetYear(date), ISOWeek.GetWeekOfYear(date));


public static bool ValidYearAndWeek(int year, int week) =>
year >= 1 && year <= 9999 && week >= 1 && week <= 53 // bounds of year/week
&& !(year <= 1 && week <= 1) && !(year >= 9999 && week >= 53); // bounds of DateTime


public int Year { get; }
public int Week { get; }
public virtual DateTime StartOfWeek { get; protected set; }
public virtual DateTime EndOfWeek { get; protected set; }


public virtual IEnumerable<DateTime> DaysInWeek =>
Enumerable.Range(1, 10).Select(i => StartOfWeek.AddDays(i));


public WeekOfYear(int year, int week)
{
if (!ValidYearAndWeek(year, week))
throw new ArgumentException($"DateTime can't represent {week} of year {year}.");


Year = year;
Week = week;
StartOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Monday);
EndOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Sunday).AddDays(1).AddTicks(-1);
}


public WeekOfYear((int Year, int Week) week) : this(week.Year, week.Week) { }
public WeekOfYear(DateTime date) : this(DateToWeekOfYear(date)) { }
}

第二个最大的问题是,美国人更喜欢从周日开始的几周。

解决方案我从上面的子类 WeekOfYear开始,并管理构造函数中的偏移量(它将周转换为日期)和 DateToWeekOfYear(它将日期转换为周)。

public class UsWeekOfYear : WeekOfYear
{
public static new (int Year, int Week) DateToWeekOfYear(DateTime date)
{
// if date is a sunday, return the next week
if (date.DayOfWeek == DayOfWeek.Sunday) date = date.AddDays(1);
return WeekOfYear.DateToWeekOfYear(date);
}


public UsWeekOfYear(int year, int week) : base(year, week)
{
StartOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Monday).AddDays(-1);
EndOfWeek = ISOWeek.ToDateTime(year, week, DayOfWeek.Sunday).AddTicks(-1);
}


public UsWeekOfYear((int Year, int Week) week) : this(week.Year, week.Week) { }
public UsWeekOfYear(DateTime date) : this(DateToWeekOfYear(date)) { }
}

下面是一些测试代码:

public static void Main(string[] args)
{
Console.WriteLine("== Last Week / First Week");
Log(new WeekOfYear(2020, 53));
Log(new UsWeekOfYear(2020, 53));


Log(new WeekOfYear(2021, 1));
Log(new UsWeekOfYear(2021, 1));


Console.WriteLine("\n== Year Crossover (iso)");
var start = new DateTime(2020, 12, 26);
var i = 0;
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-26 - Sat
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-27 - Sun
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-28 - Mon
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-29 - Tue
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-30 - Wed
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2020-12-30 - Thu
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-01 - Fri
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-02 - Sat
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-03 - Sun
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-04 - Mon
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-05 - Tue
Log(start.AddDays(i), new WeekOfYear(start.AddDays(i++))); // 2021-01-06 - Wed


Console.WriteLine("\n== Year Crossover (us)");
i = 0;
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-26 - Sat
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-27 - Sun
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-28 - Mon
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-29 - Tue
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-30 - Wed
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2020-12-30 - Thu
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-01 - Fri
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-02 - Sat
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-03 - Sun
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-04 - Mon
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-05 - Tue
Log(start.AddDays(i), new UsWeekOfYear(start.AddDays(i++))); // 2021-01-06 - Wed


var x = new UsWeekOfYear(2020, 53) as WeekOfYear;
}


public static void Log(WeekOfYear week)
{
Console.WriteLine($"{week} - {week.StartOfWeek:yyyy-MM-dd} ({week.StartOfWeek:ddd}) - {week.EndOfWeek:yyyy-MM-dd} ({week.EndOfWeek:ddd})");
}


public static void Log(DateTime date, WeekOfYear week)
{
Console.WriteLine($"{date:yyyy-MM-dd (ddd)} - {week} - {week.StartOfWeek:yyyy-MM-dd (ddd)} - {week.EndOfWeek:yyyy-MM-dd (ddd)}");
}

根据 khellang 的建议(抱歉,我不能添加评论) ,使用。NET 核心非常简单。这是我的密码:

var today = DateTime.Today;
CultureInfo myCI = new CultureInfo("it-IT");
Calendar myCal = myCI.Calendar;
var weekNumber = myCal.GetWeekOfYear(today, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
var firstWeekDay = ISOWeek.ToDateTime(today.Year, weekNumber, DayOfWeek.Monday);