Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Updated tzdata to fix issues with 2024b
- Fix deprecation: Module.eval_quoted/4 is deprecated. Use Code.eval_quoted/3 instead
- Fix deprecation: "min..max inside match is deprecated"
- Fix week of year numbering for `{Wmon}`, `{Wsun}`, `%W` and `%U` in years starting on a Sunday or Monday

---

Expand Down
47 changes: 11 additions & 36 deletions lib/format/datetime/formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -555,45 +555,13 @@ defmodule Timex.Format.DateTime.Formatter do
pad_numeric(week, flags, width)
end

def format_token(_locale, :week_mon, %{:year => year} = date, _modifiers, flags, width) do
new_year = Timex.Date.new!(year, 1, 1)
week_start = Timex.Date.beginning_of_week(new_year, :monday)

# This date can be calculated by taking the day number of the year,
# shifting the day number of the year down by the number of days which
# occurred in the previous year, then dividing by 7
day_num =
if Date.compare(week_start, new_year) == :lt do
prev_year_day_start = Date.day_of_year(week_start)
prev_year_day_end = Date.day_of_year(Timex.Date.new!(week_start.year, 12, 31))
shift = prev_year_day_end - prev_year_day_start
shift + Date.day_of_year(Timex.Date.new!(year, date.month, date.day))
else
Date.day_of_year(Timex.Date.new!(year, date.month, date.day))
end

div(day_num, 7)
def format_token(_locale, :week_mon, date, _modifiers, flags, width) do
week_number(date, :monday)
|> pad_numeric(flags, width)
end

def format_token(_locale, :week_sun, %{:year => year} = date, _modifiers, flags, width) do
new_year = Timex.Date.new!(year, 1, 1)
week_start = Timex.Date.beginning_of_week(new_year, :sunday)

# This date can be calculated by taking the day number of the year,
# shifting the day number of the year down by the number of days which
# occurred in the previous year, then dividing by 7
day_num =
if Date.compare(week_start, new_year) == :lt do
prev_year_day_start = Date.day_of_year(week_start)
prev_year_day_end = Date.day_of_year(Timex.Date.new!(week_start.year, 12, 31))
shift = prev_year_day_end - prev_year_day_start
shift + Date.day_of_year(Timex.Date.new!(year, date.month, date.day))
else
Date.day_of_year(Timex.Date.new!(year, date.month, date.day))
end

div(day_num, 7)
def format_token(_locale, :week_sun, date, _modifiers, flags, width) do
week_number(date, :sunday)
|> pad_numeric(flags, width)
end

Expand Down Expand Up @@ -859,6 +827,13 @@ defmodule Timex.Format.DateTime.Formatter do
{:error, {:formatter, :unsupported_token, token}}
end

# Week of the year counted from the first `weekstart` day, as strftime
# defines %W and %U: days before that first day belong to week 0.
defp week_number(date, weekstart) do
weekday = Timex.weekday!(date, weekstart) - 1
div(Timex.day(date) + 6 - weekday, 7)
end

defp pad_numeric(number, flags, width) when is_integer(number),
do: pad_numeric("#{number}", flags, width)

Expand Down
4 changes: 2 additions & 2 deletions lib/format/datetime/formatters/default.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ defmodule Timex.Format.DateTime.Formatters.Default do
### Weeks

* `{Wiso}` - ISO week number (01..53)
* `{Wmon}` - week number of the year, Monday first (01..53)
* `{Wsun}` - week number of the year, Sunday first (01..53)
* `{Wmon}` - week number of the year, Monday first (00..53)
* `{Wsun}` - week number of the year, Sunday first (00..53)

### Time

Expand Down
4 changes: 2 additions & 2 deletions lib/format/datetime/formatters/strftime.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ defmodule Timex.Format.DateTime.Formatters.Strftime do
### Weeks

* `%V` - ISO week number (01..53)
* `%W` - week number of the year, Monday first (00..52)
* `%U` - week number of the year, Sunday first (00..52)
* `%W` - week number of the year, Monday first (00..53)
* `%U` - week number of the year, Sunday first (00..53)

### Time

Expand Down
17 changes: 17 additions & 0 deletions test/format_default_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,23 @@ defmodule DateFormatTest.FormatDefault do
date = Timex.to_datetime({2014, 12, 31})
assert {:ok, "52"} = format(date, "{Wmon}")
assert {:ok, "52"} = format(date, "{Wsun}")

# Years beginning on Sunday (2023) or Monday (2024)
date = Timex.to_datetime({2023, 4, 28})
assert {:ok, "17"} = format(date, "{Wmon}")
assert {:ok, "17"} = format(date, "{Wsun}")

date = Timex.to_datetime({2023, 4, 30})
assert {:ok, "17"} = format(date, "{Wmon}")
assert {:ok, "18"} = format(date, "{Wsun}")

date = Timex.to_datetime({2024, 1, 1})
assert {:ok, "1"} = format(date, "{Wmon}")
assert {:ok, "0"} = format(date, "{Wsun}")

date = Timex.to_datetime({2024, 1, 8})
assert {:ok, "2"} = format(date, "{Wmon}")
assert {:ok, "1"} = format(date, "{Wsun}")
end

test "format simple compound date formats" do
Expand Down
16 changes: 16 additions & 0 deletions test/format_strftime_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ defmodule DateFormatTest.FormatStrftime do
date = Timex.to_datetime({2013, 1, 7})
assert {:ok, "01"} = format(date, "%W")
assert {:ok, "1"} = format(date, "%-W")
date = Timex.to_datetime({2023, 4, 28})
assert {:ok, "17"} = format(date, "%W")
date = Timex.to_datetime({2023, 5, 1})
assert {:ok, "18"} = format(date, "%W")
date = Timex.to_datetime({2024, 1, 1})
assert {:ok, "01"} = format(date, "%W")
date = Timex.to_datetime({2024, 1, 8})
assert {:ok, "02"} = format(date, "%W")
end

test "format %U" do
Expand All @@ -206,6 +214,14 @@ defmodule DateFormatTest.FormatStrftime do
date = Timex.to_datetime({2013, 1, 7})
assert {:ok, "01"} = format(date, "%U")
assert {:ok, "1"} = format(date, "%-U")
date = Timex.to_datetime({2023, 4, 28})
assert {:ok, "17"} = format(date, "%U")
date = Timex.to_datetime({2023, 4, 30})
assert {:ok, "18"} = format(date, "%U")
date = Timex.to_datetime({2024, 1, 1})
assert {:ok, "00"} = format(date, "%U")
date = Timex.to_datetime({2024, 1, 7})
assert {:ok, "01"} = format(date, "%U")
end

test "various simple date combinations" do
Expand Down