]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
Fix dates formatting `Y`, `w` and `W` symbols for week-numbering (#1179)
authorJun Omae (大前 潤) <42682+jun66j5@users.noreply.github.com>
Fri, 31 Jan 2025 13:22:06 +0000 (22:22 +0900)
committerGitHub <noreply@github.com>
Fri, 31 Jan 2025 13:22:06 +0000 (15:22 +0200)
babel/dates.py
tests/test_dates.py

index 8a4932d4bca2c3fea5e6f5cf9b90064b6475d561..d060fdef1384820adcea15fef9f0229222cbedf3 100644 (file)
@@ -1482,7 +1482,11 @@ class DateTimeFormat:
     def format_year(self, char: str, num: int) -> str:
         value = self.value.year
         if char.isupper():
-            value = self.value.isocalendar()[0]
+            month = self.value.month
+            if month == 1 and self.value.day < 7 and self.get_week_of_year() >= 52:
+                value -= 1
+            elif month == 12 and self.value.day > 25 and self.get_week_of_year() <= 2:
+                value += 1
         year = self.format(value, num)
         if num == 2:
             year = year[-2:]
@@ -1505,18 +1509,10 @@ class DateTimeFormat:
 
     def format_week(self, char: str, num: int) -> str:
         if char.islower():  # week of year
-            day_of_year = self.get_day_of_year()
-            week = self.get_week_number(day_of_year)
-            if week == 0:
-                date = self.value - datetime.timedelta(days=day_of_year)
-                week = self.get_week_number(self.get_day_of_year(date),
-                                            date.weekday())
+            week = self.get_week_of_year()
             return self.format(week, num)
         else:  # week of month
-            week = self.get_week_number(self.value.day)
-            if week == 0:
-                date = self.value - datetime.timedelta(days=self.value.day)
-                week = self.get_week_number(date.day, date.weekday())
+            week = self.get_week_of_month()
             return str(week)
 
     def format_weekday(self, char: str = 'E', num: int = 4) -> str:
@@ -1677,6 +1673,25 @@ class DateTimeFormat:
             date = self.value
         return (date - date.replace(month=1, day=1)).days + 1
 
+    def get_week_of_year(self) -> int:
+        """Return the week of the year."""
+        day_of_year = self.get_day_of_year(self.value)
+        week = self.get_week_number(day_of_year)
+        if week == 0:
+            date = datetime.date(self.value.year - 1, 12, 31)
+            week = self.get_week_number(self.get_day_of_year(date),
+                                        date.weekday())
+        elif week > 52:
+            weekday = datetime.date(self.value.year + 1, 1, 1).weekday()
+            if self.get_week_number(1, weekday) == 1 and \
+                    32 - (weekday - self.locale.first_week_day) % 7 <= self.value.day:
+                week = 1
+        return week
+
+    def get_week_of_month(self) -> int:
+        """Return the week of the month."""
+        return self.get_week_number(self.value.day)
+
     def get_week_number(self, day_of_period: int, day_of_week: int | None = None) -> int:
         """Return the number of the week of a day within a period. This may be
         the week number in a year or the week number in a month.
@@ -1703,20 +1718,8 @@ class DateTimeFormat:
         if first_day < 0:
             first_day += 7
         week_number = (day_of_period + first_day - 1) // 7
-
         if 7 - first_day >= self.locale.min_week_days:
             week_number += 1
-
-        if self.locale.first_week_day == 0:
-            # Correct the weeknumber in case of iso-calendar usage (first_week_day=0).
-            # If the weeknumber exceeds the maximum number of weeks for the given year
-            # we must count from zero.For example the above calculation gives week 53
-            # for 2018-12-31. By iso-calender definition 2018 has a max of 52
-            # weeks, thus the weeknumber must be 53-52=1.
-            max_weeks = datetime.date(year=self.value.year, day=28, month=12).isocalendar()[1]
-            if week_number > max_weeks:
-                week_number -= max_weeks
-
         return week_number
 
 
index bc6107f39baedaf93b5032d888da631c4b070d3a..18bf43430cdb2bac47fc8116e66262b1bae3cce2 100644 (file)
@@ -785,6 +785,365 @@ def test_russian_week_numbering():
     assert dates.format_date(v, format='YYYY-ww', locale='de_DE') == '2016-52'
 
 
+def test_week_numbering_isocalendar():
+    locale = Locale.parse('de_DE')
+    assert locale.first_week_day == 0
+    assert locale.min_week_days == 4
+
+    def week_number(value):
+        return dates.format_date(value, format="YYYY-'W'ww-e", locale=locale)
+
+    for year in range(1991, 2010):
+        for delta in range(-9, 9):
+            value = date(year, 1, 1) + timedelta(days=delta)
+            expected = '%04d-W%02d-%d' % value.isocalendar()
+            assert week_number(value) == expected
+
+def test_week_numbering_monday_mindays_4():
+    locale = Locale.parse('de_DE')
+    assert locale.first_week_day == 0
+    assert locale.min_week_days == 4
+
+    def week_number(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format="YYYY-'W'ww-e", locale=locale)
+
+    def week_of_month(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format='W', locale=locale)
+
+    assert week_number('2003-01-01') == '2003-W01-3'
+    assert week_number('2003-01-06') == '2003-W02-1'
+    assert week_number('2003-12-28') == '2003-W52-7'
+    assert week_number('2003-12-29') == '2004-W01-1'
+    assert week_number('2003-12-31') == '2004-W01-3'
+    assert week_number('2004-01-01') == '2004-W01-4'
+    assert week_number('2004-01-05') == '2004-W02-1'
+    assert week_number('2004-12-26') == '2004-W52-7'
+    assert week_number('2004-12-27') == '2004-W53-1'
+    assert week_number('2004-12-31') == '2004-W53-5'
+    assert week_number('2005-01-01') == '2004-W53-6'
+    assert week_number('2005-01-03') == '2005-W01-1'
+    assert week_number('2005-01-10') == '2005-W02-1'
+    assert week_number('2005-12-31') == '2005-W52-6'
+    assert week_number('2006-01-01') == '2005-W52-7'
+    assert week_number('2006-01-02') == '2006-W01-1'
+    assert week_number('2006-01-09') == '2006-W02-1'
+    assert week_number('2006-12-31') == '2006-W52-7'
+    assert week_number('2007-01-01') == '2007-W01-1'
+    assert week_number('2007-12-30') == '2007-W52-7'
+    assert week_number('2007-12-31') == '2008-W01-1'
+    assert week_number('2008-01-01') == '2008-W01-2'
+    assert week_number('2008-01-07') == '2008-W02-1'
+    assert week_number('2008-12-28') == '2008-W52-7'
+    assert week_number('2008-12-29') == '2009-W01-1'
+    assert week_number('2008-12-31') == '2009-W01-3'
+    assert week_number('2009-01-01') == '2009-W01-4'
+    assert week_number('2009-01-05') == '2009-W02-1'
+    assert week_number('2009-12-27') == '2009-W52-7'
+    assert week_number('2009-12-28') == '2009-W53-1'
+    assert week_number('2009-12-31') == '2009-W53-4'
+    assert week_number('2010-01-01') == '2009-W53-5'
+    assert week_number('2010-01-03') == '2009-W53-7'
+    assert week_number('2010-01-04') == '2010-W01-1'
+
+    assert week_of_month('2003-01-01') == '1'  # Wed
+    assert week_of_month('2003-02-28') == '4'
+    assert week_of_month('2003-05-01') == '1'  # Thu
+    assert week_of_month('2003-08-01') == '0'  # Fri
+    assert week_of_month('2003-12-31') == '5'
+    assert week_of_month('2004-01-01') == '1'  # Thu
+    assert week_of_month('2004-02-29') == '4'
+
+
+def test_week_numbering_monday_mindays_1():
+    locale = Locale.parse('tr_TR')
+    assert locale.first_week_day == 0
+    assert locale.min_week_days == 1
+
+    def week_number(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format="YYYY-'W'ww-e", locale=locale)
+
+    def week_of_month(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format='W', locale=locale)
+
+    assert week_number('2003-01-01') == '2003-W01-3'
+    assert week_number('2003-01-06') == '2003-W02-1'
+    assert week_number('2003-12-28') == '2003-W52-7'
+    assert week_number('2003-12-29') == '2004-W01-1'
+    assert week_number('2003-12-31') == '2004-W01-3'
+    assert week_number('2004-01-01') == '2004-W01-4'
+    assert week_number('2004-01-05') == '2004-W02-1'
+    assert week_number('2004-12-26') == '2004-W52-7'
+    assert week_number('2004-12-27') == '2005-W01-1'
+    assert week_number('2004-12-31') == '2005-W01-5'
+    assert week_number('2005-01-01') == '2005-W01-6'
+    assert week_number('2005-01-03') == '2005-W02-1'
+    assert week_number('2005-12-25') == '2005-W52-7'
+    assert week_number('2005-12-26') == '2006-W01-1'
+    assert week_number('2005-12-31') == '2006-W01-6'
+    assert week_number('2006-01-01') == '2006-W01-7'
+    assert week_number('2006-01-02') == '2006-W02-1'
+    assert week_number('2006-12-31') == '2006-W53-7'
+    assert week_number('2007-01-01') == '2007-W01-1'
+    assert week_number('2007-01-08') == '2007-W02-1'
+    assert week_number('2007-12-30') == '2007-W52-7'
+    assert week_number('2007-12-31') == '2008-W01-1'
+    assert week_number('2008-01-01') == '2008-W01-2'
+    assert week_number('2008-01-07') == '2008-W02-1'
+    assert week_number('2008-12-28') == '2008-W52-7'
+    assert week_number('2008-12-29') == '2009-W01-1'
+    assert week_number('2008-12-31') == '2009-W01-3'
+    assert week_number('2009-01-01') == '2009-W01-4'
+    assert week_number('2009-01-05') == '2009-W02-1'
+    assert week_number('2009-12-27') == '2009-W52-7'
+    assert week_number('2009-12-28') == '2010-W01-1'
+    assert week_number('2009-12-31') == '2010-W01-4'
+    assert week_number('2010-01-01') == '2010-W01-5'
+    assert week_number('2010-01-03') == '2010-W01-7'
+    assert week_number('2010-01-04') == '2010-W02-1'
+
+    assert week_of_month('2003-01-01') == '1'  # Wed
+    assert week_of_month('2003-02-28') == '5'
+    assert week_of_month('2003-06-01') == '1'  # Sun
+    assert week_of_month('2003-12-31') == '5'
+    assert week_of_month('2004-01-01') == '1'  # Thu
+    assert week_of_month('2004-02-29') == '5'
+
+
+def test_week_numbering_sunday_mindays_1():
+    locale = Locale.parse('en_US')
+    assert locale.first_week_day == 6
+    assert locale.min_week_days == 1
+
+    def week_number(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format="YYYY-'W'ww-e", locale=locale)
+
+    def week_of_month(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format='W', locale=locale)
+
+    assert week_number('2003-01-01') == '2003-W01-4'
+    assert week_number('2003-01-05') == '2003-W02-1'
+    assert week_number('2003-12-27') == '2003-W52-7'
+    assert week_number('2003-12-28') == '2004-W01-1'
+    assert week_number('2003-12-31') == '2004-W01-4'
+    assert week_number('2004-01-01') == '2004-W01-5'
+    assert week_number('2004-01-04') == '2004-W02-1'
+    assert week_number('2004-12-25') == '2004-W52-7'
+    assert week_number('2004-12-26') == '2005-W01-1'
+    assert week_number('2004-12-31') == '2005-W01-6'
+    assert week_number('2005-01-01') == '2005-W01-7'
+    assert week_number('2005-01-02') == '2005-W02-1'
+    assert week_number('2005-12-24') == '2005-W52-7'
+    assert week_number('2005-12-25') == '2005-W53-1'
+    assert week_number('2005-12-31') == '2005-W53-7'
+    assert week_number('2006-01-01') == '2006-W01-1'
+    assert week_number('2006-01-08') == '2006-W02-1'
+    assert week_number('2006-12-30') == '2006-W52-7'
+    assert week_number('2006-12-31') == '2007-W01-1'
+    assert week_number('2007-01-01') == '2007-W01-2'
+    assert week_number('2007-01-07') == '2007-W02-1'
+    assert week_number('2007-12-29') == '2007-W52-7'
+    assert week_number('2007-12-30') == '2008-W01-1'
+    assert week_number('2007-12-31') == '2008-W01-2'
+    assert week_number('2008-01-01') == '2008-W01-3'
+    assert week_number('2008-01-06') == '2008-W02-1'
+    assert week_number('2008-12-27') == '2008-W52-7'
+    assert week_number('2008-12-28') == '2009-W01-1'
+    assert week_number('2008-12-31') == '2009-W01-4'
+    assert week_number('2009-01-01') == '2009-W01-5'
+    assert week_number('2009-01-04') == '2009-W02-1'
+    assert week_number('2009-12-26') == '2009-W52-7'
+    assert week_number('2009-12-27') == '2010-W01-1'
+    assert week_number('2009-12-31') == '2010-W01-5'
+    assert week_number('2010-01-01') == '2010-W01-6'
+    assert week_number('2010-01-03') == '2010-W02-1'
+
+    assert week_of_month('2003-01-01') == '1'  # Wed
+    assert week_of_month('2003-02-01') == '1'  # Sat
+    assert week_of_month('2003-02-28') == '5'
+    assert week_of_month('2003-12-31') == '5'
+    assert week_of_month('2004-01-01') == '1'  # Thu
+    assert week_of_month('2004-02-29') == '5'
+
+
+def test_week_numbering_sunday_mindays_4():
+    locale = Locale.parse('pt_PT')
+    assert locale.first_week_day == 6
+    assert locale.min_week_days == 4
+
+    def week_number(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format="YYYY-'W'ww-e", locale=locale)
+
+    def week_of_month(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format='W', locale=locale)
+
+    assert week_number('2003-01-01') == '2003-W01-4'
+    assert week_number('2003-01-05') == '2003-W02-1'
+    assert week_number('2003-12-27') == '2003-W52-7'
+    assert week_number('2003-12-28') == '2003-W53-1'
+    assert week_number('2003-12-31') == '2003-W53-4'
+    assert week_number('2004-01-01') == '2003-W53-5'
+    assert week_number('2004-01-04') == '2004-W01-1'
+    assert week_number('2004-12-25') == '2004-W51-7'
+    assert week_number('2004-12-26') == '2004-W52-1'
+    assert week_number('2004-12-31') == '2004-W52-6'
+    assert week_number('2005-01-01') == '2004-W52-7'
+    assert week_number('2005-01-02') == '2005-W01-1'
+    assert week_number('2005-12-24') == '2005-W51-7'
+    assert week_number('2005-12-25') == '2005-W52-1'
+    assert week_number('2005-12-31') == '2005-W52-7'
+    assert week_number('2006-01-01') == '2006-W01-1'
+    assert week_number('2006-01-08') == '2006-W02-1'
+    assert week_number('2006-12-30') == '2006-W52-7'
+    assert week_number('2006-12-31') == '2007-W01-1'
+    assert week_number('2007-01-01') == '2007-W01-2'
+    assert week_number('2007-01-07') == '2007-W02-1'
+    assert week_number('2007-12-29') == '2007-W52-7'
+    assert week_number('2007-12-30') == '2008-W01-1'
+    assert week_number('2007-12-31') == '2008-W01-2'
+    assert week_number('2008-01-01') == '2008-W01-3'
+    assert week_number('2008-01-06') == '2008-W02-1'
+    assert week_number('2008-12-27') == '2008-W52-7'
+    assert week_number('2008-12-28') == '2008-W53-1'
+    assert week_number('2008-12-31') == '2008-W53-4'
+    assert week_number('2009-01-01') == '2008-W53-5'
+    assert week_number('2009-01-04') == '2009-W01-1'
+    assert week_number('2009-12-26') == '2009-W51-7'
+    assert week_number('2009-12-27') == '2009-W52-1'
+    assert week_number('2009-12-31') == '2009-W52-5'
+    assert week_number('2010-01-01') == '2009-W52-6'
+    assert week_number('2010-01-03') == '2010-W01-1'
+
+    assert week_of_month('2003-01-01') == '1'  # Wed
+    assert week_of_month('2003-02-28') == '4'
+    assert week_of_month('2003-05-01') == '0'  # Thu
+    assert week_of_month('2003-12-31') == '5'
+    assert week_of_month('2004-01-01') == '0'  # Thu
+    assert week_of_month('2004-02-29') == '5'
+
+
+def test_week_numbering_friday_mindays_1():
+    locale = Locale.parse('dv_MV')
+    assert locale.first_week_day == 4
+    assert locale.min_week_days == 1
+
+    def week_number(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format="YYYY-'W'ww-e", locale=locale)
+
+    def week_of_month(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format='W', locale=locale)
+
+    assert week_number('2003-01-01') == '2003-W01-6'
+    assert week_number('2003-01-03') == '2003-W02-1'
+    assert week_number('2003-12-25') == '2003-W52-7'
+    assert week_number('2003-12-26') == '2004-W01-1'
+    assert week_number('2003-12-31') == '2004-W01-6'
+    assert week_number('2004-01-01') == '2004-W01-7'
+    assert week_number('2004-01-02') == '2004-W02-1'
+    assert week_number('2004-12-23') == '2004-W52-7'
+    assert week_number('2004-12-24') == '2004-W53-1'
+    assert week_number('2004-12-30') == '2004-W53-7'
+    assert week_number('2004-12-31') == '2005-W01-1'
+    assert week_number('2005-01-01') == '2005-W01-2'
+    assert week_number('2005-01-07') == '2005-W02-1'
+    assert week_number('2005-12-29') == '2005-W52-7'
+    assert week_number('2005-12-30') == '2006-W01-1'
+    assert week_number('2005-12-31') == '2006-W01-2'
+    assert week_number('2006-01-01') == '2006-W01-3'
+    assert week_number('2006-01-06') == '2006-W02-1'
+    assert week_number('2006-12-28') == '2006-W52-7'
+    assert week_number('2006-12-29') == '2007-W01-1'
+    assert week_number('2006-12-31') == '2007-W01-3'
+    assert week_number('2007-01-01') == '2007-W01-4'
+    assert week_number('2007-01-05') == '2007-W02-1'
+    assert week_number('2007-12-27') == '2007-W52-7'
+    assert week_number('2007-12-28') == '2008-W01-1'
+    assert week_number('2007-12-31') == '2008-W01-4'
+    assert week_number('2008-01-01') == '2008-W01-5'
+    assert week_number('2008-01-04') == '2008-W02-1'
+    assert week_number('2008-12-25') == '2008-W52-7'
+    assert week_number('2008-12-26') == '2009-W01-1'
+    assert week_number('2008-12-31') == '2009-W01-6'
+    assert week_number('2009-01-01') == '2009-W01-7'
+    assert week_number('2009-01-02') == '2009-W02-1'
+    assert week_number('2009-12-24') == '2009-W52-7'
+    assert week_number('2009-12-25') == '2009-W53-1'
+    assert week_number('2009-12-31') == '2009-W53-7'
+    assert week_number('2010-01-01') == '2010-W01-1'
+    assert week_number('2010-01-08') == '2010-W02-1'
+
+    assert week_of_month('2003-01-01') == '1'  # Wed
+    assert week_of_month('2003-02-28') == '5'
+    assert week_of_month('2003-03-01') == '1'  # Sun
+    assert week_of_month('2003-12-31') == '5'
+    assert week_of_month('2004-01-01') == '1'  # Thu
+    assert week_of_month('2004-02-29') == '5'
+
+
+def test_week_numbering_saturday_mindays_1():
+    locale = Locale.parse('fr_DZ')
+    assert locale.first_week_day == 5
+    assert locale.min_week_days == 1
+
+    def week_number(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format="YYYY-'W'ww-e", locale=locale)
+
+    def week_of_month(value):
+        value = date.fromisoformat(value)
+        return dates.format_date(value, format='W', locale=locale)
+
+    assert week_number('2003-01-01') == '2003-W01-5'
+    assert week_number('2003-01-04') == '2003-W02-1'
+    assert week_number('2003-12-26') == '2003-W52-7'
+    assert week_number('2003-12-27') == '2004-W01-1'
+    assert week_number('2003-12-31') == '2004-W01-5'
+    assert week_number('2004-01-01') == '2004-W01-6'
+    assert week_number('2004-01-03') == '2004-W02-1'
+    assert week_number('2004-12-24') == '2004-W52-7'
+    assert week_number('2004-12-31') == '2004-W53-7'
+    assert week_number('2005-01-01') == '2005-W01-1'
+    assert week_number('2005-01-08') == '2005-W02-1'
+    assert week_number('2005-12-30') == '2005-W52-7'
+    assert week_number('2005-12-31') == '2006-W01-1'
+    assert week_number('2006-01-01') == '2006-W01-2'
+    assert week_number('2006-01-07') == '2006-W02-1'
+    assert week_number('2006-12-29') == '2006-W52-7'
+    assert week_number('2006-12-31') == '2007-W01-2'
+    assert week_number('2007-01-01') == '2007-W01-3'
+    assert week_number('2007-01-06') == '2007-W02-1'
+    assert week_number('2007-12-28') == '2007-W52-7'
+    assert week_number('2007-12-31') == '2008-W01-3'
+    assert week_number('2008-01-01') == '2008-W01-4'
+    assert week_number('2008-01-05') == '2008-W02-1'
+    assert week_number('2008-12-26') == '2008-W52-7'
+    assert week_number('2008-12-27') == '2009-W01-1'
+    assert week_number('2008-12-31') == '2009-W01-5'
+    assert week_number('2009-01-01') == '2009-W01-6'
+    assert week_number('2009-01-03') == '2009-W02-1'
+    assert week_number('2009-12-25') == '2009-W52-7'
+    assert week_number('2009-12-26') == '2010-W01-1'
+    assert week_number('2009-12-31') == '2010-W01-6'
+    assert week_number('2010-01-01') == '2010-W01-7'
+    assert week_number('2010-01-02') == '2010-W02-1'
+
+    assert week_of_month('2003-01-01') == '1'  # Wed
+    assert week_of_month('2003-02-28') == '4'
+    assert week_of_month('2003-08-01') == '1'  # Fri
+    assert week_of_month('2003-12-31') == '5'
+    assert week_of_month('2004-01-01') == '1'  # Thu
+    assert week_of_month('2004-02-29') == '5'
+
+
 def test_en_gb_first_weekday():
     assert Locale.parse('en').first_week_day == 0  # Monday in general
     assert Locale.parse('en_US').first_week_day == 6  # Sunday in the US