คำนวณวันที่จากหมายเลขสัปดาห์


137

ใครรู้วิธีง่ายๆในการหาวันที่ของวันแรกในสัปดาห์ (วันจันทร์ที่นี่ในยุโรป) ฉันรู้ปีและเลขสัปดาห์? ผมจะทำใน C #


เกี่ยวข้องอย่างยิ่งอาจจะซ้ำกัน: stackoverflow.com/questions/659183/…
John Rasch

คำตอบ:


258

ฉันมีปัญหากับการแก้ปัญหาโดย @HenkHolterman แม้จะแก้ไขโดย @RobinAndersson

การอ่านมาตรฐาน ISO 8601 ช่วยแก้ปัญหาได้ดี ใช้วันพฤหัสบดีแรกเป็นเป้าหมายไม่ใช่วันจันทร์ โค้ดด้านล่างจะใช้ได้ในสัปดาห์ที่ 53 ของปี 2009 เช่นกัน

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
ฉันได้ลองวิธีแก้ปัญหาเกือบทุกวิธีที่ให้ไว้ที่นี่อันนี้เป็นวิธีเดียวที่ใช้ได้กับฉันในตอนนี้ ปัจจุบันเป็นวันที่ 7 กุมภาพันธ์ 2555 สัปดาห์ที่แล้วคือ # 6 รหัสนี้ให้วันที่ 6 กุมภาพันธ์เป็นวันที่เริ่มต้นของสัปดาห์อย่างถูกต้อง วิธีแก้ปัญหาอื่น ๆ ทั้งหมดให้ฉันในวันที่ 13 กุมภาพันธ์ซึ่งเป็นวันที่เริ่มต้นของสัปดาห์ที่ 7
HaukurHaf

1
ใช้งานได้เหมือนมีเสน่ห์ .. ผ่านการทดสอบข้อมูลบางส่วน: pastebin.com/mfx8s1vq ทุกงานไร้ที่ติ ขอบคุณมิคาเอล!
Mittchel

1
@ RobinWassén-Andersson สิ่งที่ดีที่คุณกลับมาถามคำถามนี้ในตอนนั้น: D อีก 6 คะแนนและฉันจะผูกกับคำตอบที่ "ไม่" ฮิฮิ
Mikael Svenson

2
คำแนะนำ: สร้าง "ISO8601" ให้เป็นชื่อวิธีการเนื่องจากไม่มีคำตอบที่ 'ถูกต้อง' ที่เป็นสากล
Henk Holterman

1
@Muflix ฉันไม่รู้เกี่ยวกับ SQL และวันที่ / ปฏิทินมากพอที่จะรู้ - แต่การทำ CLR จะได้ผล
Mikael Svenson

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 เป็นวิธีการขยายในปฏิทินและ DateTimeFormatInfo ฉันยังอ้างถึงคำตอบนี้สำหรับเครดิต
Atif Aziz

1
เครื่องของฉันทำงานไม่ถูกต้อง มันแสดงวันที่ด้วย 0010 แทนที่จะเป็น 2010 ฉันไม่รู้ว่ามันเป็นปัญหาใน. net framework หรือในฟังก์ชันนี้ ลองดีต่อไป ...
Eduardo Xavier

6
firstMondayเป็นชื่อตัวแปรที่ไม่ถูกต้องสำหรับบางสิ่งที่อาจไม่ใช่วันจันทร์ =)
mflodin

14

UPDATE : .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 ของปีก่อนหน้า
Henk Holterman

1
ขึ้นอยู่กับว่าคุณต้องการกำหนดสิ่งต่างๆอย่างไร น่าเสียดายที่เราไม่มีข้อมูลมากมายให้ไปที่นี่ ... ฉันหวังว่านี่จะเป็นประโยชน์
Jon Skeet

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


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


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 และรูปแบบการกำหนดหมายเลขเดียวกันกับที่เราใช้ภายในที่ Intel และฉันแน่ใจว่าใช้ในบริบทอื่น ๆ มากมาย

// 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
นั่นคือคำจำกัดความหนึ่งมีคำอื่น ๆ
Henk Holterman

1
มาตรฐาน ISO กำหนดให้สัปดาห์ที่ 1 เป็นสัปดาห์โดยมีวันพฤหัสบดีแรกของปีอยู่ในนั้น
RickardN


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)สำหรับสัปดาห์ที่ 29 ของปี 2020 ผลตอบแทน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;
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.