diff --git a/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
index 769dbc0dace..0a5910f1aec 100644
--- a/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
+++ b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
@@ -49,6 +49,11 @@
+
+
+
diff --git a/samples/ControlCatalog/Pages/CalendarPage.xaml b/samples/ControlCatalog/Pages/CalendarPage.xaml
index 99727e91241..9da75587975 100644
--- a/samples/ControlCatalog/Pages/CalendarPage.xaml
+++ b/samples/ControlCatalog/Pages/CalendarPage.xaml
@@ -48,6 +48,12 @@
+
+
+
+
diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs
index 7062a9af814..41b816cd573 100644
--- a/src/Avalonia.Controls/Calendar/Calendar.cs
+++ b/src/Avalonia.Controls/Calendar/Calendar.cs
@@ -1,4 +1,4 @@
-// (c) Copyright Microsoft Corporation.
+// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
@@ -6,6 +6,7 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
+using System.Globalization;
using Avalonia.Automation.Peers;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
@@ -353,6 +354,57 @@ public IBrush? HeaderBackground
set => SetValue(HeaderBackgroundProperty, value);
}
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty IsWeekNumberVisibleProperty =
+ AvaloniaProperty.Register(nameof(IsWeekNumberVisible));
+
+ ///
+ /// Gets or sets a value indicating whether week numbers are shown in the month view.
+ ///
+ public bool IsWeekNumberVisible
+ {
+ get => GetValue(IsWeekNumberVisibleProperty);
+ set => SetValue(IsWeekNumberVisibleProperty, value);
+ }
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty WeekNumberRuleProperty =
+ AvaloniaProperty.Register(
+ nameof(WeekNumberRule),
+ defaultValue: (CalendarWeekNumberRule)DateTimeHelper.GetCurrentDateFormat().CalendarWeekRule);
+
+ ///
+ /// Gets or sets the rule used to determine the first week of the year for week number display.
+ /// The default is taken from the current culture. Use
+ /// for ISO 8601 week numbering.
+ ///
+ public CalendarWeekNumberRule WeekNumberRule
+ {
+ get => GetValue(WeekNumberRuleProperty);
+ set => SetValue(WeekNumberRuleProperty, value);
+ }
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty WeekNumberHeaderProperty =
+ AvaloniaProperty.Register(nameof(WeekNumberHeader), "#");
+
+ ///
+ /// Gets or sets the content displayed in the week-number column header cell.
+ /// Set this to a localized string such as "CW" , "KW" , or "Wk"
+ /// to give users context for the week-number column. Defaults to "#" .
+ ///
+ public object? WeekNumberHeader
+ {
+ get => GetValue(WeekNumberHeaderProperty);
+ set => SetValue(WeekNumberHeaderProperty, value);
+ }
+
public static readonly StyledProperty DisplayModeProperty =
AvaloniaProperty.Register(
nameof(DisplayMode),
@@ -2209,6 +2261,9 @@ static Calendar()
DisplayDateProperty.Changed.AddClassHandler((x, e) => x.OnDisplayDateChanged(e));
DisplayDateStartProperty.Changed.AddClassHandler((x, e) => x.OnDisplayDateStartChanged(e));
DisplayDateEndProperty.Changed.AddClassHandler((x, e) => x.OnDisplayDateEndChanged(e));
+ IsWeekNumberVisibleProperty.Changed.AddClassHandler((x, _) => x.UpdateMonths());
+ WeekNumberRuleProperty.Changed.AddClassHandler((x, _) => x.UpdateMonths());
+ WeekNumberHeaderProperty.Changed.AddClassHandler((x, _) => x.UpdateMonths());
KeyDownEvent.AddClassHandler((x, e) => x.Calendar_KeyDown(e));
KeyUpEvent.AddClassHandler((x, e) => x.Calendar_KeyUp(e));
}
diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs
index e33298023f0..09cfac405bd 100644
--- a/src/Avalonia.Controls/Calendar/CalendarItem.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarItem.cs
@@ -24,7 +24,7 @@ namespace Avalonia.Controls.Primitives
[TemplatePart(PART_ElementNextButton, typeof(Button))]
[TemplatePart(PART_ElementPreviousButton, typeof(Button))]
[TemplatePart(PART_ElementYearView, typeof(Grid))]
- [PseudoClasses(":calendardisabled")]
+ [PseudoClasses(":calendardisabled", ":hasweeknumbers")]
public sealed class CalendarItem : TemplatedControl
{
///
@@ -49,6 +49,9 @@ public sealed class CalendarItem : TemplatedControl
private readonly System.Globalization.Calendar _calendar = new GregorianCalendar();
+ private CalendarWeekNumberLabel[] _weekNumberLabels = Array.Empty();
+ private CalendarWeekNumberLabel? _weekNumberHeaderLabel;
+
internal Calendar? Owner { get; set; }
internal CalendarDayButton? CurrentButton { get; set; }
@@ -168,16 +171,18 @@ private void PopulateGrids()
{
if (MonthView != null)
{
- var childCount = Calendar.RowsPerMonth + Calendar.RowsPerMonth * Calendar.ColumnsPerMonth;
+ // +7 for the week-number column (1 header + 6 data cells)
+ var childCount = Calendar.RowsPerMonth + Calendar.RowsPerMonth * Calendar.ColumnsPerMonth + Calendar.RowsPerMonth;
using var children = new PooledList(childCount);
+ // Day-of-week title cells — day columns are at 1-7 (shifted right by 1)
for (int i = 0; i < Calendar.ColumnsPerMonth; i++)
{
if (DayTitleTemplate?.Build() is Control cell)
{
cell.DataContext = string.Empty;
cell.SetValue(Grid.RowProperty, 0);
- cell.SetValue(Grid.ColumnProperty, i);
+ cell.SetValue(Grid.ColumnProperty, i + 1);
children.Add(cell);
}
}
@@ -198,7 +203,7 @@ private void PopulateGrids()
cell.Owner = Owner;
}
cell.SetValue(Grid.RowProperty, i);
- cell.SetValue(Grid.ColumnProperty, j);
+ cell.SetValue(Grid.ColumnProperty, j + 1);
cell.CalendarDayButtonMouseDown += cellMouseLeftButtonDown;
cell.CalendarDayButtonMouseUp += cellMouseLeftButtonUp;
cell.PointerEntered += cellMouseEntered;
@@ -206,7 +211,26 @@ private void PopulateGrids()
children.Add(cell);
}
}
-
+
+ // Week-number cells — added after existing cells to preserve child indices 0–48.
+ // Header placeholder (row 0, col 0)
+ _weekNumberHeaderLabel = new CalendarWeekNumberLabel();
+ _weekNumberHeaderLabel.IsHeader = true;
+ _weekNumberHeaderLabel.SetValue(Grid.RowProperty, 0);
+ _weekNumberHeaderLabel.SetValue(Grid.ColumnProperty, 0);
+ children.Add(_weekNumberHeaderLabel);
+
+ // Data labels (rows 1–6, col 0)
+ _weekNumberLabels = new CalendarWeekNumberLabel[Calendar.RowsPerMonth - 1];
+ for (int i = 1; i < Calendar.RowsPerMonth; i++)
+ {
+ var label = new CalendarWeekNumberLabel();
+ label.SetValue(Grid.RowProperty, i);
+ label.SetValue(Grid.ColumnProperty, 0);
+ _weekNumberLabels[i - 1] = label;
+ children.Add(label);
+ }
+
MonthView.Children.AddRange(children);
}
@@ -254,6 +278,10 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
NextButton = e.NameScope.Find(PART_ElementNextButton);
MonthView = e.NameScope.Find(PART_ElementMonthView);
YearView = e.NameScope.Find(PART_ElementYearView);
+
+ // Prepend a column for week numbers; day columns follow in positions 1–7.
+ // Done here so PopulateGrids() can place cells at the correct indices.
+ MonthView?.ColumnDefinitions.Insert(0, new ColumnDefinition(GridLength.Auto));
if (Owner != null)
{
@@ -373,6 +401,7 @@ internal void UpdateMonthMode()
{
SetDayTitles();
SetCalendarDayButtons(_currentMonth);
+ UpdateWeekNumberLabels(_currentMonth);
}
}
private void SetMonthModeHeaderButton()
@@ -589,6 +618,36 @@ private void SetCalendarDayButtons(DateTime firstDayOfMonth)
}
}
+ private void UpdateWeekNumberLabels(DateTime firstDayOfMonth)
+ {
+ if (_weekNumberLabels.Length == 0)
+ return;
+
+ int lastMonthToDisplay = PreviousMonthDays(firstDayOfMonth);
+ DateTime firstDateDisplayed = DateTimeHelper.CompareYearMonth(firstDayOfMonth, DateTime.MinValue) > 0
+ ? _calendar.AddDays(firstDayOfMonth, -lastMonthToDisplay)
+ : firstDayOfMonth;
+
+ bool show = Owner?.IsWeekNumberVisible ?? false;
+ var rule = Owner?.WeekNumberRule ?? (CalendarWeekNumberRule)DateTimeHelper.GetCurrentDateFormat().CalendarWeekRule;
+ var firstDayOfWeek = Owner?.FirstDayOfWeek ?? DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek;
+
+ PseudoClasses.Set(":hasweeknumbers", show);
+
+ for (int i = 0; i < _weekNumberLabels.Length; i++)
+ {
+ DateTime firstDayOfRow = _calendar.AddDays(firstDateDisplayed, i * NumberOfDaysPerWeek);
+ _weekNumberLabels[i].Content = DateTimeHelper.GetWeekOfYear(firstDayOfRow, rule, firstDayOfWeek).ToString();
+ _weekNumberLabels[i].IsVisible = show;
+ }
+
+ if (_weekNumberHeaderLabel != null)
+ {
+ _weekNumberHeaderLabel.Content = Owner?.WeekNumberHeader;
+ _weekNumberHeaderLabel.IsVisible = show;
+ }
+ }
+
internal void UpdateYearMode()
{
if (Owner != null)
diff --git a/src/Avalonia.Controls/Calendar/CalendarWeekNumberLabel.cs b/src/Avalonia.Controls/Calendar/CalendarWeekNumberLabel.cs
new file mode 100644
index 00000000000..c1b6fc27d4b
--- /dev/null
+++ b/src/Avalonia.Controls/Calendar/CalendarWeekNumberLabel.cs
@@ -0,0 +1,27 @@
+namespace Avalonia.Controls.Primitives
+{
+ ///
+ /// Displays a week number in the month view of a .
+ /// Apply styles targeting to customise
+ /// the appearance — for example FontWeight="Bold" .
+ /// Use the :header pseudo-class to target the column header cell (row 0).
+ ///
+ public sealed class CalendarWeekNumberLabel : ContentControl
+ {
+ ///
+ /// Gets or sets a value indicating whether this label is the column header cell
+ /// (placed in row 0 of the month grid, above the week-number data cells).
+ /// Themes can target this with the :header pseudo-class.
+ ///
+ public bool IsHeader
+ {
+ get => field;
+ internal set
+ {
+ if (field == value) return;
+ field = value;
+ PseudoClasses.Set(":header", value);
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.Controls/Calendar/CalendarWeekNumberRule.cs b/src/Avalonia.Controls/Calendar/CalendarWeekNumberRule.cs
new file mode 100644
index 00000000000..11646be9a95
--- /dev/null
+++ b/src/Avalonia.Controls/Calendar/CalendarWeekNumberRule.cs
@@ -0,0 +1,37 @@
+namespace Avalonia.Controls
+{
+ ///
+ /// Defines the rule that determines the first week of the calendar year for week-number display.
+ ///
+ public enum CalendarWeekNumberRule
+ {
+ ///
+ /// The first week of the year starts on the first day of the year and ends before the
+ /// following designated first day of the week. Equivalent to
+ /// .
+ ///
+ FirstDay = 0,
+
+ ///
+ /// The first week of the year begins on the first occurrence of the designated first day
+ /// of the week on or after the first day of the year. Equivalent to
+ /// .
+ ///
+ FirstFullWeek = 1,
+
+ ///
+ /// The first week of the year is the first week with four or more days before the
+ /// designated first day of the week. Equivalent to
+ /// .
+ ///
+ FirstFourDayWeek = 2,
+
+ ///
+ /// Uses ISO 8601 week numbering: the first week of the year must contain at least four days,
+ /// and Monday is treated as the first day of the week, regardless of
+ /// . This is the most common rule in European locales
+ /// and professional calendar applications.
+ ///
+ Iso = 3,
+ }
+}
diff --git a/src/Avalonia.Controls/Calendar/DateTimeHelper.cs b/src/Avalonia.Controls/Calendar/DateTimeHelper.cs
index 570f05cfe8d..af9ee79a22d 100644
--- a/src/Avalonia.Controls/Calendar/DateTimeHelper.cs
+++ b/src/Avalonia.Controls/Calendar/DateTimeHelper.cs
@@ -131,5 +131,27 @@ public static string ToYearString(DateTime date)
var format = GetCurrentDateFormat();
return date.Year.ToString(format);
}
+
+ ///
+ /// Gets the week-of-year number for the specified date.
+ ///
+ /// The date.
+ /// The rule that defines what constitutes the first week of the year.
+ /// The first day of the week. Ignored when is .
+ /// The week number that falls in.
+ public static int GetWeekOfYear(DateTime date, CalendarWeekNumberRule rule, DayOfWeek firstDayOfWeek)
+ {
+ var mappedRule = (CalendarWeekRule)(int)rule;
+
+ // Iso is an explicit shorthand for ISO 8601 (FirstFourDayWeek + Monday).
+ // Also catches FirstFourDayWeek + Monday explicitly, because .NET's
+ // Calendar.GetWeekOfYear incorrectly returns week 53 for late-December dates
+ // that ISO 8601 assigns to week 1 of the next year (e.g. 2018-12-31).
+ if (rule == CalendarWeekNumberRule.Iso ||
+ (mappedRule == CalendarWeekRule.FirstFourDayWeek && firstDayOfWeek == DayOfWeek.Monday))
+ return ISOWeek.GetWeekOfYear(date);
+
+ return CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(date, mappedRule, firstDayOfWeek);
+ }
}
}
diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs
index 864ba86ef08..0ef6a73a2ff 100644
--- a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs
+++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Layout;
@@ -140,6 +141,24 @@ public partial class CalendarDatePicker
public static readonly StyledProperty VerticalContentAlignmentProperty =
ContentControl.VerticalContentAlignmentProperty.AddOwner();
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty IsWeekNumberVisibleProperty =
+ Calendar.IsWeekNumberVisibleProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty WeekNumberRuleProperty =
+ Calendar.WeekNumberRuleProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty WeekNumberHeaderProperty =
+ Calendar.WeekNumberHeaderProperty.AddOwner();
+
///
/// Gets a collection of dates that are marked as not selectable.
///
@@ -358,5 +377,26 @@ public VerticalAlignment VerticalContentAlignment
get => GetValue(VerticalContentAlignmentProperty);
set => SetValue(VerticalContentAlignmentProperty, value);
}
+
+ ///
+ public bool IsWeekNumberVisible
+ {
+ get => GetValue(IsWeekNumberVisibleProperty);
+ set => SetValue(IsWeekNumberVisibleProperty, value);
+ }
+
+ ///
+ public CalendarWeekNumberRule WeekNumberRule
+ {
+ get => GetValue(WeekNumberRuleProperty);
+ set => SetValue(WeekNumberRuleProperty, value);
+ }
+
+ ///
+ public object? WeekNumberHeader
+ {
+ get => GetValue(WeekNumberHeaderProperty);
+ set => SetValue(WeekNumberHeaderProperty, value);
+ }
}
}
diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml
index 2613a0a1334..03fcc462dac 100644
--- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml
+++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResources.xaml
@@ -402,6 +402,8 @@
+
@@ -1228,6 +1230,8 @@
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml
index 395e2d514f6..7e44c412a67 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CalendarDatePicker.xaml
@@ -133,7 +133,10 @@
SelectedDate="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedDate, Mode=TwoWay}"
DisplayDate="{TemplateBinding DisplayDate}"
DisplayDateStart="{TemplateBinding DisplayDateStart}"
- DisplayDateEnd="{TemplateBinding DisplayDateEnd}" />
+ DisplayDateEnd="{TemplateBinding DisplayDateEnd}"
+ IsWeekNumberVisible="{TemplateBinding IsWeekNumberVisible}"
+ WeekNumberRule="{TemplateBinding WeekNumberRule}"
+ WeekNumberHeader="{TemplateBinding WeekNumberHeader}" />
diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
index ac3ebadbb80..4b0fb20195a 100644
--- a/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/CalendarItem.xaml
@@ -73,9 +73,10 @@
-
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/CalendarWeekNumberLabel.xaml b/src/Avalonia.Themes.Fluent/Controls/CalendarWeekNumberLabel.xaml
new file mode 100644
index 00000000000..c458f355ca6
--- /dev/null
+++ b/src/Avalonia.Themes.Fluent/Controls/CalendarWeekNumberLabel.xaml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
index 0daa900a4c4..1cf2c755929 100644
--- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
+++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
@@ -24,6 +24,7 @@
+
diff --git a/src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml b/src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml
index 9eba0a413b2..88083598c71 100644
--- a/src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/CalendarDatePicker.xaml
@@ -128,7 +128,10 @@
FirstDayOfWeek="{TemplateBinding FirstDayOfWeek}"
IsTodayHighlighted="{TemplateBinding IsTodayHighlighted}"
SelectedDate="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedDate,
- Mode=TwoWay}" />
+ Mode=TwoWay}"
+ IsWeekNumberVisible="{TemplateBinding IsWeekNumberVisible}"
+ WeekNumberRule="{TemplateBinding WeekNumberRule}"
+ WeekNumberHeader="{TemplateBinding WeekNumberHeader}" />
diff --git a/src/Avalonia.Themes.Simple/Controls/CalendarWeekNumberLabel.xaml b/src/Avalonia.Themes.Simple/Controls/CalendarWeekNumberLabel.xaml
new file mode 100644
index 00000000000..492664e6984
--- /dev/null
+++ b/src/Avalonia.Themes.Simple/Controls/CalendarWeekNumberLabel.xaml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml b/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml
index ef6de7ca78e..a876ae3edf3 100644
--- a/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml
+++ b/src/Avalonia.Themes.Simple/Controls/SimpleControls.xaml
@@ -56,6 +56,7 @@
+
diff --git a/tests/Avalonia.Controls.UnitTests/CalendarTests.cs b/tests/Avalonia.Controls.UnitTests/CalendarTests.cs
index 75ba43c9e4f..e8073087f19 100644
--- a/tests/Avalonia.Controls.UnitTests/CalendarTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/CalendarTests.cs
@@ -440,5 +440,77 @@ public void CalendarItem_Should_Reset_YearView_Mouse_Down_Flag_On_Detach_From_Vi
// Verify the flag was reset
Assert.False((bool)field.GetValue(calendarItem)!);
}
+
+ [Fact]
+ public void WeekNumberHeader_Defaults_To_Hash_Symbol()
+ {
+ var calendar = new Calendar();
+ Assert.Equal(calendar.WeekNumberHeader, "#");
+ }
+
+ [Fact]
+ public void WeekNumberHeader_Can_Be_Set_To_String()
+ {
+ var calendar = new Calendar();
+ calendar.WeekNumberHeader = "CW";
+ Assert.Equal("CW", calendar.WeekNumberHeader);
+ }
+
+ // --- Week number tests ---
+
+ [Fact]
+ public void IsWeekNumberVisible_Defaults_To_False()
+ {
+ var calendar = new Calendar();
+ Assert.False(calendar.IsWeekNumberVisible);
+ }
+
+ [Fact]
+ public void WeekNumberRule_Defaults_To_Culture_CalendarWeekRule()
+ {
+ var calendar = new Calendar();
+ Assert.IsType(calendar.WeekNumberRule);
+ }
+
+ [Fact]
+ public void IsWeekNumberVisible_Can_Be_Set()
+ {
+ var calendar = new Calendar();
+ calendar.IsWeekNumberVisible = true;
+ Assert.True(calendar.IsWeekNumberVisible);
+ }
+
+ [Fact]
+ public void WeekNumberRule_Can_Be_Set()
+ {
+ var calendar = new Calendar();
+ calendar.WeekNumberRule = CalendarWeekNumberRule.FirstFourDayWeek;
+ Assert.Equal(CalendarWeekNumberRule.FirstFourDayWeek, calendar.WeekNumberRule);
+ }
+
+ [Theory]
+ // ISO 8601: week 1 of 2023 starts on Monday 2 Jan 2023
+ [InlineData(2023, 1, 2, CalendarWeekNumberRule.FirstFourDayWeek, DayOfWeek.Monday, 1)]
+ // 2022-12-31 is still in ISO week 52 of 2022
+ [InlineData(2022, 12, 31, CalendarWeekNumberRule.FirstFourDayWeek, DayOfWeek.Monday, 52)]
+ // .NET bug: 2018-12-31 is a Monday and is ISO week 1 of 2019, not week 53 of 2018
+ [InlineData(2018, 12, 31, CalendarWeekNumberRule.FirstFourDayWeek, DayOfWeek.Monday, 1)]
+ // US rule: week 1 always starts on Jan 1
+ [InlineData(2023, 1, 1, CalendarWeekNumberRule.FirstDay, DayOfWeek.Sunday, 1)]
+ [InlineData(2023, 12, 31, CalendarWeekNumberRule.FirstDay, DayOfWeek.Sunday, 53)]
+ // Iso shorthand: same results as FirstFourDayWeek + Monday
+ [InlineData(2023, 1, 2, CalendarWeekNumberRule.Iso, DayOfWeek.Sunday, 1)]
+ [InlineData(2022, 12, 31, CalendarWeekNumberRule.Iso, DayOfWeek.Sunday, 52)]
+ [InlineData(2018, 12, 31, CalendarWeekNumberRule.Iso, DayOfWeek.Sunday, 1)]
+ public void GetWeekOfYear_Returns_Correct_Week_Number(
+ int year, int month, int day,
+ CalendarWeekNumberRule rule,
+ DayOfWeek firstDayOfWeek,
+ int expectedWeek)
+ {
+ var date = new DateTime(year, month, day);
+ int week = DateTimeHelper.GetWeekOfYear(date, rule, firstDayOfWeek);
+ Assert.Equal(expectedWeek, week);
+ }
}
}