reptile7's JavaScript blog
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 &le; 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.

Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)