aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNiclas Axelsson <[email protected]>2011-03-02 16:44:43 +0100
committerNiclas Axelsson <[email protected]>2011-03-02 16:44:49 +0100
commitc7d5c7105cd5161fa2cb7c4358ea6097946fede7 (patch)
tree3edd3b534f34e476a3a08b8f9f49fd0b14684d79
parentbbed1363f171199b3d87145d8a5e2c1875ca90ca (diff)
parent7c94e6f3a69462968cfd352fb40389454bd42e7e (diff)
downloadotp-c7d5c7105cd5161fa2cb7c4358ea6097946fede7.tar.gz
otp-c7d5c7105cd5161fa2cb7c4358ea6097946fede7.tar.bz2
otp-c7d5c7105cd5161fa2cb7c4358ea6097946fede7.zip
Merge branch 'ih/calendar-iso-8601-erl' into dev
* ih/calendar-iso-8601-erl: Add ISO week number calculation functions to the calendar module in stdlib OTP-9087
-rw-r--r--lib/stdlib/doc/src/calendar.xml32
-rw-r--r--lib/stdlib/src/calendar.erl53
-rw-r--r--lib/stdlib/test/calendar_SUITE.erl22
3 files changed, 103 insertions, 4 deletions
diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml
index 36f0c03162..f90d8308b6 100644
--- a/lib/stdlib/doc/src/calendar.xml
+++ b/lib/stdlib/doc/src/calendar.xml
@@ -63,6 +63,14 @@
given as local time, they must be converted to universal time, in
order to get the correct value of the elapsed time between epochs.
Use of the function <c>time_difference/2</c> is discouraged.</p>
+ <p>There exists different definitions for the week of the year.
+ The calendar module contains a week of the year implementation
+ which conforms to the ISO 8601 standard. Since the week number for
+ a given date can fall on the previous, the current or on the next
+ year it is important to provide the information which year is it
+ together with the week number. The function <c>iso_week_number/0</c>
+ and <c>iso_week_number/1</c> returns a tuple of the year and the
+ week number.</p>
</description>
<section>
@@ -154,6 +162,30 @@ time() = {Hour, Minute, Second}
</desc>
</func>
<func>
+ <name>iso_week_number() -> IsoWeekNumber</name>
+ <fsummary>Compute the iso week number for the actual date</fsummary>
+ <type>
+ <v>IsoWeekNumber = {int(), int()}</v>
+ </type>
+ <desc>
+ <p>This function returns the tuple {Year, WeekNum} representing
+ the iso week number for the actual date. For determining the
+ actual date, the function <c>local_time/0</c> is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name>iso_week_number(Date) -> IsoWeekNumber</name>
+ <fsummary>Compute the iso week number for the given date</fsummary>
+ <type>
+ <v>Date = date()</v>
+ <v>IsoWeekNumber = {int(), int()}</v>
+ </type>
+ <desc>
+ <p>This function returns the tuple {Year, WeekNum} representing
+ the iso week number for the given date.</p>
+ </desc>
+ </func>
+ <func>
<name>last_day_of_the_month(Year, Month) -> int()</name>
<fsummary>Compute the number of days in a month</fsummary>
<desc>
diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl
index ddc0666f77..57b7c28dee 100644
--- a/lib/stdlib/src/calendar.erl
+++ b/lib/stdlib/src/calendar.erl
@@ -28,6 +28,8 @@
gregorian_days_to_date/1,
gregorian_seconds_to_datetime/1,
is_leap_year/1,
+ iso_week_number/0,
+ iso_week_number/1,
last_day_of_the_month/2,
local_time/0,
local_time_to_universal_time/1,
@@ -70,6 +72,7 @@
-type second() :: 0..59.
-type daynum() :: 1..7.
-type ldom() :: 28 | 29 | 30 | 31. % last day of month
+-type weeknum() :: 1..53.
-type t_now() :: {non_neg_integer(),non_neg_integer(),non_neg_integer()}.
@@ -77,6 +80,7 @@
-type t_time() :: {hour(),minute(),second()}.
-type t_datetime() :: {t_date(),t_time()}.
-type t_datetime1970() :: {{year1970(),month(),day()},t_time()}.
+-type t_yearweeknum() :: {year(),weeknum()}.
%%----------------------------------------------------------------------
@@ -172,6 +176,42 @@ is_leap_year1(Year) when Year rem 400 =:= 0 ->
is_leap_year1(_) -> false.
+%%
+%% Calculates the iso week number for the current date.
+%%
+-spec iso_week_number() -> t_yearweeknum().
+iso_week_number() ->
+ {Date, _} = local_time(),
+ iso_week_number(Date).
+
+
+%%
+%% Calculates the iso week number for the given date.
+%%
+-spec iso_week_number(t_date()) -> t_yearweeknum().
+iso_week_number({Year, Month, Day}) ->
+ D = date_to_gregorian_days({Year, Month, Day}),
+ W01_1_Year = gregorian_days_of_iso_w01_1(Year),
+ W01_1_NextYear = gregorian_days_of_iso_w01_1(Year + 1),
+ if W01_1_Year =< D andalso D < W01_1_NextYear ->
+ % Current Year Week 01..52(,53)
+ {Year, (D - W01_1_Year) div 7 + 1};
+ D < W01_1_Year ->
+ % Previous Year 52 or 53
+ PWN = case day_of_the_week(Year - 1, 1, 1) of
+ 4 -> 53;
+ _ -> case day_of_the_week(Year - 1, 12, 31) of
+ 4 -> 53;
+ _ -> 52
+ end
+ end,
+ {Year - 1, PWN};
+ W01_1_NextYear =< D ->
+ % Next Year, Week 01
+ {Year + 1, 1}
+ end.
+
+
%% last_day_of_the_month(Year, Month)
%%
%% Returns the number of days in a month.
@@ -377,6 +417,19 @@ dty(Y, D1, D2) when D1 < D2 ->
dty(Y, _D1, D2) ->
{Y, D2}.
+%%
+%% The Gregorian days of the iso week 01 day 1 for a given year.
+%%
+-spec gregorian_days_of_iso_w01_1(year()) -> non_neg_integer().
+gregorian_days_of_iso_w01_1(Year) ->
+ D0101 = date_to_gregorian_days(Year, 1, 1),
+ DOW = day_of_the_week(Year, 1, 1),
+ if DOW =< 4 ->
+ D0101 - DOW + 1;
+ true ->
+ D0101 + 7 - DOW + 1
+ end.
+
%% year_day_to_date(Year, DayOfYear) = {Month, DayOfMonth}
%%
%% Note: 1 is the first day of the month.
diff --git a/lib/stdlib/test/calendar_SUITE.erl b/lib/stdlib/test/calendar_SUITE.erl
index 81b0299118..8192d035ca 100644
--- a/lib/stdlib/test/calendar_SUITE.erl
+++ b/lib/stdlib/test/calendar_SUITE.erl
@@ -28,7 +28,8 @@
day_of_the_week_calibrate/1,
leap_years/1,
last_day_of_the_month/1,
- local_time_to_universal_time_dst/1]).
+ local_time_to_universal_time_dst/1,
+ iso_week_number/1]).
-define(START_YEAR, 1947).
-define(END_YEAR, 2012).
@@ -38,7 +39,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[gregorian_days, gregorian_seconds, day_of_the_week,
day_of_the_week_calibrate, leap_years,
- last_day_of_the_month, local_time_to_universal_time_dst].
+ last_day_of_the_month, local_time_to_universal_time_dst, iso_week_number].
groups() ->
[].
@@ -55,7 +56,6 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
-
gregorian_days(doc) ->
"Tests that date_to_gregorian_days and gregorian_days_to_date "
"are each others inverses from ?START_YEAR-01-01 up to ?END_YEAR-01-01. "
@@ -170,6 +170,15 @@ local_time_to_universal_time_dst_x(Config) when is_list(Config) ->
{comment,"Bug in mktime() in this OS"}
end.
+iso_week_number(doc) ->
+ "Test the iso week number calculation for all three possibilities."
+ " When the date falls on the last week of the previous year,"
+ " when the date falls on a week within the given year and finally,"
+ " when the date falls on the first week of the next year.";
+iso_week_number(suite) ->
+ [];
+iso_week_number(Config) when is_list(Config) ->
+ ?line check_iso_week_number().
%%
%% LOCAL FUNCTIONS
@@ -259,7 +268,12 @@ check_last_day_of_the_month({SYr, SMon}, {EYr, EMon}) when SYr < EYr ->
check_last_day_of_the_month(_, _) ->
ok.
-
+%% check_iso_week_number
+%%
+check_iso_week_number() ->
+ ?line {2004, 53} = calendar:iso_week_number({2005, 1, 1}),
+ ?line {2007, 1} = calendar:iso_week_number({2007, 1, 1}),
+ ?line {2009, 1} = calendar:iso_week_number({2008, 12, 29}).