From 7c94e6f3a69462968cfd352fb40389454bd42e7e Mon Sep 17 00:00:00 2001 From: Imre Horvath Date: Tue, 14 Dec 2010 23:03:44 +0100 Subject: Add ISO week number calculation functions to the calendar module in stdlib This new feature adds the missing week number function to the calendar module of the stdlib application. The implementation conforms to the ISO 8601 standard. The new feature has been implemented tested and documented. --- lib/stdlib/doc/src/calendar.xml | 32 +++++++++++++++++++++++ lib/stdlib/src/calendar.erl | 53 ++++++++++++++++++++++++++++++++++++++ lib/stdlib/test/calendar_SUITE.erl | 22 +++++++++++++--- 3 files changed, 103 insertions(+), 4 deletions(-) (limited to 'lib/stdlib') 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 time_difference/2 is discouraged.

+

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 iso_week_number/0 + and iso_week_number/1 returns a tuple of the year and the + week number.

@@ -153,6 +161,30 @@ time() = {Hour, Minute, Second}

This function checks if a year is a leap year.

+ + iso_week_number() -> IsoWeekNumber + Compute the iso week number for the actual date + + IsoWeekNumber = {int(), int()} + + +

This function returns the tuple {Year, WeekNum} representing + the iso week number for the actual date. For determining the + actual date, the function local_time/0 is used.

+
+
+ + iso_week_number(Date) -> IsoWeekNumber + Compute the iso week number for the given date + + Date = date() + IsoWeekNumber = {int(), int()} + + +

This function returns the tuple {Year, WeekNum} representing + the iso week number for the given date.

+
+
last_day_of_the_month(Year, Month) -> int() Compute the number of days in a month 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}). -- cgit v1.2.3