hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

daydate.ha (16570B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 // Hare internally uses the Unix epoch (1970-01-01) for calendrical logic. Here
      5 // we provide useful constant for working with the astronomically numbered
      6 // proleptic Gregorian calendar, as offsets from the Hare epoch.
      7 
      8 // The Hare epochal day of the Julian Day Number.
      9 export def EPOCHDAY_JULIAN: i64 = -2440588;
     10 
     11 // The Hare epochal day of the Gregorian Common Era.
     12 export def EPOCHDAY_GREGORIAN: i64 = -719164;
     13 
     14 // Number of days in the Gregorian 400 year cycle
     15 def GREGORIAN_CYCLE_DAYS: i64 = 146097;
     16 
     17 fn has(item: int, list: int...) bool = {
     18 	for (let member .. list) {
     19 		if (member == item) {
     20 			return true;
     21 		};
     22 	};
     23 	return false;
     24 };
     25 
     26 // Calculates whether a year is a leap year.
     27 export fn isleapyear(y: int) bool = {
     28 	return if (y % 4 != 0) false
     29 	else if (y % 100 != 0) true
     30 	else if (y % 400 != 0) false
     31 	else true;
     32 };
     33 
     34 // Calculates whether a given year, month, and day-of-month, is a valid date.
     35 fn is_valid_ymd(y: int, m: int, d: int) bool = {
     36 	return m >= 1 && m <= 12 && d >= 1 &&
     37 		d <= calc_days_in_month(y, m);
     38 };
     39 
     40 // Calculates whether a given year, and day-of-year, is a valid date.
     41 fn is_valid_yd(y: int, yd: int) bool = {
     42 	return yd >= 1 && yd <= calc_days_in_year(y);
     43 };
     44 
     45 // Calculates whether a given ISO week-numbering year, and day-of-year,
     46 // is a valid date.
     47 fn is_valid_isoywd(iy: int, iw: int, wd: int) bool = {
     48 	return (
     49 		iw > 0 && iw <= (if (islongisoyear(iy)) 53 else 52)
     50 		&& wd >= MONDAY && wd <= SUNDAY
     51 	);
     52 };
     53 
     54 // Calculates the number of days in the given month of the given year.
     55 fn calc_days_in_month(y: int, m: int) int = {
     56 	const days_per_month: [_]int = [
     57 		31, -1, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
     58 	];
     59 	if (m == 2) {
     60 		return if (isleapyear(y)) 29 else 28;
     61 	} else {
     62 		return days_per_month[m - 1];
     63 	};
     64 };
     65 
     66 // Calculates the number of days in a given year.
     67 fn calc_days_in_year(y: int) int = {
     68 	return if (isleapyear(y)) 366 else 365;
     69 };
     70 
     71 // Calculates the day-of-week of January 1st, given a year.
     72 fn calc_janfirstweekday(y: int) int = {
     73 	const y = (y % 400) + 400; // keep year > 0 (using Gregorian cycle)
     74 	// Gauss' algorithm
     75 	const wd = (5 * ((y - 1) % 4)
     76 		+ 4 * ((y - 1) % 100)
     77 		+ 6 * ((y - 1) % 400)
     78 	) % 7;
     79 	return wd;
     80 };
     81 
     82 // Calculates whether an ISO week-numbering year has
     83 // 53 weeks (long) or 52 weeks (short).
     84 fn islongisoyear(y: int) bool = {
     85 	const jan1 = calc_janfirstweekday(y);
     86 	return jan1 == THURSDAY || (isleapyear(y) && jan1 == WEDNESDAY);
     87 };
     88 
     89 // Calculates the era, given a year.
     90 fn calc_era(y: int) int = {
     91 	return if (y >= 0) {
     92 		yield 1; // CE "Common Era"
     93 	} else {
     94 		yield 0; // BCE "Before Common Era"
     95 	};
     96 };
     97 
     98 // Calculates the year, month, and day-of-month, given an epochal day.
     99 fn calc_ymd(e: i64) (int, int, int) = {
    100 	// Algorithm adapted from:
    101 	// https://en.wikipedia.org/wiki/Julian_day#Julian_or_Gregorian_calendar_from_Julian_day_number
    102 	//
    103 	// TODO: Review, cite, verify, annotate.
    104 
    105 	// workaround for dates before -4716 March 1st
    106 	let E = e;
    107 	let cycles = 0;
    108 	for (E < -2441951) {
    109 		E += GREGORIAN_CYCLE_DAYS;
    110 		cycles += 1;
    111 	};
    112 
    113 	const J = E - EPOCHDAY_JULIAN;
    114 
    115 	const b = 274277;
    116 	const c = -38;
    117 	const j = 1401;
    118 	const m = 2;
    119 	const n = 12;
    120 	const p = 1461;
    121 	const r = 4;
    122 	const s = 153;
    123 	const u = 5;
    124 	const v = 3;
    125 	const w = 2;
    126 	const y = 4716;
    127 
    128 	const f = J + j + (((4 * J + b) / GREGORIAN_CYCLE_DAYS) * 3) / 4 + c;
    129 	const a = r * f + v;
    130 	const g = (a % p) / r;
    131 	const h = u * g + w;
    132 
    133 	const D = (h % s) / u + 1;
    134 	const M = ((h / s + m) % n) + 1;
    135 	const Y = (a / p) - y + (n + m - M) / n;
    136 
    137 	const Y = Y - (400 * cycles);
    138 
    139 	return (Y: int, M: int, D: int);
    140 };
    141 
    142 // Calculates the day-of-year, given a year, month, and day-of-month.
    143 fn calc_yearday(y: int, m: int, d: int) int = {
    144 	const months_firsts: [_]int = [
    145 		0, 31, 59,
    146 		90, 120, 151,
    147 		181, 212, 243,
    148 		273, 304, 334,
    149 	];
    150 
    151 	if (m > FEBRUARY && isleapyear(y)) {
    152 		return months_firsts[m - 1] + d + 1;
    153 	} else {
    154 		return months_firsts[m - 1] + d;
    155 	};
    156 };
    157 
    158 // Calculates the ISO week-numbering year,
    159 // given a year, month, day-of-month, and day-of-week.
    160 fn calc_isoweekyear(y: int, m: int, d: int, wd: int) int = {
    161 	if (
    162 		// if the date is within a week whose Thursday
    163 		// belongs to the previous Gregorian year
    164 		m == JANUARY && (
    165 			(d == 1 && has(wd, FRIDAY, SATURDAY, SUNDAY)) ||
    166 			(d == 2 && has(wd, SATURDAY, SUNDAY)) ||
    167 			(d == 3 && has(wd, SUNDAY))
    168 		)
    169 	) {
    170 		return y - 1;
    171 	} else if (
    172 		// if the date is within a week whose Thursday
    173 		// belongs to the next Gregorian year
    174 		m == DECEMBER && (
    175 			(d == 29 && has(wd, MONDAY)) ||
    176 			(d == 30 && has(wd, MONDAY, TUESDAY)) ||
    177 			(d == 31 && has(wd, MONDAY, TUESDAY, WEDNESDAY))
    178 		)
    179 	) {
    180 		return y + 1;
    181 	} else {
    182 		return y;
    183 	};
    184 };
    185 
    186 // Calculates the ISO week,
    187 // given a year, and week.
    188 fn calc_isoweek(y: int, w: int) int = {
    189 	switch (w) {
    190 	case 0 =>
    191 		return if (islongisoyear(y - 1)) 53 else 52;
    192 	case 53 =>
    193 		return if (islongisoyear(y)) 53 else 1;
    194 	case =>
    195 		return w;
    196 	};
    197 };
    198 
    199 // Calculates the week within a Gregorian year [0..53],
    200 // given a day-of-year and day-of-week.
    201 // All days in a year before the year's first Monday belong to week 0.
    202 fn calc_week(yd: int, wd: int) int = {
    203 	return (yd - wd + 9) / 7;
    204 };
    205 
    206 // Calculates the week within a Gregorian year [0..53],
    207 // given a day-of-year and day-of-week.
    208 // All days in a year before the year's first Sunday belong to week 0.
    209 fn calc_sundayweek(yd: int, wd: int) int = {
    210 	return (yd + 6 - ((wd + 1) % 7)) / 7;
    211 };
    212 
    213 // Calculates the day-of-week, given a epochal day,
    214 // from Monday=0 to Sunday=6.
    215 fn calc_weekday(e: i64) int = {
    216 	const wd = ((e + 3) % 7): int;
    217 	return (wd + 7) % 7;
    218 };
    219 
    220 // Calculates the daydate,
    221 // given a year, month, and day-of-month.
    222 fn calc_daydate__ymd(y: int, m: int, d: int) (i64 | invalid) = {
    223 	if (!is_valid_ymd(y, m, d)) {
    224 		return invalid;
    225 	};
    226 
    227 	// Algorithm adapted from:
    228 	// https://en.wikipedia.org/wiki/Julian_day
    229 	//
    230 	// TODO: Review, cite, verify, annotate.
    231 
    232 	// workaround for dates before -4800 March 1st
    233 	let Y = y;
    234 	let cycles = 0;
    235 	for (Y <= -4800) {
    236 		Y += 400; // Gregorian 400 year cycle
    237 		cycles += 1;
    238 	};
    239 
    240 	const jdn = ( // Julian Date Number
    241 		(1461 * (Y + 4800 + (m - 14) / 12)) / 4
    242 		+ (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12
    243 		- (3 * ((Y + 4900 + (m - 14) / 12) / 100)) / 4
    244 		+ d
    245 		- 32075
    246 	);
    247 
    248 	const e = jdn + EPOCHDAY_JULIAN - (GREGORIAN_CYCLE_DAYS * cycles);
    249 
    250 	return e;
    251 };
    252 
    253 // Calculates the daydate,
    254 // given a year, week, and day-of-week.
    255 fn calc_daydate__ywd(y: int, w: int, wd: int) (i64 | invalid) = {
    256 	const jan1wd = calc_janfirstweekday(y);
    257 	const yd = wd - jan1wd + 7 * w;
    258 	return calc_daydate__yd(y, yd)?;
    259 };
    260 
    261 // Calculates the daydate,
    262 // given a year and day-of-year.
    263 fn calc_daydate__yd(y: int, yd: int) (i64 | invalid) = {
    264 	if (yd < 1 || yd > calc_days_in_year(y)) {
    265 		return invalid;
    266 	};
    267 	return calc_daydate__ymd(y, 1, 1)? + yd - 1;
    268 };
    269 
    270 // Calculates the daydate,
    271 // given an ISO week-numbering year, ISO week, and day-of-week.
    272 fn calc_daydate__isoywd(iy: int, iw: int, wd: int) (i64 | invalid) = {
    273 	if (!is_valid_isoywd(iy, iw, wd)) {
    274 		return invalid;
    275 	};
    276 	const jan4 = calc_daydate__ymd(iy, 1, 4)?;
    277 	const isoyearstart = jan4 - calc_weekday(jan4);
    278 	return isoyearstart + (iw - 1) * 7 + wd;
    279 };
    280 
    281 @test fn calc_daydate__ymd() void = {
    282 	const cases = [
    283 		(( -768,  2,  5),  -999999, false),
    284 		((   -1, 12, 31),  -719529, false),
    285 		((    0,  1,  1),  -719528, false),
    286 		((    0,  1,  2),  -719527, false),
    287 		((    0, 12, 31),  -719163, false),
    288 		((    1,  1,  1),  -719162, false),
    289 		((    1,  1,  2),  -719161, false),
    290 		(( 1965,  3, 23),    -1745, false),
    291 		(( 1969, 12, 31),       -1, false),
    292 		(( 1970,  1,  1),        0, false),
    293 		(( 1970,  1,  2),        1, false),
    294 		(( 1999, 12, 31),    10956, false),
    295 		(( 2000,  1,  1),    10957, false),
    296 		(( 2000,  1,  2),    10958, false),
    297 		(( 2038,  1, 18),    24854, false),
    298 		(( 2038,  1, 19),    24855, false),
    299 		(( 2038,  1, 20),    24856, false),
    300 		(( 2243, 10, 17),   100000, false),
    301 		(( 4707, 11, 28),   999999, false),
    302 		(( 4707, 11, 29),  1000000, false),
    303 		((29349,  1, 25),  9999999, false),
    304 
    305 		(( 1970,-99,-99),  0, true),
    306 		(( 1970, -9, -9),  0, true),
    307 		(( 1970, -1, -1),  0, true),
    308 		(( 1970,  0,  0),  0, true),
    309 		(( 1970,  0,  1),  0, true),
    310 		(( 1970,  1, 99),  0, true),
    311 		(( 1970, 99, 99),  0, true),
    312 	];
    313 	for (let (params, expect, should_error) .. cases) {
    314 		const actual = calc_daydate__ymd(
    315 			params.0, params.1, params.2,
    316 		);
    317 
    318 		if (should_error) {
    319 			assert(actual is invalid, "invalid date accepted");
    320 		} else {
    321 			assert(actual is i64, "valid date not accepted");
    322 			assert(actual as i64 == expect, "date miscalculation");
    323 		};
    324 	};
    325 };
    326 
    327 @test fn calc_daydate__ywd() void = {
    328 	const cases = [
    329 		(( -768,  0, 4), -1000034),
    330 		(( -768,  5, 4), -999999),
    331 		((   -1, 52, 5), -719529),
    332 		((    0,  0, 6), -719528),
    333 		((    0,  0, 7), -719527),
    334 		((    0, 52, 7), -719163),
    335 		((    1,  0, 1), -719162),
    336 		((    1,  0, 2), -719161),
    337 		(( 1965, 12, 2), -1745),
    338 		(( 1969, 52, 3), -1),
    339 		(( 1970,  0, 4), 0),
    340 		(( 1970,  0, 5), 1),
    341 		(( 1999, 52, 5), 10956),
    342 		(( 2000,  0, 6), 10957),
    343 		(( 2000,  0, 7), 10958),
    344 		(( 2020,  0, 3), 18262),
    345 		(( 2022,  9, 1), 19051),
    346 		(( 2022,  9, 2), 19052),
    347 		(( 2023, 51, 7), 19715),
    348 		(( 2024,  8, 3), 19781),
    349 		(( 2024,  8, 4), 19782),
    350 		(( 2024,  8, 5), 19783),
    351 		(( 2024, 49, 4), 20069),
    352 		(( 2024, 52, 2), 20088),
    353 		(( 2038,  3, 1), 24854),
    354 		(( 2038,  3, 2), 24855),
    355 		(( 2038,  3, 3), 24856),
    356 		(( 2243, 41, 2), 99993),
    357 		(( 4707, 47, 4), 999999),
    358 		(( 4707, 47, 5), 1000000),
    359 		((29349,  3, 6), 9999999),
    360 	];
    361 
    362 	for (let (ywd, expected) .. cases) {
    363 		const actual = calc_daydate__ywd(ywd.0, ywd.1, ywd.2)!;
    364 		assert(actual == expected,
    365 			"incorrect calc_daydate__ywd() result");
    366 	};
    367 };
    368 
    369 @test fn calc_daydate__yd() void = {
    370 	const cases = [
    371 		( -768, 36,  -999999),
    372 		(   -1, 365, -719529),
    373 		(    0, 1,   -719528),
    374 		(    0, 2,   -719527),
    375 		(    0, 366, -719163),
    376 		(    1, 1,   -719162),
    377 		(    1, 2,   -719161),
    378 		( 1965, 82,  -1745  ),
    379 		( 1969, 365, -1     ),
    380 		( 1970, 1,   0      ),
    381 		( 1970, 2,   1      ),
    382 		( 1999, 365, 10956  ),
    383 		( 2000, 1,   10957  ),
    384 		( 2000, 2,   10958  ),
    385 		( 2038, 18,  24854  ),
    386 		( 2038, 19,  24855  ),
    387 		( 2038, 20,  24856  ),
    388 		( 2243, 290, 100000 ),
    389 		( 4707, 332, 999999 ),
    390 		( 4707, 333, 1000000),
    391 		(29349, 25,  9999999),
    392 	];
    393 
    394 	for (let (y, yd, expected) .. cases) {
    395 		const actual = calc_daydate__yd(y, yd)!;
    396 		assert(expected == actual,
    397 			"error in date calculation from yd");
    398 	};
    399 	assert(calc_daydate__yd(2020, 0) is invalid,
    400 		"calc_daydate__yd() did not reject invalid yearday");
    401 	assert(calc_daydate__yd(2020, 400) is invalid,
    402 		"calc_daydate__yd() did not reject invalid yearday");
    403 };
    404 
    405 @test fn calc_daydate__isoywd() void = {
    406 	const testcases = [
    407 		(( 1965, 12,  1),    -1745, false),
    408 		(( 1970,  1,  2),       -1, false),
    409 		(( 1970,  1,  3),        0, false),
    410 		(( 1970,  1,  4),        1, false),
    411 		(( 1999, 52,  4),    10956, false),
    412 		(( 1999, 52,  5),    10957, false),
    413 		(( 1999, 52,  6),    10958, false),
    414 		(( 2038,  3,  0),    24854, false),
    415 		(( 2038,  3,  1),    24855, false),
    416 		(( 2038,  3,  2),    24856, false),
    417 		(( 2243, 42,  1),   100000, false),
    418 		(( 4707, 48,  3),   999999, false),
    419 		(( 4707, 48,  4),  1000000, false),
    420 		((29349,  4,  5),  9999999, false),
    421 
    422 		(( 1970,-99,-99),  0, true),
    423 		(( 1970, -9, -9),  0, true),
    424 		(( 1970, -1, -1),  0, true),
    425 		(( 1970,  0,  0),  0, true),
    426 		(( 1970,  0,  1),  0, true),
    427 		(( 1970,  1, 99),  0, true),
    428 		(( 1970, 99, 99),  0, true),
    429 	];
    430 	for (let (params, expect, should_error) .. testcases) {
    431 		const actual = calc_daydate__isoywd(
    432 			params.0, params.1, params.2,
    433 		);
    434 
    435 		if (should_error) {
    436 			assert(actual is invalid, "invalid date accepted");
    437 		} else {
    438 			assert(actual is i64, "valid date not accepted");
    439 			assert(actual as i64 == expect, "date miscalculation");
    440 		};
    441 	};
    442 };
    443 
    444 @test fn calc_ymd() void = {
    445 	const cases = [
    446 		(-999999, ( -768,  2,  5)),
    447 		(-719529, (   -1, 12, 31)),
    448 		(-719528, (    0,  1,  1)),
    449 		(-719527, (    0,  1,  2)),
    450 		(-719163, (    0, 12, 31)),
    451 		(-719162, (    1,  1,  1)),
    452 		(-719161, (    1,  1,  2)),
    453 		(  -1745, ( 1965,  3, 23)),
    454 		(     -1, ( 1969, 12, 31)),
    455 		(      0, ( 1970,  1,  1)),
    456 		(      1, ( 1970,  1,  2)),
    457 		(  10956, ( 1999, 12, 31)),
    458 		(  10957, ( 2000,  1,  1)),
    459 		(  10958, ( 2000,  1,  2)),
    460 		(  24854, ( 2038,  1, 18)),
    461 		(  24855, ( 2038,  1, 19)),
    462 		(  24856, ( 2038,  1, 20)),
    463 		( 100000, ( 2243, 10, 17)),
    464 		( 999999, ( 4707, 11, 28)),
    465 		(1000000, ( 4707, 11, 29)),
    466 		(9999999, (29349,  1, 25)),
    467 	];
    468 	for (let (paramt, expect) .. cases) {
    469 		const actual = calc_ymd(paramt);
    470 		assert(expect.0 == actual.0, "year mismatch");
    471 		assert(expect.1 == actual.1, "month mismatch");
    472 		assert(expect.2 == actual.2, "day mismatch");
    473 	};
    474 };
    475 
    476 @test fn calc_yearday() void = {
    477 	const cases = [
    478 		(( -768,  2,  5),  36),
    479 		((   -1, 12, 31), 365),
    480 		((    0,  1,  1),   1),
    481 		((    0,  1,  2),   2),
    482 		((    0, 12, 31), 366),
    483 		((    1,  1,  1),   1),
    484 		((    1,  1,  2),   2),
    485 		(( 1965,  3, 23),  82),
    486 		(( 1969, 12, 31), 365),
    487 		(( 1970,  1,  1),   1),
    488 		(( 1970,  1,  2),   2),
    489 		(( 1999, 12, 31), 365),
    490 		(( 2000,  1,  1),   1),
    491 		(( 2000,  1,  2),   2),
    492 		(( 2020,  2, 12),  43),
    493 		(( 2038,  1, 18),  18),
    494 		(( 2038,  1, 19),  19),
    495 		(( 2038,  1, 20),  20),
    496 		(( 2243, 10, 17), 290),
    497 		(( 4707, 11, 28), 332),
    498 		(( 4707, 11, 29), 333),
    499 		((29349,  1, 25),  25),
    500 	];
    501 	for (let (params, expect) .. cases) {
    502 		const actual = calc_yearday(params.0, params.1, params.2);
    503 		assert(expect == actual, "yearday miscalculation");
    504 	};
    505 };
    506 
    507 @test fn calc_week() void = {
    508 	const cases = [
    509 		((  1, 0),  1),
    510 		((  1, 1),  1),
    511 		((  1, 2),  1),
    512 		((  1, 3),  1),
    513 		((  1, 4),  0),
    514 		((  1, 5),  0),
    515 		((  1, 6),  0),
    516 		(( 21, 1),  4),
    517 		(( 61, 6),  9),
    518 		((193, 5), 28),
    519 		((229, 6), 33),
    520 		((286, 0), 42),
    521 		((341, 6), 49),
    522 		((365, 2), 53),
    523 		((366, 3), 53),
    524 	];
    525 
    526 	for (let (params, expect) .. cases) {
    527 		const actual = calc_week(params.0, params.1);
    528 		assert(expect == actual, "week miscalculation");
    529 	};
    530 };
    531 
    532 @test fn calc_sundayweek() void = {
    533 	const cases = [
    534 		((  1, 0),  0),
    535 		((  1, 1),  0),
    536 		((  1, 2),  0),
    537 		((  1, 3),  0),
    538 		((  1, 4),  0),
    539 		((  1, 5),  0),
    540 		((  1, 6),  1),
    541 		(( 21, 1),  3),
    542 		(( 61, 2),  9),
    543 		((193, 4), 27),
    544 		((229, 0), 33),
    545 		((286, 3), 41),
    546 		((341, 6), 49),
    547 		((365, 5), 52),
    548 		((366, 0), 53),
    549 	];
    550 
    551 	for (let (params, expect) .. cases) {
    552 		const actual = calc_sundayweek(params.0, params.1);
    553 		assert(expect == actual, "week miscalculation");
    554 	};
    555 };
    556 
    557 @test fn calc_weekday() void = {
    558 	const cases = [
    559 		(-999999, 3), // -0768-02-05
    560 		(-719529, 4), // -0001-12-31
    561 		(-719528, 5), //  0000-01-01
    562 		(-719527, 6), //  0000-01-02
    563 		(-719163, 6), //  0000-12-31
    564 		(-719162, 0), //  0001-01-01
    565 		(-719161, 1), //  0001-01-02
    566 		(  -1745, 1), //  1965-03-23
    567 		(     -1, 2), //  1969-12-31
    568 		(      0, 3), //  1970-01-01
    569 		(      1, 4), //  1970-01-02
    570 		(  10956, 4), //  1999-12-31
    571 		(  10957, 5), //  2000-01-01
    572 		(  10958, 6), //  2000-01-02
    573 		(  24854, 0), //  2038-01-18
    574 		(  24855, 1), //  2038-01-19
    575 		(  24856, 2), //  2038-01-20
    576 		( 100000, 1), //  2243-10-17
    577 		( 999999, 3), //  4707-11-28
    578 		(1000000, 4), //  4707-11-29
    579 		(9999999, 5), // 29349-01-25
    580 	];
    581 	for (let (paramt, expect) .. cases) {
    582 		const actual = calc_weekday(paramt);
    583 		assert(expect == actual, "weekday miscalculation");
    584 	};
    585 };
    586 
    587 @test fn calc_janfirstweekday() void = {
    588 	const cases = [
    589 	//	 year   weekday
    590 		(1969,  2),
    591 		(1970,  3),
    592 		(1971,  4),
    593 		(1972,  5),
    594 		(1973,  0),
    595 		(1974,  1),
    596 		(1975,  2),
    597 		(1976,  3),
    598 		(1977,  5),
    599 		(1978,  6),
    600 		(1979,  0),
    601 		(1980,  1),
    602 		(1981,  3),
    603 		(1982,  4),
    604 		(1983,  5),
    605 		(1984,  6),
    606 		(1985,  1),
    607 		(1986,  2),
    608 		(1987,  3),
    609 		(1988,  4),
    610 		(1989,  6),
    611 		(1990,  0),
    612 		(1991,  1),
    613 		(1992,  2),
    614 		(1993,  4),
    615 		(1994,  5),
    616 		(1995,  6),
    617 		(1996,  0),
    618 		(1997,  2),
    619 		(1998,  3),
    620 		(1999,  4),
    621 		(2000,  5),
    622 		(2001,  0),
    623 		(2002,  1),
    624 		(2003,  2),
    625 		(2004,  3),
    626 		(2005,  5),
    627 		(2006,  6),
    628 		(2007,  0),
    629 		(2008,  1),
    630 		(2009,  3),
    631 		(2010,  4),
    632 		(2011,  5),
    633 		(2012,  6),
    634 		(2013,  1),
    635 		(2014,  2),
    636 		(2015,  3),
    637 		(2016,  4),
    638 		(2017,  6),
    639 		(2018,  0),
    640 		(2019,  1),
    641 		(2020,  2),
    642 		(2021,  4),
    643 		(2022,  5),
    644 		(2023,  6),
    645 		(2024,  0),
    646 		(2025,  2),
    647 		(2026,  3),
    648 		(2027,  4),
    649 		(2028,  5),
    650 		(2029,  0),
    651 		(2030,  1),
    652 		(2031,  2),
    653 		(2032,  3),
    654 		(2033,  5),
    655 		(2034,  6),
    656 		(2035,  0),
    657 		(2036,  1),
    658 		(2037,  3),
    659 		(2038,  4),
    660 		(2039,  5),
    661 	];
    662 	for (let (paramt, expect) .. cases) {
    663 		const actual = calc_janfirstweekday(paramt);
    664 		assert(expect == actual, "calc_janfirstweekday() miscalculation");
    665 	};
    666 };