2009-01-09 38 views
14

Theo số official (gregorian) calendar, số tuần trong ngày 29/12/2008 là 1, bởi vì sau ngày cuối cùng của tuần 52 (tức là 28/12), có ba hoặc ít ngày còn lại trong năm. Kinda lạ, nhưng OK, quy tắc là các quy tắc..NET có cho tôi số tuần sai trong ngày 29 tháng 12 năm 2008 không?

Vì vậy, theo lịch này, chúng ta có những giá trị ranh giới cho 2008/2009

  • 28/12 là tuần 52
  • 29/12 là tuần 1
  • 1/1 là tuần 1
  • 8/1 là tuần 2

C# cung cấp một lớp GregorianCalendar, mà có một chức năng GetWeekOfYear(date, rule, firstDayOfWeek).

Tham số rule là một liệt kê có 3 giá trị có thể: FirstDay, FirstFourWeekDay, FirstFullWeek. Từ những gì tôi đã hiểu tôi nên đi cho các quy tắc FirstFourWeekDay, nhưng tôi đã thử tất cả chúng chỉ trong trường hợp.

Tham số cuối cùng thông báo ngày trong tuần nào sẽ được xem là ngày đầu tiên của tuần, theo lịch đó là thứ Hai, thứ Hai đến thứ Hai.

Vì vậy, tôi bắn ra một giao diện điều khiển ứng dụng nhanh chóng và dơ bẩn để kiểm tra điều này:

using System; 
using System.Globalization; 

namespace CalendarTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var cal = new GregorianCalendar(); 
      var firstWeekDay = DayOfWeek.Monday; 
      var twentyEighth = new DateTime(2008, 12, 28); 
      var twentyNinth = new DateTime(2008, 12, 29); 
      var firstJan = new DateTime(2009, 1, 1); 
      var eightJan = new DateTime(2009, 1, 8); 
      PrintWeekDays(cal, twentyEighth, firstWeekDay); 
      PrintWeekDays(cal, twentyNinth, firstWeekDay); 
      PrintWeekDays(cal, firstJan, firstWeekDay); 
      PrintWeekDays(cal, eightJan, firstWeekDay); 
      Console.ReadKey(); 
     } 

     private static void PrintWeekDays(Calendar cal, DateTime dt, DayOfWeek firstWeekDay) 
     { 
      Console.WriteLine("Testing for " + dt.ToShortDateString()); 
      Console.WriteLine("--------------------------------------------"); 
      Console.Write(CalendarWeekRule.FirstDay.ToString() + "\t\t"); 
      Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstDay, firstWeekDay)); 
      Console.Write(CalendarWeekRule.FirstFourDayWeek.ToString() + "\t"); 
      Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFourDayWeek, firstWeekDay)); 
      Console.Write(CalendarWeekRule.FirstFullWeek.ToString() + "\t\t"); 
      Console.WriteLine(cal.GetWeekOfYear(dt, CalendarWeekRule.FirstFullWeek, firstWeekDay)); 
      Console.WriteLine("--------------------------------------------"); 
     } 
    } 
} 

... và đây là những gì tôi có được

Testing for 28.12.2008 
-------------------------------------------- 
FirstDay    52 
FirstFourDayWeek  52 
FirstFullWeek   51 
-------------------------------------------- 
Testing for 29.12.2008 
-------------------------------------------- 
FirstDay    53 
FirstFourDayWeek  53 
FirstFullWeek   52 
-------------------------------------------- 
Testing for 01.01.2009 
-------------------------------------------- 
FirstDay    1 
FirstFourDayWeek  1 
FirstFullWeek   52 
-------------------------------------------- 
Testing for 08.01.2009 
-------------------------------------------- 
FirstDay    2 
FirstFourDayWeek  2 
FirstFullWeek   1 
-------------------------------------------- 

Vì vậy, như chúng ta thấy, không ai trong số các kết hợp trên phù hợp với lịch chính thức (nếu bạn đang vội vàng, chỉ thấy rằng 29/12 không bao giờ được tuần # 1).

Tôi đang gặp vấn đề gì ở đây? Có lẽ có cái gì đó rõ ràng mà tôi đang thiếu? (đó là thứ Sáu và giờ làm việc muộn ở đây tại Bỉ, chịu với tôi;))

Edit: Có lẽ tôi nên giải thích: những gì tôi cần là một chức năng hoạt động cho bất cứ năm nào, trả lại kết quả tương tự như lịch gregorian tôi liên kết . Vì vậy, không có cách giải quyết đặc biệt nào cho năm 2008.

Trả lời

18

article này dường như ăn sâu vào vấn đề và cách giải quyết tốt. Các trung tâm của vấn đề là việc thực hiện NET lịch dường như không thực hiện một cách trung thực các tiêu chuẩn ISO

+6

hmm ... MS không tuân theo các tiêu chuẩn? Tôi bị sốc. – rmeador

+0

Tìm kiếm đầy hứa hẹn :) tiếc là tôi không thể kiểm tra điều này ngay bây giờ, nhưng tôi sẽ làm điều đó vào ngày mai. – rodbv

+0

rmeador: từ những gì tôi đã thấy Java có cùng một vấn đề ... – rodbv

1

Theo kinh nghiệm của tôi, hành vi được chứng minh là hành vi điển hình, tham khảo tuần cuối cùng một phần như tuần 53. Điều này có thể là do tất cả các số liệu tuần có liên quan để tính đến cuối năm dương lịch cho mục đích báo cáo và IRS (hoặc cơ quan thuế mà bạn chọn) xem xét năm dương lịch kết thúc vào ngày 31 tháng 12, không phải là tuần cuối cùng của năm.

2

Số tuần khác nhau giữa các quốc gia và phải phụ thuộc vào cài đặt ngôn ngữ/khu vực của bạn nếu tôi không hoàn toàn sai.

Edit: Wikipedia hỗ trợ hồi ức mơ hồ của tôi rằng những con số khác nhau dựa vào quốc gia: http://en.wikipedia.org/wiki/Week_number#Week_number

Tôi mong chờ một khung kính để tuân thủ quốc gia đã chọn trong thời gian chạy địa phương của bạn.

+0

Hm, có thể được. Tôi sẽ thấy nếu tôi có được những điều đúng nếu tôi sửa chữa nó trên en- US cultureinfo. – rodbv

+0

Đã thực hiện một số bài kiểm tra và đọc một số MSDN: văn hóa được sử dụng để thông báo cho các tham số thứ 2 và thứ 3 của GetWeekOfYear, quy tắc và ngày đầu tiên. Nhưng vì tôi đang cung cấp những thứ đó, nền văn hóa trở nên không liên quan. – rodbv

-1

Là một công việc xung quanh, bạn có thể nói số tuần là TuầnNumber mod 52. Tôi tin rằng điều này sẽ làm việc cho các trường hợp bạn mô tả.

+0

Nó có thể giải quyết cho trường hợp này nhưng tôi cần một thuật toán chung – rodbv

+1

và bên cạnh đó, có nhiều năm với 53 tuần, ví dụ 2009 – rodbv

-1

Là một workaround, tại sao không sử dụng FirstFourDayWeek nhưng thêm:

if (weekNumber > 52) 
    weekNumber = 1; 
+1

Tôi cần một thuật toán chung..có những trường hợp năm đó chính thức là 53 tuần (2009 là như thế, IIRC) – rodbv

3

@Conrad là chính xác. Việc triển khai .NET của DateTime và GregorianCalendar không thực hiện/tuân thủ đầy đủ thông số ISO 8601. Điều đó đang được nói, họ spec là cực kỳ chi tiết và không tầm thường để thực hiện đầy đủ, ít nhất là cho phía phân tích của sự vật.

Một số biết thêm thông tin có sẵn tại địa điểm sau:

một cách đơn giản:

Một tuần được xác định bởi số lượng của nó trong một năm nhất định và bắt đầu bằng Thứ Hai. Tuần lễ đầu tiên của một năm là một trong đó bao gồm các Thứ Năm đầu tiên, hoặc tương đương với một trong đó bao gồm tháng một 4.

Đây là một phần của mã tôi sử dụng để xử lý đúng theo tiêu chuẩn ISO 8601 ngày:

#region FirstWeekOfYear 
    /// <summary> 
    /// Gets the first week of the year. 
    /// </summary> 
    /// <param name="year">The year to retrieve the first week of.</param> 
    /// <returns>A <see cref="DateTime"/>representing the start of the first 
    /// week of the year.</returns> 
    /// <remarks> 
    /// Week 01 of a year is per definition the first week that has the Thursday 
    /// in this year, which is equivalent to the week that contains the fourth 
    /// day of January. In other words, the first week of a new year is the week 
    /// that has the majority of its days in the new year. Week 01 might also 
    /// contain days from the previous year and the week before week 01 of a year 
    /// is the last week (52 or 53) of the previous year even if it contains days 
    /// from the new year. 
    /// A week starts with Monday (day 1) and ends with Sunday (day 7). 
    /// </remarks> 
    private static DateTime FirstWeekOfYear(int year) 
    { 
     int dayNumber; 

     // Get the date that represents the fourth day of January for the given year. 
     DateTime date = new DateTime(year, 1, 4, 0, 0, 0, DateTimeKind.Utc); 

     // A week starts with Monday (day 1) and ends with Sunday (day 7). 
     // Since DayOfWeek.Sunday = 0, translate it to 7. All of the other values 
     // are correct since DayOfWeek.Monday = 1. 
     if (date.DayOfWeek == DayOfWeek.Sunday) 
     { 
      dayNumber = 7; 
     } 
     else 
     { 
      dayNumber = (int)date.DayOfWeek; 
     } 

     // Since the week starts with Monday, figure out what day that 
     // Monday falls on. 
     return date.AddDays(1 - dayNumber); 
    } 

    #endregion 

    #region GetIsoDate 
    /// <summary> 
    /// Gets the ISO date for the specified <see cref="DateTime"/>. 
    /// </summary> 
    /// <param name="date">The <see cref="DateTime"/> for which the ISO date 
    /// should be calculated.</param> 
    /// <returns>An <see cref="Int32"/> representing the ISO date.</returns> 
    private static int GetIsoDate(DateTime date) 
    { 
     DateTime firstWeek; 
     int year = date.Year; 

     // If we are near the end of the year, then we need to calculate 
     // what next year's first week should be. 
     if (date >= new DateTime(year, 12, 29)) 
     { 
      if (date == DateTime.MaxValue) 
      { 
       firstWeek = FirstWeekOfYear(year); 
      } 
      else 
      { 
       firstWeek = FirstWeekOfYear(year + 1); 
      } 

      // If the current date is less than next years first week, then 
      // we are still in the last month of the current year; otherwise 
      // change to next year. 
      if (date < firstWeek) 
      { 
       firstWeek = FirstWeekOfYear(year); 
      } 
      else 
      { 
       year++; 
      } 
     } 
     else 
     { 
      // We aren't near the end of the year, so make sure 
      // we're not near the beginning. 
      firstWeek = FirstWeekOfYear(year); 

      // If the current date is less than the current years 
      // first week, then we are in the last month of the 
      // previous year. 
      if (date < firstWeek) 
      { 
       if (date == DateTime.MinValue) 
       { 
        firstWeek = FirstWeekOfYear(year); 
       } 
       else 
       { 
        firstWeek = FirstWeekOfYear(--year); 
       } 
      } 
     } 

     // return the ISO date as a numeric value, so it makes it 
     // easier to get the year and the week. 
     return (year * 100) + ((date - firstWeek).Days/7 + 1); 
    } 

    #endregion 

    #region Week 
    /// <summary> 
    /// Gets the week component of the date represented by this instance. 
    /// </summary> 
    /// <value>The week, between 1 and 53.</value> 
    public int Week 
    { 
     get 
     { 
      return this.isoDate % 100; 
     } 
    } 
    #endregion 

    #region Year 
    /// <summary> 
    /// Gets the year component of the date represented by this instance. 
    /// </summary> 
    /// <value>The year, between 1 and 9999.</value> 
    public int Year 
    { 
     get 
     { 
      return this.isoDate/100; 
     } 
    } 
    #endregion 
0

Tôi biết đây là một bài viết cũ, nhưng bất kỳ cách nào thời gian noda dường như để có được kết quả chính xác ..

Các vấn đề liên quan