Tính ngày từ số tuần


135

Bất cứ ai cũng biết một cách dễ dàng để có được ngày của ngày đầu tiên trong tuần (thứ hai ở đây tại châu Âu). Tôi biết số năm và số tuần? Tôi sẽ làm điều này trong C #.


Liên quan chặt chẽ, có lẽ trùng lặp: stackoverflow.com/questions/659183/ Kẻ
John Rasch

Câu trả lời:


251

Tôi đã gặp vấn đề với giải pháp của @HenkHolterman ngay cả với bản sửa lỗi của @RobinAndersson.

Đọc lên tiêu chuẩn ISO 8601 giải quyết vấn đề độc đáo. Sử dụng thứ Năm đầu tiên làm mục tiêu và không phải thứ Hai. Mã dưới đây cũng sẽ hoạt động cho Tuần 53 năm 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
Tôi đã thử hầu hết mọi giải pháp được đưa ra ở đây, đây là giải pháp duy nhất phù hợp với tôi ngay bây giờ. Hiện tại, đó là ngày 7 tháng 2 năm 2012. Tuần thứ nr là # 6. Mã này chính xác cho tôi ngày 6 tháng 2 là ngày bắt đầu trong tuần. Các giải pháp khác đều cho tôi ngày 13 tháng 2, đây thực sự là ngày bắt đầu của tuần # 7.
HaukurHaf

1
Hoạt động như một bùa mê .. đã thử nghiệm một vài dữ liệu: pastebin.com/mfx8s1vq Tất cả đều hoạt động hoàn hảo! Cảm ơn Mikael!
Mittchel

1
@ RobinWassén-Andersson Điều tốt là bạn đã xem lại câu hỏi này sau đó: D thêm 6 phiếu và tôi sẽ buộc với câu trả lời "không" đúng hehe.
Mikael Svenson

2
Gợi ý: Tạo "ISO8601" pert của tên phương thức, vì đơn giản là không có câu trả lời 'chính xác' phổ quát.
Henk Holterman

1
@Muflix Tôi không biết đủ về SQL và ngày / lịch cần biết - nhưng thực hiện CLR sẽ hoạt động.
Mikael Svenson

36

Tôi thích giải pháp được cung cấp bởi Henk Holterman. Nhưng để độc lập hơn một chút về văn hóa, bạn phải có được ngày đầu tuần cho văn hóa hiện tại (không phải lúc nào cũng là thứ hai):

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

Tôi đã thêm cái này vào Mannex, như một phương thức mở rộng cho Lịch và DateTimeFormatInfo. Tôi cũng đã tham khảo chéo cho câu trả lời này cho tín dụng.
Atif Aziz

1
Nó không hoạt động đúng trên máy của tôi. Nó hiển thị một ngày với 0010 thay vì năm 2010. Tôi không biết liệu đó có phải là sự cố trong khung .net hoặc trong chức năng này không. Dù sao thì cũng cố gắng ...
Eduardo Xavier

6
firstMondaylà một tên biến xấu cho một cái gì đó có thể không phải là thứ Hai. =)
mflodin

11

CẬP NHẬT : .NET Core 3.0 và .NET Standard 2.1 đã xuất xưởng với loại này.

Tin tốt! Một yêu cầu kéo thêmSystem.Globalization.ISOWeek vào .NET Core vừa được hợp nhất và hiện đang được dự kiến ​​phát hành 3.0. Hy vọng rằng nó sẽ lan truyền đến các nền tảng .NET khác trong một tương lai không xa.

Bạn sẽ có thể sử dụng ISOWeek.ToDateTime(int year, int week, DayOfWeek dayOfWeek) phương pháp để tính toán này.

Bạn có thể tìm thấy mã nguồn ở đây .


9

Cách dễ nhất có lẽ là tìm thứ Hai đầu tiên của năm, sau đó thêm số tuần có liên quan. Đây là một số mã mẫu. Nó giả sử số tuần bắt đầu từ 1, nhân tiện:

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
Đúng, nhưng thứ hai đầu tiên của năm có thể thuộc về tuần 52 | 53 của năm trước.
Henk Holterman

1
Nó phụ thuộc vào cách bạn muốn xác định mọi thứ. Thật không may, chúng tôi không có nhiều thông tin để tiếp tục ở đây ... Tôi hy vọng điều này hữu ích.
Jon Skeet

3

Cá nhân tôi sẽ tận dụng thông tin văn hóa để có được ngày trong tuần và lặp lại ngày đầu tiên của văn hóa. Tôi không chắc liệu tôi có giải thích đúng không, đây là một ví dụ:

    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

sử dụng 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

Theo ISO 8601: 1988 được sử dụng ở Thụy Điển tuần đầu tiên của năm là tuần đầu tiên có ít nhất bốn ngày trong năm mới.

Vì vậy, nếu tuần của bạn bắt đầu vào thứ Hai, thứ Năm đầu tiên, bất kỳ năm nào cũng nằm trong tuần đầu tiên. Bạn có thể DateAdd hoặc DateDiff từ đó.


2

Giả sử số tuần bắt đầu từ 1

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

Điều này sẽ cung cấp cho bạn ngày đầu tiên trong bất kỳ tuần nào. Tôi đã không thực hiện nhiều thử nghiệm về nó, nhưng có vẻ như nó hoạt động. Đó là giải pháp nhỏ hơn hầu hết các giải pháp khác mà tôi tìm thấy trên web, vì vậy muốn chia sẻ.


câu trả lời không hay. Không cần sử dụng DateTime.Pude vì DateTime có một hàm tạo mất năm, tháng & ngày. new DateTime(1,1,YearNumber)
Jamiec

Đã cập nhật mã để tạo DateTime mới thay vì sử dụng phân tích cú pháp. Muộn rồi. ;)
QuinnG

2

Thư viện khoảng thời gian miễn phí cho .NET bao gồm Tuần lớp phù hợp ISO 8601 :

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

2

Điều này làm việc cho tôi, nó cũng có lợi thế khi mong đợi một văn hóa làm thông số để kiểm tra công thức với các nền văn hóa khác nhau. Nếu trống, nó nhận được thông tin văn hóa hiện tại ... các giá trị hợp lệ như: "nó", "en-us", "fr", ... và cứ thế. Bí quyết là trừ đi số tuần của ngày đầu tiên của năm, có thể là 1 để chỉ ra rằng ngày đầu tiên nằm trong tuần đầu tiên. Hi vọng điêu nay co ich.

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

Đây là một phương pháp tương thích với các số tuần mà Google Analytics và cũng là sơ đồ đánh số tương tự mà chúng tôi đã sử dụng trong Intel và tôi chắc chắn cũng được sử dụng trong nhiều bối cảnh khác.

// 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

Tôi đã thử một số mã ở trên và một số có lỗi nhỏ, khi bạn thử các năm khác nhau với các ngày bắt đầu khác nhau trong tuần bạn sẽ thấy chúng, tôi đã lấy mã của Jon Skeet, sửa nó và nó hoạt động, mã rất đơn giản.

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

Thay đổi nhẹ mã Mikael Svenson. Tôi tìm thấy tuần của thứ hai đầu tiên và thay đổi số tuần thích hợp.

 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

Tuần 1 được định nghĩa là tuần bắt đầu vào thứ Hai và chứa thứ Năm đầu tiên của năm.


1
Đó là một định nghĩa, có những định nghĩa khác.
Henk Holterman

1
Tiêu chuẩn ISO xác định tuần 1 là tuần có thứ năm đầu tiên trong năm.
RickardN


0

Tôi đã cải thiện một chút về giải pháp của Thomas bằng cách ghi đè:

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

Nếu không thì ngày trước một tuần ..

Cảm ơn Thomas, giúp đỡ rất nhiều.


0

Tôi đã sử dụng một trong những giải pháp nhưng nó cho tôi kết quả sai, đơn giản vì nó tính Chủ nhật là ngày đầu tuần.

Tôi đã thay đổi:

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

đến:

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

và bây giờ nó đang làm việc như một lá bùa.


1
Nhưng điều này có trả lời câu hỏi của OP không? Đó là 1/1 cộng với một số ngày cố định. Không có ngày đầu tiên của khái niệm tuần.
Gert Arnold

0

Giải pháp đề xuất chưa hoàn tất - nó chỉ hoạt động cho CalendarWeekRule.FirstFullWeek. Các loại quy tắc tuần khác không hoạt động. Điều này có thể được nhìn thấy bằng cách sử dụng trường hợp thử nghiệm này:

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

Tôi đã thực hiện một phiên bản tinh chỉnh của giải pháp được đề xuất đơn giản hơn và tham số hóa đầu tiênDayOfWeek:

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

đây là giải pháp của tôi khi chúng tôi muốn tính một ngày nhất định, số tuần và ngày trong tuần.

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

Tôi đã đơn giản hóa mã Mikael Svensson cung cấp chính xác cho nhiều quốc gia ở châu Âu.

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

Tôi đã viết và kiểm tra mã sau đây và đang làm việc hoàn toàn tốt cho tôi. Xin vui lòng cho tôi biết nếu có ai gặp rắc rối với điều này, tôi cũng đã đăng một câu hỏi để có được câu trả lời tốt nhất có thể. Ai đó có thể thấy nó hữu ích.

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

Điều này là sai - ít nhất là đối với rất nhiều nước châu Âu. GetFirstDateOfWeekByWeekNumber(2020, 29)cho tuần 29 năm 2020 này trở lại 07/20/2020. Nhưng ngày đầu tiên của tuần thứ 29 là07/13/2020
Matthias Burger

0

Hiện tại, không có lớp C # nào xử lý chính xác các số ISO 8601week. Mặc dù bạn có thể khởi tạo một nền văn hóa, hãy tìm thứ gần nhất và sửa nó, tôi nghĩ tốt hơn là tự mình thực hiện phép tính hoàn chỉnh:

    /// <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;
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.