Sunday, January 14, 2018
A JavaScript 1.2 Calendar and Clock, Part 5
Blog Entry #387
We'll finish off the js-caltop1.html document's scripts[2] script in today's post. I've done some homework on that mysterious code in the compute( ) function and I've got it all pretty well sorted out at this point - here we go...
More on the line form
A proper discussion of the compute( ) function obliges us to flesh out the line form that code-wise surrounds it.
As detailed earlier in this series, the line form contains five hidden controls and the push button.
The hidden part of the form is written JavaScriptically via a document.write( ) command that appears
(a) after a
t = new Date( );
constructor, whose t Date provides values for the form's month and year fields, and(b) before the compute( ) function
in the scripts[2] script.
The button and the line form's closing
</form>
tag, and the cells[2] table cell that contains them, are written as normal HTML that appears outside and after the scripts[2] script.We can code the line form more cleanly if we put all of it in the cells[2] cell and separate its HTML and JavaScript as follows:
<td id="rightTd">
<form name="line">
<input name="res" type="hidden">
<input name="day" type="hidden" value="1">
<input name="month" type="hidden">
<input name="year" type="hidden">
<input name="result" type="hidden">
<input name="butt" type="button" value=" Show Calendar " onclick="compute(this.form); window.open('js-calbot2.html', target='frame2');">
</form>
<script type="text/javascript">
var t = new Date( );
document.line.month.value = t.getMonth( ) + 1;
document.line.year.value = "19" + t.getYear( );
</script>
</td>
In the compute( ) function, we will use the values of the day, month, and year fields to find the starting day of the week for a given month; this determination will enable us to build a calendar for the month from scratch in the frame2 frame.
compute( ) it
References
(1) The compute( ) code is based on an algorithm developed by Kim Larsen in a 1995 "Computing the Day of the Week" article; the compute( ) code differs slightly from Larsen's algorithm in that the former is pegged to a 0 = Saturday to 6 = Friday week whereas the latter is pegged to a 0 = Monday to 6 = Sunday week.
N.B. A significant part of the third paragraph of Larsen's article ("More formally, ...") is not rendered due to an incorrectly coded
<img>
element (that points to an image of a ≤ character, which could have been coded with a ≤ character reference) - you'll have to go to the page source to get the full text.(2) Ask Dr. Math's "The Calendar and the Days of the Week" article is a good starting point if you are new to day-of-the-week algorithms (as I was); it notably links to
(a) a "Formula for the First Day of a Year" article that compares Larsen's algorithm with one version of* a more well-known day-of-the-week algorithm called Zeller's congruence (*note that Ask Dr. Math's Zeller formula is different from Wikipedia's Gregorian Zeller formula) and
(b) a "Deriving Zeller's Rule" article that is helpful in understanding the month part(s) of the Larsen and Zeller algorithms.
Deconstruction
The Larsen and Zeller algorithms calculate a day-of-the-week index for the day of the week for a specific date. In our case, the compute( ) function will determine the day of the week that the first day of the current month falls on - "Monday" for January 2018, e.g. - if we haven't touched the month and year controls in the cells[1] table cell; if desired, however, compute( ) and its inputs can be modified so as to determine the day of the week for any Gregorian date.
Upon clicking the button and calling the compute( ) function, a this.form reference to the line form is passed to compute( ) and given a form identifier. As noted at the end of the previous post, the compute( ) body begins by changing the Show Calendar button label to Update Calendar.
function compute(form) {
document.line.butt.value = "Update Calendar";
After that, compute( ) gets down to business. A subsequent set of operations extracts date, month, and year inputs for the compute( ) day-of-the-week code from the line form; for the arithmetic to come, these inputs are stored in val1, val2x, and val3 variables, respectively.
var val1 = parseInt(form.day.value, 10);
if ((val1 < 0) || (val1 > 31)) { window.alert("Day is out of range."); }
var val2 = parseInt(form.month.value, 10);
if ((val2 < 0) || (val2 > 12)) { window.alert("Month is out of range."); }
var val2x = parseInt(form.month.value, 10);
var val3 = parseInt(form.year.value, 10);
if (val3 < 1900) { window.alert("You're that old!"); }
• The day, month, and year values have a string data type; we'll carry out some addition with the numbers they hold in a little bit so it is necessary to numberify them via either the parseInt( ) function or the Number( ) function.
• The day value is fixed at 1 - at no point in the code is it reset - and therefore the
if ((val1 < 0) || (val1 > 31))
test can be thrown out.• The
if ((val2 < 0) || (val2 > 12))
test can also be thrown out: the month value can either stay at t.getMonth( ) + 1
or be reset by the isnlist selection list but at no point does it go outside the 1-12 range.• It's actually not necessary to use two variables for the month input: the values of val2 and val2x will diverge for January and February (while remaining the same for all other months) but that divergence can be recoded with one variable.
• You can push the val3 value below 1900 if you click the button enough times but the You're that old! alert( ) response upon doing that seems a bit silly, doesn't it?
The compute( ) day-of-the-week code works with a year of months that run from the March of a given year to the February of the next year and that are numbered 3 to 14, respectively. The January and February of the given year are renumbered and shifted to the previous year by the following conditionals:
if (val2 == 1) {
val2x = 13;
val3 = val3 - 1; }
if (val2 == 2) {
val2x = 14;
val3 = val3 - 1; }
The compute( ) day-of-the-week code works with a week of days that run from Saturday to Friday and that are numbered 0 to 6, respectively. The val1 1 date is mapped onto its corresponding day number via:
var val4 = parseInt(((val2x + 1) * 3) / 5, 10);
var val5 = parseInt(val3 / 4, 10);
var val6 = parseInt(val3 / 100, 10);
var val7 = parseInt(val3 / 400, 10);
var val8 = val1 + (val2x * 2) + val4 + val3 + val5 - val6 + val7 + 2;
var val9 = parseInt(val8 / 7, 10);
var week = val8 - (val9 * 7);
The terms that go into the
var val8 = val1 + (val2x * 2) + val4 + val3 + val5 - val6 + val7 + 2;
line are not meaningful in an absolute sense; rather, they are part of a formula that tracks changes in the day of the week in response to various time markers.•
(val2x * 2) + ((val2x + 1) * 3) / 5
moves the day of the week forward by three days or two days as we go from month to month in the March to February year.• In going from year to year,
val3 + val3 / 4 - val3 / 100 + val3 / 400
moves the day of the week forward by one day if val3 ends with a 28 February (365 % 7 = 1) or two days if val3 ends with a 29 February.• The
+ 2
at the end syncs us with a 0 = Saturday to 6 = Friday week; Larsen's formula doesn't have it; a + 1
would sync us with the getDay( ) method's 0 = Sunday to 6 = Saturday week.• Math.floor( ) can be used in place of parseInt( ).
• The last two commands -
var val9 = parseInt(val8 / 7, 10); var week = val8 - (val9 * 7);
- are equivalent to var week = val8 % 7;
.The val1 date's day number, week, is stored in the line form's res field and then mapped onto a corresponding day string via a switch-like set of if statements; the resulting week day string is finally stored in the line form's result field.
form.res.value = week;
if (week == 1) { week = "Sunday"; }
else if (week == 2) { week = "Monday"; }
else if (week == 3) { week = "Tuesday"; }
else if (week == 4) { week = "Wednesday"; }
else if (week == 5) { week = "Thursday"; }
else if (week == 6) { week = "Friday"; }
else if (week == 0) { week = "Saturday"; }
form.result.value = week; } /* That's it for compute( ). */
A better getDayString( )
We earlier created
(1) a separate t Date just for the line form and
(2) a
var dayArray = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
Array for the date string part of the code;both t and dayArray have a global scope. With these features in hand, we can get the week day string with:
t.setDate(1);
var week = dayArray[t.getDay( )];
That's it - a lot simpler, huh? This is something the author could have done: the setDate( ) method of the Date object was implemented in JavaScript 1.0 whereas the core Array object was implemented in JavaScript 1.1.
We are at long last ready to load the js-calbot2.html document into the frame2 frame and take on its calendar code, and we'll do just that in our next episode.
Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)