从星期数计算日期


Answers:


251

我遇到了@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);
}       

3
我尝试了这里给出的几乎所有解决方案,这是目前唯一对我正确的解决方案。目前是2012年2月7日。第n周是#6。此代码正确地给了我2月6日作为一周的开始日期。其他解决方案都给了我2月13日,这实际上是第7周的开始日期。
HaukurHaf'2

1
像魅力一样工作..测试了一些数据: pastebin.com/mfx8s1vq 所有工作都完美无缺!谢谢Mikael!
米切尔

1
@RobinWassén-Andersson很好,您再重新回答了这个问题:D再增加6票,我将回答“不是”正确答案。
Mikael Svenson

2
建议:将方法名称命名为“ ISO8601”,因为根本没有通用的“正确”答案。
Henk Holterman '04

1
@Muflix我对SQL和日期/日历知之甚少-但是执行CLR会起作用。
米卡·史文森

36

我喜欢Henk Holterman提供的解决方案。但是要使文化更加独立,您必须获得当前文化的一周的第一天(并不总是星期一):

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

我已将其添加到Mannex中,作为Calendar和DateTimeFormatInfo的扩展方法。我也交叉引用了此答案以获取信誉。
阿提夫·阿齐兹

1
它在我的机器上无法正常工作。它显示的日期是0010而不是2010。我不知道这是.net框架还是此函数中的问题。无论如何,都可以尝试
Eduardo Xavier 2010年

6
firstMonday是一个不好的变量名,可能不是星期一。=)
mflodin 2012年

11

更新:.NET Core 3.0和.NET Standard 2.1附带了此类型。

好消息!一个拉请求加入System.Globalization.ISOWeek到.NET的核心刚刚合并,目前内定为3.0版本。希望它将在不远的将来传播到其他.NET平台。

您应该可以使用 ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek)方法进行计算。

您可以在此处找到源代码。


9

最简单的方法可能是找到一年中的第一个星期一,然后添加相关的星期数。这是一些示例代码。顺便说一下,它假设一个星期数字从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);
    }
}

4
是的,但一年的第一个星期一可能属于上一年的第52 | 53周。
汉克·霍尔特曼

1
这取决于您要如何定义事物。不幸的是,我们在这里没有太多的信息要继续……我希望这是有用的。
乔恩·斯基特

3

就个人而言,我会利用文化信息来获取星期几,然后循环到文化的一周的第一天。我不确定我是否解释正确,下面是一个示例:

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

3

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

2

根据瑞典使用的 ISO 8601:1988今年的第一周是在新年内至少有四天的第一周。

因此,如果您的一周从星期一开始,则每年的第一个星期四都在第一周之内。您可以从中添加DateAdd或DateDiff。


2

假设星期数从1开始

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

这应该给您任何一周的第一天。我没有对其进行大量测试,但是看起来它可以工作。这个解决方案比我在网络上找到的大多数解决方案都要小,因此想共享。


错误的答案。不需要使用DateTime.Parse,因为DateTime的构造函数需要年,月和日。new DateTime(1,1,YearNumber)
Jamiec

更新了代码以创建新的DateTime而不是使用解析。天色已晚。;)
QuinnG 2010年

2

.NET的免费时间段库包括ISO 8601符合类Week

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

2

这对我有用,它还有一个优势,可以期望使用cultureinfo作为参数来测试具有不同区域性的公式。如果为空,则获取当前的区域性信息。有效值例如:“ it”,“ en-us”,“ fr”,... ando等。技巧是减去一年中第一天的星期数,该数字可能为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

2

这是一种与Google Analytics(分析)兼容的星期编号方法,也与我们在英特尔内部使用的编号方法兼容,并且我敢肯定在许多其他情况下也会使用该编号方法。

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

2

我在上面尝试了一些代码,但有一些小错误,当您尝试不同的年份并在一周中不同的开始日期时,您会看到它们,我使用了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

1

轻轻更改了Mikael Svenson代码。我找到了第一个星期一的星期,并适当地更改了星期数。

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

0

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


1
那是一个定义,还有其他定义。
亨克·霍尔特曼

1
ISO标准将第1周定义为其中包含第一个星期四的一周。
RickardN 2012年


0

我通过重写对Thomas的解决方案做了一些改进:

   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;
      }
      */

否则,日期提前一周。

谢谢托马斯,有很大的帮助。


0

我使用了其中一种解决方案,但它给我带来了错误的结果,仅仅是因为它将星期日视为一周的第一天。

我变了:

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

现在它正在发挥魅力。


1
但这是否回答了OP的问题?是1/1加上固定的天数。没有一周的第一天概念。
Gert Arnold

0

提出的解决方案并不完整-仅适用于CalendarWeekRule.FirstFullWeek。其他类型的星期规则不起作用。使用以下测试案例可以看出这一点:

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

0

我对提出的解决方案进行了精炼,简化了firstDayOfWeek的参数:

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

0

当我们要计算给定年份,星期数和星期几的日期时,这是我的解决方案。

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

0

我简化了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));        
}

0

我已经编写并测试了以下代码,并且对我来说工作正常。如果有人对此遇到麻烦,请告诉我,我也发布了一个问题,以便获得最佳答案。有人可能会觉得有用。

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

这是错误的-至少对于许多欧洲国家而言。GetFirstDateOfWeekByWeekNumber(2020, 29)对于2020年第29周,这将返回07/20/2020。但是第29周的第一天是07/13/2020
Matthias Burger

0

当前,没有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;
    }
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.