summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bash-completion/cal2
-rw-r--r--misc-utils/cal.19
-rw-r--r--misc-utils/cal.c141
3 files changed, 122 insertions, 30 deletions
diff --git a/bash-completion/cal b/bash-completion/cal
index 9a90a2a59..1510eb1fa 100644
--- a/bash-completion/cal
+++ b/bash-completion/cal
@@ -11,7 +11,7 @@ _cal_module()
esac
case $cur in
-*)
- OPTS="--one --three --sunday --monday --julian --year --version --help"
+ OPTS="--one --three --sunday --monday --julian --year --week --version --help"
COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
return 0
;;
diff --git a/misc-utils/cal.1 b/misc-utils/cal.1
index 7e8e6baf1..81f3b22a4 100644
--- a/misc-utils/cal.1
+++ b/misc-utils/cal.1
@@ -65,6 +65,9 @@ Display Julian dates (days one-based, numbered from January 1).
\fB\-y\fR, \fB\-\-year\fR
Display a calendar for the whole year.
.TP
+\fB\-w\fR, \fB\-\-week\fR
+Display week numbers in the calendar (US or ISO-8601).
+.TP
\fB\-\-color\fR [\fIwhen\fR]
Colorize output. The
.I when
@@ -104,6 +107,12 @@ September. By this time, most countries had recognized the reformation
(although a few did not recognize it until the early 1900's). Ten days
following that date were eliminated by the reformation, so the calendar for
that month is a bit unusual.
+.PP
+The week numbering depends on the first day of the week. If Sunday (the
+default) is used for the first day of the week, then the customary North
+American numbering will be used, i.e. first Sunday of the week starts the
+first week. If Monday is selected, then the ISO-8601 standard week numbering
+is used, where the first Thursday is in week number 1.
.SH HISTORY
A cal command appeared in Version 6 AT&T UNIX.
.SH AVAILABILITY
diff --git a/misc-utils/cal.c b/misc-utils/cal.c
index f91bc8ebe..92bd395e3 100644
--- a/misc-utils/cal.c
+++ b/misc-utils/cal.c
@@ -171,6 +171,7 @@ enum {
#define REFORMATION_MONTH 9 /* September */
#define FIRST_MISSING_DAY 639799 /* 3 Sep 1752 */
#define NUMBER_MISSING_DAYS 11 /* 11 day correction */
+#define YDAY_AFTER_MISSING 258 /* 14th in Sep 1752 */
#define DAYS_IN_YEAR 365 /* the common case, leap years are calculated */
#define MONTHS_IN_YEAR 12
@@ -223,6 +224,11 @@ static const int d_sep1752[MAXDAYS / 2] = {
SPACE, SPACE, SPACE, SPACE, SPACE, SPACE, SPACE
};
+enum {
+ WEEK_NUM_DISABLED = 0,
+ WEEK_NUM_ISO,
+ WEEK_NUM_US
+};
/* utf-8 can have up to 6 bytes per char; and an extra byte for ending \0 */
static char day_headings[J_WEEK_LEN * 6 + 1];
@@ -241,11 +247,12 @@ static void center(const char *, size_t, int);
static void day_array(int, int, long, int *);
static int day_in_week(int, int, long);
static int day_in_year(int, int, long);
-static void yearly(int, long, int);
-static int do_monthly(int, int, long, struct fmt_st*, int);
-static void monthly(int, int, long);
+static int week_number(int, int, long, int);
+static void yearly(int, long, int, int);
+static int do_monthly(int, int, long, int, struct fmt_st*, int);
+static void monthly(int, int, long, int);
static int two_header_lines(int month, long year);
-static void monthly3(int, int, long);
+static void monthly3(int, int, long, int);
static void __attribute__ ((__noreturn__)) usage(FILE * out);
static void headers_init(int);
@@ -253,7 +260,7 @@ int main(int argc, char **argv)
{
struct tm *local_time;
time_t now;
- int ch, day = 0, month = 0, yflag = 0;
+ int ch, day = 0, month = 0, yflag = 0, wflag = WEEK_NUM_DISABLED;
long year;
int num_months = NUM_MONTHS;
int colormode = UL_COLORMODE_AUTO;
@@ -269,6 +276,7 @@ int main(int argc, char **argv)
{"monday", no_argument, NULL, 'm'},
{"julian", no_argument, NULL, 'j'},
{"year", no_argument, NULL, 'y'},
+ {"week", no_argument, NULL, 'w'},
{"color", optional_argument, NULL, OPT_COLOR},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'h'},
@@ -327,7 +335,7 @@ int main(int argc, char **argv)
}
#endif
- while ((ch = getopt_long(argc, argv, "13mjsyVh", longopts, NULL)) != -1)
+ while ((ch = getopt_long(argc, argv, "13mjsywVh", longopts, NULL)) != -1)
switch(ch) {
case '1':
num_months = 1; /* default */
@@ -347,6 +355,9 @@ int main(int argc, char **argv)
case 'y':
yflag = 1;
break;
+ case 'w':
+ wflag = WEEK_NUM_US; /* default per weekstart */
+ break;
case OPT_COLOR:
if (optarg)
colormode = colormode_or_err(optarg,
@@ -364,6 +375,9 @@ int main(int argc, char **argv)
argc -= optind;
argv += optind;
+ if (wflag)
+ wflag = (weekstart == MONDAY ? WEEK_NUM_ISO : WEEK_NUM_US);
+
time(&now);
local_time = localtime(&now);
@@ -407,11 +421,11 @@ int main(int argc, char **argv)
day = 0;
if (yflag)
- yearly(day, year, julian);
+ yearly(day, year, julian, wflag);
else if (num_months == 1)
- monthly(day, month, year);
+ monthly(day, month, year, wflag);
else if (num_months == 3)
- monthly3(day, month, year);
+ monthly3(day, month, year, wflag);
return EXIT_SUCCESS;
}
@@ -448,12 +462,12 @@ static void headers_init(int julian)
full_month[i] = nl_langinfo(MON_1 + i);
}
-static int do_monthly(int day, int month, long year,
+static int do_monthly(int day, int month, long year, int wflag,
struct fmt_st *out, int header_hint)
{
int col, row, days[MAXDAYS];
char *p, lineout[FMT_ST_CHARS];
- size_t width = (julian ? J_WEEK_LEN : WEEK_LEN) - 1;
+ size_t width = (julian ? J_WEEK_LEN : WEEK_LEN) - 1 + (wflag?3:0);
int pos = 0;
day_array(day, month, year, days);
@@ -478,10 +492,21 @@ static int do_monthly(int day, int month, long year,
pos++;
}
- snprintf(out->s[pos++], FMT_ST_CHARS, "%s", day_headings);
+ snprintf(out->s[pos++], FMT_ST_CHARS, "%s%s",(wflag?" ":""), day_headings);
for (row = 0; row < DAYS_IN_WEEK - 1; row++) {
int has_hl = 0;
- for (col = 0, p = lineout; col < DAYS_IN_WEEK; col++) {
+ p = lineout;
+ if (wflag)
+ for (col = 0; col < DAYS_IN_WEEK; col++) {
+ int xd = days[row * DAYS_IN_WEEK + col];
+ if (xd != SPACE) {
+ p += sprintf(p,"%2d ",
+ week_number(xd & ~TODAY_FLAG,month,year,wflag));
+ break;
+ } else if (col+1 == DAYS_IN_WEEK)
+ p += sprintf(p," ");
+ }
+ for (col = 0; col < DAYS_IN_WEEK; col++) {
int xd = days[row * DAYS_IN_WEEK + col];
if (xd != SPACE && (xd & TODAY_FLAG))
has_hl = 1;
@@ -496,12 +521,12 @@ static int do_monthly(int day, int month, long year,
return pos;
}
-static void monthly(int day, int month, long year)
+static void monthly(int day, int month, long year, int wflag)
{
int i, rows;
struct fmt_st out;
- rows = do_monthly(day, month, year, &out, -1);
+ rows = do_monthly(day, month, year, wflag, &out, -1);
for (i = 0; i < rows; i++) {
my_putstring(out.s[i]);
my_putstring("\n");
@@ -521,7 +546,7 @@ static int two_header_lines(int month, long year)
return 0;
}
-static void monthly3(int day, int month, long year)
+static void monthly3(int day, int month, long year, int wflag)
{
char lineout[FMT_ST_CHARS];
int i;
@@ -556,9 +581,9 @@ static void monthly3(int day, int month, long year)
rows = FMT_ST_LINES;
else
rows = FMT_ST_LINES - 1;
- do_monthly(day, prev_month, prev_year, &out_prev, two_lines);
- do_monthly(day, month, year, &out_curm, two_lines);
- do_monthly(day, next_month, next_year, &out_next, two_lines);
+ do_monthly(day, prev_month, prev_year, wflag, &out_prev, two_lines);
+ do_monthly(day, month, year, wflag, &out_curm, two_lines);
+ do_monthly(day, next_month, next_year, wflag, &out_next, two_lines);
width = (julian ? J_WEEK_LEN : WEEK_LEN) -1;
for (i = 0; i < (two_lines ? 3 : 2); i++) {
@@ -585,25 +610,25 @@ static void monthly3(int day, int month, long year)
}
}
-static void yearly(int day, long year, int julian)
+static void yearly(int day, long year, int julian, int wflag)
{
int col, *dp, i, month, row, which_cal;
int maxrow, sep_len, week_len;
int days[MONTHS_IN_YEAR][MAXDAYS];
char *p;
/* three weeks + separators + \0 */
- char lineout[ sizeof(day_headings) + 2 +
- sizeof(day_headings) + 2 +
- sizeof(day_headings) + 1 ];
-
+ int wnumlen = (wflag?3:0);
+ char lineout[ wnumlen + sizeof(day_headings) + 2 +
+ wnumlen + sizeof(day_headings) + 2 +
+ wnumlen + sizeof(day_headings) + 1 ];
if (julian) {
maxrow = J_MONTH_COLS;
sep_len = J_HEAD_SEP;
- week_len = J_WEEK_LEN;
+ week_len = J_WEEK_LEN + wnumlen;
} else {
maxrow = MONTH_COLS;
sep_len = HEAD_SEP;
- week_len = WEEK_LEN;
+ week_len = WEEK_LEN + wnumlen;
}
snprintf(lineout, sizeof(lineout), "%ld", year);
/* 2013-04-28: The -1 near sep_len makes year header to be
@@ -625,17 +650,32 @@ static void yearly(int day, long year, int julian)
}
if (julian)
snprintf(lineout, sizeof(lineout),
- "\n%s%*s %s\n", day_headings, sep_len, "", day_headings);
+ "\n%*s%s%*s %*s%s\n",
+ wnumlen,"", day_headings, sep_len, "",
+ wnumlen,"", day_headings);
else
snprintf(lineout, sizeof(lineout),
- "\n%s%*s %s%*s %s\n", day_headings, sep_len,
- "", day_headings, sep_len, "", day_headings);
+ "\n%*s%s%*s %*s%s%*s %*s%s\n",
+ wnumlen,"", day_headings, sep_len, "",
+ wnumlen,"", day_headings, sep_len, "",
+ wnumlen,"", day_headings);
my_putstring(lineout);
for (row = 0; row < DAYS_IN_WEEK - 1; row++) {
p = lineout;
for (which_cal = 0; which_cal < maxrow; which_cal++) {
dp = &days[month + which_cal][row * DAYS_IN_WEEK];
+ if (wflag) {
+ for (col = 0; col < DAYS_IN_WEEK; col++) {
+ int xd = days[month + which_cal][row * DAYS_IN_WEEK + col];
+ if (xd != SPACE) {
+ p += sprintf(p,"%2d ",
+ week_number(xd & ~TODAY_FLAG,month + which_cal + 1,year,wflag));
+ break;
+ } else if (col+1 == DAYS_IN_WEEK)
+ p += sprintf(p," ");
+ }
+ }
for (col = 0; col < DAYS_IN_WEEK; col++)
p = ascii_day(p, *dp++);
p += sprintf(p, " ");
@@ -729,6 +769,48 @@ static int day_in_week(int d, int m, long y)
return NONEDAY;
}
+/*
+ * week_number
+ * return the week number of a given date, 1..53.
+ * Supports ISO-8601 and North American modes.
+ * Day may be given as Julian day of the year mode, in which
+ * case the month is disregarded entirely.
+ */
+static int week_number(int day, int month, long year, int wflag) {
+ int fday = 0,wday,yday;
+ wday = day_in_week(1,1,year);
+ if (wflag == WEEK_NUM_ISO)
+ fday = wday + (wday>=FRIDAY?-2:5);
+ else /* WEEK_NUM_US */
+ /* according to gcal, the first Sun is in the first week */
+ fday = wday + (wday==SUNDAY?6:-1);
+ /* according to wikipedia, i.e. the first Sat is in the first week */
+ /* fday = wday + 6; */
+ /* For julian dates the month can be set to 1, the global julian variable
+ * cannot be relied upon here, because we may recurse internally for
+ * 31.12. which would not work. */
+ if (day > 31) month = 1;
+ yday = day_in_year(day,month,year);
+ if (year == REFORMATION_YEAR)
+ if (yday >= YDAY_AFTER_MISSING)
+ fday -= NUMBER_MISSING_DAYS;
+ /* Last year is last year */
+ if (yday+fday < 7)
+ return week_number(31,12,year-1,wflag);
+ /* Or it could be part of the next year.
+ * The reformation year had less days than 365 making this check
+ * invalid, but reformation year ended on Sunday and in week 51,
+ * so it's ok here. */
+ if (wflag == WEEK_NUM_ISO && yday >= 363 &&
+ day_in_week(day,month,year) >= MONDAY &&
+ day_in_week(day,month,year) <= WEDNESDAY &&
+ day_in_week(31,12,year) >= MONDAY &&
+ day_in_week(31,12,year) <= WEDNESDAY) {
+ return week_number(1,1,year+1,wflag);
+ }
+ return (yday+fday) / 7;
+}
+
static char *ascii_day(char *p, int day)
{
int display, val;
@@ -818,6 +900,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
fputs(_(" -m, --monday Monday as first day of week\n"), out);
fputs(_(" -j, --julian output Julian dates\n"), out);
fputs(_(" -y, --year show the whole year\n"), out);
+ fputs(_(" -w, --week show US or ISO-8601 week numbers\n"), out);
fputs(_(" --color[=<when>] colorize messages (auto, always or never)\n"), out);
fputs(USAGE_SEPARATOR, out);