Listing 1 ymd2ywd
#!/bin/ksh
#########################################################################
#
# Usage: ymd2ywd [-v] year month day
#
# Purpose: Converts a date in year-month-day format to a date in
# year-week-day format.
#
# For the input date...
#
# year: must be greater than or equal to zero (0).
# month: may be an integer or a character string.
# day: is a valid day of the month.
#
# For the output date...
#
# year: is the year containing the output week, which may be
# different from the input week.
# week: is a 2-digit week number (01-53).
# day: is the ordinal day of the week (1-7).
#
# The result of the conversion is dependent on two constants
# (FIRST_DOW and W1_ANCHOR), which can be configured to implement
# various definitions of a week number.
#
# From http://www.personal.ecu.edu/mccartyr/aboutwdc.htm:
#
# "The program is based on the so-called Proleptic Greogrian Calendar.
# This is the present-day Gregorian Calendar extended indefinitely into
# the past and future. So the program makes no attempt at historical
# accuracy prior to the Gregorian Calendar Reforms in September 1752
# (Britain) or October 1582. For example, although the pre-Gregorian year
# 1500 was a leap year, the program treats it as a common year, in
# accordance with the Gregorian leap year rule."
#
# References:
#
# http://en.wikipedia.org/wiki/Week
# http://en.wikipedia.org/wiki/ISO_8601
# http://www.personal.ecu.edu/mccartyr/aboutwdc.htm
#
#########################################################################
#===================
# EXTERNAL FUNCTIONS
#===================
. $(dirname $0)/define_functions
#==========
# CONSTANTS
#==========
USAGE="Usage: $(basename $0) [-v] year month day (where year >= 0)"
SUN=0
MON=1
TUE=2
WED=3
THU=4
FRI=5
SAT=6
set -A DOW_STRINGS Sunday Monday Tuesday Wednesday Thursday Friday Saturday
readonly DOW_STRINGS SUN MON TUE WED THU FRI SAT
#================================================================
# CONFIGURABLE CONSTANTS
#
# Modify values of FIRST_DOW and W1_ANCHOR to implement alternate
# definitions of a week number. For example, the ISO 8601 week
# number is defined by FIRST_DOW=$MON and W1_ANCHOR=3. Intel work
# weeks are defined by FIRST_DOW=$SUN and W1_ANCHOR=0.
#================================================================
#----------------------------------------------------------
# FIRST_DOW: The first day of the week (Sun, Mon, ... Sat).
#----------------------------------------------------------
FIRST_DOW=$SUN
#-----------------------------------------------
# W1_ANCHOR:
#
# An integer representing a date that must occur
# in week 01. The value is an offset from Jan 1.
# For example:
#
# -2 => 30 Dec
# -1 => 31 Dec
# 0 => 1 Jan
# 1 => 2 Jan
# etc.
#-----------------------------------------------
W1_ANCHOR=0
readonly FIRST_DOW W1_ANCHOR
#===============
# MAIN PROCEDURE
#===============
#---------------
# Get the input.
#---------------
# The use of getopts seems to preclude inputting years < 0,
# because getopts sees the minus sign and interprets the
# year number as an invalid option. That's probably not a
# big deal for most users. Besides, the function
# day_of_week is limited to years > -7.
OUTPUT=normal
while getopts v name
do
case $name in
v)
OUTPUT=verbose
;;
?)
echo $USAGE
exit 1
;;
esac
done
shift $((OPTIND-1))
if [[ -z $@ ]]
then
echo $USAGE
exit 1
else
Y=$1
M=$2
D=$3
fi
#------------------------------------------------------------
# Validate the input. If necessary, convert $M to an integer.
#------------------------------------------------------------
if is_integer $M 2> /dev/null
then
if ! is_valid_date $Y $M $D
then
echo $USAGE
exit 1
fi
else
if is_valid_date $Y $M $D
then
M=$(month_to_int $M)
else
echo $USAGE
exit 1
fi
fi
#---------------------------------------------
# DOW: the day of the week for the given date.
# (0=Sun, 1=Mon, ... 6=Sat)
#---------------------------------------------
DOW=$(day_of_week $Y $M $D)
#-------------------------------------------------------
# DAYS_TO_ADD: number of days until the end of the week.
#-------------------------------------------------------
DAYS_TO_ADD=$(( ((6-$DOW) + FIRST_DOW)%7 ))
#----------------------------------------------
# NEW_DOW: integer representing day of the week
# as a function of $FIRST_DOW.
# (1=1st dow, 2=2nd dow, ... 7=7th dow)
#----------------------------------------------
NEW_DOW=$((7-$DAYS_TO_ADD))
#-----------------------------------------------------------
# ORDINAL_LAST_DOW: the number of days from the beginning of
# the given year until the last day of the week.
#-----------------------------------------------------------
ORDINAL_LAST_DOW=$(( $(ordinal_date $Y $M $D) + DAYS_TO_ADD ))
#----------------------------------------------------------
# Determine whether the last day of the week belongs to a
# week in the previous year. If it does then add the number
# of days from the previous year to the ordinal date for
# the last day of the week and set WEEK_YEAR to Y-1;
# otherwise, set WEEK_YEAR to Y.
#----------------------------------------------------------
if (( $ORDINAL_LAST_DOW <= $((W1_ANCHOR)) ))
then
WEEK_YEAR=$((Y-1))
DAYS_IN_WY=$(days_in_year $WEEK_YEAR)
ORDINAL_LAST_DOW=$((ORDINAL_LAST_DOW+DAYS_IN_WY))
else
WEEK_YEAR=$Y
DAYS_IN_WY=$(days_in_year $WEEK_YEAR)
fi
#---------------------------------------------------
# Determine whether the last day of the week belongs
# to a week in the next year (Y+1). If it does, then
# set WEEK_YEAR to the next year. Also, compute the
# number of days to count, which will be used to
# determine the week number.
#---------------------------------------------------
DAYS_TO_COUNT=$((ORDINAL_LAST_DOW-W1_ANCHOR))
if (( $DAYS_TO_COUNT > $DAYS_IN_WY ))
then
DAYS_TO_COUNT=$(( DAYS_TO_COUNT%DAYS_IN_WY ))
WEEK_YEAR=$((Y+1))
fi
#----------------------------------------------
# Implement a ceiling function.
# Determine the week number for $D-$M-$Y by
# dividing $DAYS_TO_COUNT by 7 and rounding up.
#----------------------------------------------
if (( $(( DAYS_TO_COUNT%7 )) > 0 ))
then
ROUND_UP=1
else
ROUND_UP=0
fi
WEEK_NUM=$(( (DAYS_TO_COUNT/7) + ROUND_UP ))
#------------------
# Print the result.
#------------------
if (( $WEEK_NUM < 10 ))
then
WEEK_NUM=0$WEEK_NUM
fi
if [[ $OUTPUT = verbose ]]
then
echo $Y-$M-$D is ${DOW_STRINGS[$DOW]}, ${WEEK_YEAR}-W${WEEK_NUM}-${NEW_DOW}
else
echo "${WEEK_YEAR}-W${WEEK_NUM}-${NEW_DOW}"
fi
# End Listing 1:
|