ncal(1): handle Sweden's transitional calendar (1700-1712)

Sweden used a unique intermediate calendar during 1700-1712.  In 1700,
Sweden omitted the Julian leap day (Feb 29, 1700) to begin a gradual
transition to the Gregorian calendar.  The transition was abandoned
mid-way due to the Great Northern War; to revert to the Julian calendar,
an extra day "Feb 30, 1712" was added.  Sweden finally adopted the
Gregorian calendar in 1753 (already handled correctly by ncal).

ncal previously displayed Feb 1700 as a 29-day month and Feb 1712 as a
29-day month, both incorrect for the Swedish (-s SE) calendar mode.

Add flag_sweden, which is set when the SE country code is selected (via
-s SE or the locale) and cleared by -J, since -J selects the plain
Julian calendar.  When active, sndaysr() and sdater() apply the one-day
offset that characterised the Swedish transitional calendar:

  - Feb 1700: displayed with 28 days (no leap day)
  - 1700-03-01 to 1712-02-30: dates run one day ahead of Julian, so
    weekdays are correctly anchored to the actual solar day
  - Feb 1712: displayed with 30 days (the unique "Feb 30" day)
  - 1712-03-01 onward: back to Julian until the 1753 Gregorian switch

Add ATF tests covering the 3-month and Julian-day views around both
boundaries and the -J override.

Reported-by:	John Costello <https://joxn.org>
Signed-off-by:	Juan Michelini <[email protected]>
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Reviewed by:	asomers, js
MFC after:	2 weeks
Pull Request:	https://github.com/freebsd/freebsd-src/pull/2353
This commit is contained in:
Juan Michelini authored and Alan Somers committed 2026-10-12 18:30:07 -06:00
1 parent 398254f999
commit 1ca7f66185
8 files changed
+105

No files matched your search

+24
View File
@@ -164,6 +164,7 @@ static int nswitch; /* user defined switch date */
static int nswitchb; /* switch date for backward compatibility */
static int highlightdate;
static bool flag_monday; /* user wants week starts on Monday */
static bool flag_sweden; /* Sweden, 1700-1712 */
static char *center(char *s, char *t, int w);
static wchar_t *wcenter(wchar_t *s, wchar_t *t, int w);
@@ -219,6 +220,7 @@ main(int argc, char *argv[])
flag_highlight = isatty(STDOUT_FILENO);
flag_weeks = 0;
flag_monday = false;
flag_sweden = false;
/*
* Use locale to determine the country code,
@@ -243,6 +245,7 @@ main(int argc, char *argv[])
} else {
nswitch = ndaysj(&p->dt);
dftswitch = p;
flag_sweden = (strcmp(p->cc, "SE") == 0);
}
@@ -285,6 +288,7 @@ main(int argc, char *argv[])
usage();
nswitch = ndaysj(&never);
flag_julian_cal = 1;
flag_sweden = false;
break;
case 'C':
flag_backward = 1;
@@ -342,6 +346,7 @@ main(int argc, char *argv[])
errx(EX_USAGE,
"%s: invalid country code", optarg);
nswitch = ndaysj(&(p->dt));
flag_sweden = (strcmp(p->cc, "SE") == 0);
break;
case 'w':
if (flag_backward)
@@ -1015,6 +1020,11 @@ static int
sndaysr(struct date *d)
{
/* Sweden skipped Feb 29, 1700 and added Feb 30, 1712. */
if (flag_sweden && d->y * 12 + d->m >= 1700 * 12 + 3 &&
d->y * 12 + d->m < 1712 * 12 + 3)
return (ndaysj(d) - 1);
if (nswitch != 0)
if (nswitch < ndaysj(d))
return (ndaysg(d));
@@ -1043,6 +1053,20 @@ static struct date *
sdater(int nd, struct date *d)
{
/* Inverse of the Sweden case in sndaysr(). */
if (flag_sweden) {
date start = { 1700, 2, 29 }, end = { 1712, 3, 1 };
if (nd >= ndaysj(&start) && nd < ndaysj(&end)) {
jdate(nd + 1, d);
if (d->y == 1712 && d->m == 3) {
d->m = 2;
d->d = 30;
}
return (d);
}
}
if (nswitch < nd)
return (gdate(nd, d));
else
+5
View File
@@ -91,5 +91,10 @@ ${PACKAGE}FILES+= regress.s-r-B-nhl.out
${PACKAGE}FILES+= regress.s-r-gmgy-nhl.out
${PACKAGE}FILES+= regress.s-r-m-nhl.out
${PACKAGE}FILES+= regress.s-r-mgy-nhl.out
${PACKAGE}FILES+= regress.se-J-170002-nhl.out
${PACKAGE}FILES+= regress.se-r-3-1700-nhl.out
${PACKAGE}FILES+= regress.se-r-3-1712-nhl.out
${PACKAGE}FILES+= regress.se-r-jd-170003-nhl.out
${PACKAGE}FILES+= regress.se-r-jd-171202-nhl.out
.include <bsd.test.mk>
+36
View File
@@ -136,6 +136,39 @@ ncal_options_body()
ncal_out s-r-gmgy-nhl -N -d 2008.03 1 2007
}
atf_test_case sweden
sweden_head()
{
atf_set "descr" "Sweden's 1700-1712 transitional calendar"
}
sweden_body()
{
# February 1700 has no leap day and February 1712 has 30 days.
ncal_out se-r-3-1700-nhl -N -h -3 -s SE -d 1700.02
ncal_out se-r-3-1712-nhl -N -h -3 -s SE -d 1712.02
}
atf_test_case sweden_jd
sweden_jd_head()
{
atf_set "descr" "Sweden's transitional calendar in Julian-day format"
}
sweden_jd_body()
{
ncal_out se-r-jd-170003-nhl -N -h -j -s SE 3 1700
ncal_out se-r-jd-171202-nhl -N -h -j -s SE 2 1712
}
atf_test_case sweden_julian
sweden_julian_head()
{
atf_set "descr" "-J overrides the Swedish transitional calendar"
}
sweden_julian_body()
{
ncal_out se-J-170002-nhl -N -h -s SE -J 2 1700
}
atf_init_test_cases()
{
atf_add_test_case year_md
@@ -145,4 +178,7 @@ atf_init_test_cases()
atf_add_test_case invalid_args
atf_add_test_case cal_options
atf_add_test_case ncal_options
atf_add_test_case sweden
atf_add_test_case sweden_jd
atf_add_test_case sweden_julian
}
@@ -0,0 +1,8 @@
February 1700
Mo 5 12 19 26
Tu 6 13 20 27
We 7 14 21 28
Th 1 8 15 22 29
Fr 2 9 16 23
Sa 3 10 17 24
Su 4 11 18 25
@@ -0,0 +1,8 @@
January 1700 February 1700 March 1700
Mo 1 8 15 22 29 5 12 19 26 5 12 19 26
Tu 2 9 16 23 30 6 13 20 27 6 13 20 27
We 3 10 17 24 31 7 14 21 28 7 14 21 28
Th 4 11 18 25 1 8 15 22 1 8 15 22 29
Fr 5 12 19 26 2 9 16 23 2 9 16 23 30
Sa 6 13 20 27 3 10 17 24 3 10 17 24 31
Su 7 14 21 28 4 11 18 25 4 11 18 25
@@ -0,0 +1,8 @@
January 1712 February 1712 March 1712
Mo 1 8 15 22 29 5 12 19 26 3 10 17 24 31
Tu 2 9 16 23 30 6 13 20 27 4 11 18 25
We 3 10 17 24 31 7 14 21 28 5 12 19 26
Th 4 11 18 25 1 8 15 22 29 6 13 20 27
Fr 5 12 19 26 2 9 16 23 30 7 14 21 28
Sa 6 13 20 27 3 10 17 24 1 8 15 22 29
Su 7 14 21 28 4 11 18 25 2 9 16 23 30
@@ -0,0 +1,8 @@
March 1700
Mo 64 71 78 85
Tu 65 72 79 86
We 66 73 80 87
Th 60 67 74 81 88
Fr 61 68 75 82 89
Sa 62 69 76 83 90
Su 63 70 77 84
@@ -0,0 +1,8 @@
February 1712
Mo 36 43 50 57
Tu 37 44 51 58
We 38 45 52 59
Th 32 39 46 53 60
Fr 33 40 47 54 61
Sa 34 41 48 55
Su 35 42 49 56