Converting Dates to Week Dates
Ed Schaefer and John Spurgeon
Dates are often identified by a year, a month, and a
day of the month. An alternative notation, common in European countries and
in manufacturing industries, uses a year, a week number, and a day of the
week to specify a date.
The main difficulty with using weeks to specify a date
is identifying when the first week of a year should begin. ISO, the
International Standards Organization, created the ISO 8601 standard to
reduce confusion in the area of date/time representations. Regarding week
dates, the document states that calendar weeks start on Monday; weeks are
numbered starting with the number one; and the first calendar week of a
year is the week that includes the first Thursday of the year.
Algorithms based on the ISO 8601 definition of a week
have been developed to convert dates from year-month-day format to
year-week-day format. One example is an algorithm described by Rick McCarty
[1].
But what if the ISO 8601 definition of a week date
doesn't apply? What if you are operating under the assumption that
weeks start on Sunday, not Monday? What if January 1st must fall in the
first calendar week of the year?
We developed Korn shell script ymd2ywd (Listing 1)
that converts a date in year-month-day format to an equivalent date in
year-week-day format. The algorithm employed is similar to McCarty's.
Both algorithms rely only on simple math functions and convert a wide range
of dates. (Some algorithms rely on date arithmetic capabilities provided by
the programming environment or significantly limit the range of dates that
can be converted.) However, it is easy to configure our ymd2ywd script to
account for various assumptions about when a week begins and what
constitutes the first week of the year. In other words, ymd2ywd is a more general solution to a class of similar problems.
Functions
Here is a list of the various functions:
- ymd2ywd (Listing 1) makes use of some
simple functions. It sources the define_functions script (Listing 2).
- define_functions
(Listing 2) uses the command typeset -fu to indicate that certain
strings are names of functions rather than variables. It also sets the
FPATH environment variable, which specifies the search path for when
functions are referenced.
- day_of_week
(Listing 3) takes three integers representing a year, month, and day of the
month and prints an integer representing a day
of the week. This function utilizes a particularly interesting algorithm
described by Mike Keith [2].
- days_in_year (Listing 4) takes a year and
prints the number of days in that year.
- is_integer (Listing 5) takes a string and
returns 0 if the string can be treated as an integer by the shell;
otherwise the function returns 1.
- is_leap_year (Listing 6) takes a year and
returns 0 if the year is a leap year; otherwise the function returns 1.
- is_valid_date (Listing 7) takes a year,
month, and day of the month separated by white space and returns 0 if the
values represent a valid date; otherwise, the function returns 1.
- month_to_int
(Listing 8) takes a string representing a month and prints an integer
representation of the month. The function prints zero (0) if the string
cannot be converted to a month.
- ordinal_date (Listing 9) takes a year,
month, and day of the month and returns an integer representing the day of
the year, also known as the ordinal date.
Configurable Parameters
In ymd2ywd, two constants can be configured to specify
different assumptions. The first constant, FIRST_DOW, indicates when a week
begins. The second constant, W1_ANCHOR, defines a date that must be
included in the first week of the year. The value of W1_ANCHOR is an
integer representing an offset from January 1st. For example, W1_ANCHOR=0
means that January 1st must be included in the first week of the year;
W1_ANCHOR=1 means that January 2nd must be included in the first week of
the year, etc.
The Algorithm
Three arguments, a year, month, and day of the month,
are passed to the script. The is_valid_date function validates that the
arguments represent a valid date. If necessary, the month string is
converted to an integer. The year, month, and day values are assigned to
the variables Y, M, and D, respectively.
Values are computed and assigned to the variables DOW,
DAYS_TO_ADD, FIRST_DOW, and ORDINAL_LAST_DOW. $DOW is an integer
representing the day of the week (0=Sunday, 1=Monday, ... 6=Saturday)
corresponding to the given date. $DAYS_TO_ADD is the number of days that
must be added to DOW to reach the end of the week. $NEW_DOW is an integer
representing the ordinal day of the week as determined by $FIRST_DOW. And
$ORDINAL_LAST_DOW is the number of days from the beginning of the given
year until the last day of the week.
If $ORDINAL_LAST_DOW is less than or equal to
$W1_ANCHOR, then the last day of the week belongs to a week in the previous
year ($Y-1). In that case, WEEK_YEAR is set to $Y-1 and ORDINAL_LAST_DOW is
set to the number of days from the beginning of the year $WEEK_YEAR until
the last day of the week. Otherwise, WEEK_YEAR is set to $Y. In both cases,
DAYS_IN_WY is set to the number of days in the year $WEEK_YEAR.
The variable DAYS_TO_COUNT is tentatively set to $ORDINAL_LAST_DOW minus $W1_ANCHOR. If $DAYS_TO_COUNT is greater
than $DAYS_IN_WY, then the last day of the week belongs to a week in the
next year ($Y+1). In that case, DAYS_TO_COUNT is set to the remainder of
$DAYS_TO_COUNT divided by $DAYS_IN_WY. Then, WEEK_YEAR is set to $Y+1.
Finally, the week number is computed and assigned to
the variable WEEK_NUM. The week number is computed by dividing $DAYS_TO_COUNT by seven and rounding up. The converted
date is specified by the values of WEEK_YEAR, WEEK_NUM, and NEW_DOW.
Example 1
This example assumes that the first day of the week is
Sunday and the first week of the year must include January 1st. In this
case, ymd2ywd behaves as follows:
> ymd2ywd 2006 Jan 1
2006-W01-1
The -v (verbose) flag tells the script to print more details:
> ymd2ywd -v 2006 Jan 1
2006-1-1 is Sunday, 2006-W01-1
Example 2
This example assumes that the first day of the week
is Monday and the first week of the year must include January 1st. In this
case:
> ymd2ywd -v 2006 Jan 1
2006-1-1 is Sunday, 2006-W01-7
Example 3
This example assumes that the first day of the week is
Monday and the first week of the year must include January 4th. These
assumptions correspond to the ISO 8601 standard for week numbers. In this
case:
> ymd2ywd -v 2006 Jan 1
2006-1-1 is Sunday, 2005-W52-7
Note that saying "The first week of the year
must include January 4th" is equivalent to saying "The first
week of the year is the week that includes the first Thursday of the
year".
What's in the tar File
A tar file includes the ymd2ywd script and the
associated functions. Two simple scripts, print_next_date and print_year,
are also included. The print_year script uses print_next_date and ymd2ywd
to convert all of the dates in a given year from year-month-day format to
year-week-day format. Here is the output for "print_year 2005"
where FIRST_DOW equals Sunday and the W1_ANCHOR offset equals 0 (January 1
is in the first week):
2005-1-1 is Saturday, 2005-W01-7
2005-1-2 is Sunday, 2005-W02-1
2005-1-3 is Monday, 2005-W02-2
.
.
.
2005-12-29 is Thursday, 2005-W53-5
2005-12-30 is Friday, 2005-W53-6
2005-12-31 is Saturday, 2005-W53-7
Resources
1. McCarty, Rick. "Algorithm for Converting
Gregorian Dates to ISO 8601 Week Date", 1999 -- http://www.personal.ecu.edu/mccartyr/ISOwdALG.txt
2. Keith, Mike. "How about a date? Calculating
the day of the week", World of Words & Numbers -- http://users.aol.com/s6sj7gt/mikecal.htm
John Spurgeon is a software developer and systems
administrator for Intel's Factory Information Control Systems, IFICS,
in Aloha, Oregon. Outside of work, he enjoys turfgrass management,
triathlons, ultra-marathon cycling, and spending time with his family.
Ed Schaefer is a frequent contributor to Sys Admin. He is a software
developer and DBA for Intel's Factory Information Control Systems,
IFICS, in Aloha, Oregon. Ed also hosts the monthly Shell Corner column on
UnixReview.com. He can be reached at: shellcorner@comcast.net.
|