Multiple datepicker

S

Simon Templar

Hi,

I am trying to introduce a multiple datepicker into a webpage and the
problem arises with the second picker. I tried to copy and adapt the code I
employed for the first one, renaming the variables but it is simply unable
to output any date to the database. The code for the first picker (which
works fine) is as follows:



<html>
<head>
<script type="text/javascript">

var numDays = {
'1': 31, '2': 28, '3': 31, '4': 30, '5': 31, '6': 30,
'7': 31, '8': 31, '9': 30, '10': 31, '11': 30, '12': 31
};

function setDays(oMonthSel, oDaysSel, oYearSel)
{
var nDays, oDaysSelLgth, opt, i = 1;
nDays = numDays[oMonthSel[oMonthSel.selectedIndex].value];
if (nDays == 28 && oYearSel[oYearSel.selectedIndex].value % 4 == 0)
++nDays;
oDaysSelLgth = oDaysSel.length;
if (nDays != oDaysSelLgth)
{
if (nDays < oDaysSelLgth)
oDaysSel.length = nDays;
else for (i; i < nDays - oDaysSelLgth + 1; i++)
{
opt = new Option(oDaysSelLgth + i, oDaysSelLgth + i);
oDaysSel.options[oDaysSel.length] = opt;
}
}
var oForm = oMonthSel.form;
var month = oMonthSel.options[oMonthSel.selectedIndex].value;
var day = oDaysSel.options[oDaysSel.selectedIndex].value;
var year = oYearSel.options[oYearSel.selectedIndex].value;
oForm.f_despara.value = month + '/' + day + '/' + year;
}
</head>
<body>
<form>
<table>
<tr>
<td>
<select name="day" class="buscar" id="day"
onChange="setDays(month,this,year)">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="26">26</option>
<option value="27">27</option>
<option value="28">28</option>
<option value="29">29</option>
<option value="30">30</option>
<option value="31">31</option>
<option selected>D&iacute;a</option>
</select></td>
<td><select name="month" class="buscar" id="month"
onchange="setDays(this,day,year)">
<option value="1">Enero</option>
<option value="2">Febrero</option>
<option value="3">Marzo</option>
<option value="4">Abril</option>
<option value="5">Mayo</option>
<option value="6">Junio</option>
<option value="7">Julio</option>
<option value="8">Agosto</option>
<option value="9">Septiembre</option>
<option value="10">Octubre</option>
<option value="11">Noviembre</option>
<option value="12">Diciembre</option>
<option selected>Mes</option>
</select></td>
<td><select name="year" class="buscar" id="year"
onchange="setDays(month,day,this)">
<option value="1995">1995</option>
<option value="1996">1996</option>
<option value="1997">1997</option>
<option value="1998">1998</option>
<option value="1999">1999</option>
<option value="2000">2000</option>
<option value="2001">2001</option>
<option value="2002">2002</option>
<option value="2003">2003</option>
<option value="2004">2004</option>
<option selected>A&ntilde;o</option>
</select>
<input type="hidden" name="f_despara" value="" />
</td>
</tr>
</table>
</form>
</body>
</html>




Then I tried to add a second date picker and never worked. I tried this way:
I added another function to the script:



function setDays2(oMonth2Sel, oDays2Sel, oYear2Sel)
{
var nDays2, oDays2SelLgth, opt2, i2 = 1;
nDays2 = numDays[oMonth2Sel[oMonth2Sel.selectedIndex].value];
if (nDays2 == 28 && oYear2Sel[oYear2Sel.selectedIndex].value % 4 == 0)
++nDays2;
oDays2SelLgth = oDays2Sel.length;
if (nDays2 != oDays2SelLgth)
{
if (nDays2 < oDays2SelLgth)
oDays2Sel.length = nDays2;
else for (i2; i2 < nDays2 - oDays2SelLgth + 1; i2++)
{
opt = new Option(oDays2SelLgth + i2, oDays2SelLgth + i2);
oDays2Sel.options[oDays2Sel.length] = opt2;
}
}
var oForm2 = oMonth2Sel.form;
var month2 = oMonth2Sel.options[oMonth2Sel.selectedIndex].value;
var day2 = oDays2Sel.options[oDays2Sel.selectedIndex].value;
var year2 = oYear2Sel.options[oYear2Sel.selectedIndex].value;
oForm.f_vac1.value = month2 + '/' + day2 + '/' + year2;
}



and then added the date picker fields to the form:



<tr>
<td><select name="day2" class="buscar" id="day2"
onChange="setDays2(month2,this,year2)">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="26">26</option>
<option value="27">27</option>
<option value="28">28</option>
<option value="29">29</option>
<option value="30">30</option>
<option value="31">31</option>
<option selected>D&iacute;a</option>
</select></td>
<td><select name="month2" class="buscar" id="month2"
onchange="setDays2(this,day2,year2)">
<option value="1">Enero</option>
<option value="2">Febrero</option>
<option value="3">Marzo</option>
<option value="4">Abril</option>
<option value="5">Mayo</option>
<option value="6">Junio</option>
<option value="7">Julio</option>
<option value="8">Agosto</option>
<option value="9">Septiembre</option>
<option value="10">Octubre</option>
<option value="11">Noviembre</option>
<option value="12">Diciembre</option>
<option selected>Mes</option>
</select></td>
<td><select name="year2" class="buscar" id="year2"
onchange="setDays2(month2,day2,this)">
<option value="1995">1995</option>
<option value="1996">1996</option>
<option value="1997">1997</option>
<option value="1998">1998</option>
<option value="1999">1999</option>
<option value="2000">2000</option>
<option value="2001">2001</option>
<option value="2002">2002</option>
<option value="2003">2003</option>
<option value="2004">2004</option>
<option selected>A&ntilde;o</option>
</select>
<input type="hidden" name="f_vac1" value="" />
</td>
</tr>



The script works well avoiding invalid date on the form but never passes the
value to the database.

Could someone help me?

Thanks in advance,

Gustavo
 
T

Thomas 'PointedEars' Lahn

Simon said:

This is not Valid HTML. said:
<head>
<script type="text/javascript">

var numDays = {
'1': 31, '2': 28,

In leap years, February has 29 days.

'3': 31, '4': 30, '5': 31, '6': 30,
'7': 31, '8': 31, '9': 30, '10': 31, '11': 30, '12': 31
};

The number of days a month has can be determined reliably (including
leap years) using Date objects:

/**
* Returns the number of days of a month.
*
* @author
* (C) 2004 Thomas Lahn <[email protected]>
* @partof
* <http://pointedears.de/scripts/date.js>
* @argument Date d
* Date object to hold the month.
* @return type number
* The number of the days of the month,
* -1 if no Date object has been passed.
*/
function getDaysOfMonth(d)
{
if (d && d.constructor == Date)
{
// make a "copy" of the passed Date object so that it does not change
var y;
if (d.getFullYear)
{
y = d.getFullYear();
}
else
{
y = d.getYear();
if (y < 1900)
{
y += 1900;
}
}

var d2 = new Date(y, d.getMonth() + 1, d.getDate());

// the day before the first day of the next month
// is the last day of the current month
d2.setDate(0);

return d2.getDate();
}

return -1;
}


For the rest of your source code, I suggest you put a testcase in a Valid
HTML document (if it contains server-side scripting, put the source code
of that in an unparsed HTML document [.phps and the like] or a text file)
instead of dumping more than 200 uncommented (and some even unnecessary)
lines of it to a public newsgroup.


PointedEars
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Mon, 26 Jul
2004 06:28:55, seen in Thomas 'PointedEars'
Lahn said:
Simon Templar wrote:

In leap years, February has 29 days.

If you had troubled to read the Sainted one's code before responding,
you would have seen that he is aware of that; his code fully complies
with the Julian Calendar, AFAICS.

'3': 31, '4': 30, '5': 31, '6': 30,

The number of days a month has can be determined reliably (including
leap years) using Date objects:

/**
* Returns the number of days of a month.

Only for fully-Gregorian months, of course.

*
* @author
* (C) 2004 Thomas Lahn <[email protected]>
YCKI.


y = d.getYear();
if (y < 1900)
{
y += 1900;
}
}

Incorrect in some systems. I have recently posted, and no-one has
refuted, a brief and reliable means of getting the full year from any
plausible getYear().

d2.setDate(0);

Possible failure in Mac NS 4.




Recent discussion in this group should have indicated to you a
considerably quicker, and shorter, method.


Simon : see FAQ; it leads indirectly to
<URL:http://www.merlyn.demon.co.uk/js-date4.htm#MaYl> which has

function TailOfMonth(y, m) {
if (m==1) return 28 + (y%4 == 0) // 1901-2099
return [31,0,31,30,31,30, 31,31,30,31,30,31][m] }

which is rather like what you already have. On my system, it is about 5
times faster than the best I know using a Date Object, and that is
nearly twice as fast as one like what TL gave.

Modification for all Gregorian years should not slow it much.

BOTF.
 
L

Lasse Reichstein Nielsen

Thomas 'PointedEars' Lahn said:
function getDaysOfMonth(d)

Might as well pass the year and month number alone, no need for a full
date.
if (d.getFullYear)

While getFullYear is convenient, you are no better off if it turns
out to be missing. Just do:
---
function getDaysOfMonth(year, month) { // month==1 is January
if (year >= 0 && year < 100) {
year += 100;
month -= 1200;
}
return new Date(year, month, 0).getDate();
}
---
The correction for year/month seems to be necessary, otherwise the
function will fail for the year 0, as it (and everything else between
0 and 99) will have 1900 added to the year. However, the year 0 was a
leap year (according to the Javascript the Date object), while 1900
wasn't.

/L
 
M

Mick White

Dr said:
JRS: In article <[email protected]>, dated Mon, 26 Jul
2004 06:28:55, seen in Thomas 'PointedEars'
Lahn said:
Simon Templar wrote:


In leap years, February has 29 days.


If you had troubled to read the Sainted one's code before responding,
you would have seen that he is aware of that; his code fully complies
with the Julian Calendar, AFAICS.


'3': 31, '4': 30, '5': 31, '6': 30,


The number of days a month has can be determined reliably (including
leap years) using Date objects:

/**
* Returns the number of days of a month.


Only for fully-Gregorian months, of course.


*
* @author
* (C) 2004 Thomas Lahn <[email protected]>

YCKI.



y = d.getYear();
if (y < 1900)
{
y += 1900;
}
}


Incorrect in some systems. I have recently posted, and no-one has
refuted, a brief and reliable means of getting the full year from any
plausible getYear().


d2.setDate(0);


Possible failure in Mac NS 4.




Recent discussion in this group should have indicated to you a
considerably quicker, and shorter, method.


Simon : see FAQ; it leads indirectly to
<URL:http://www.merlyn.demon.co.uk/js-date4.htm#MaYl> which has

function TailOfMonth(y, m) {
if (m==1) return 28 + (y%4 == 0) // 1901-2099
return [31,0,31,30,31,30, 31,31,30,31,30,31][m] }


function TailOfMonth(y, m) {
return [31,y%4 == 0?29:28,31,30,31,30, 31,31,30,31,30,31][m] }

Mick
 
L

Lasse Reichstein Nielsen

Dr John Stockton said:
function TailOfMonth(y, m) {
if (m==1) return 28 + (y%4 == 0) // 1901-2099
return [31,0,31,30,31,30, 31,31,30,31,30,31][m] }

which is rather like what you already have. On my system, it is about 5
times faster than the best I know using a Date Object, and that is
nearly twice as fast as one like what TL gave.

In my versions of IE, Opera, that versions is about as
fast as the Date based version I suggested (not entirely sure for
Mozilla, because it asked me if I wanted to stop the script because it
had run too long .. but it seems the Date object is *very* slow).

If speed is of the essence, not creating an array for each call is a
great saving in both Mozilla and Opera (and is marginally faster in IE
as well). This version:

var months = [31,,31,30,31,30,31,31,30,31,30,31];
function lengthOfMonth(y,m) { // m==0 is January
return arr[m] || ((!(y%4) && (!!(y%25) || !(y%400)))+28);
}

is very fast in both Opera and Mozilla (in a loop, it takes almost no
time over a no-op function), and it isn't slower in IE.

/L
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Mon, 26 Jul 2004
19:02:04, seen in Lasse Reichstein Nielsen
Might as well pass the year and month number alone, no need for a full
date.


While getFullYear is convenient, you are no better off if it turns
out to be missing. Just do:
---
function getDaysOfMonth(year, month) { // month==1 is January
if (year >= 0 && year < 100) {
year += 100;
month -= 1200;
}
return new Date(year, month, 0).getDate();
}
---
The correction for year/month seems to be necessary, otherwise the
function will fail for the year 0, as it (and everything else between
0 and 99) will have 1900 added to the year. However, the year 0 was a
leap year (according to the Javascript the Date object), while 1900
wasn't.

The correction is over-complex, since for this purpose it is only
necessary to add 400 years (and only for February; and only for Year 0,
I think). The year only needs to be correct modulo 400.

Or add a multiple thereof; so, since there is probably little interest
in month-lengths before BC 10000 or after AD 250000, the best is just to
add 10000 years unconditionally - assuming that new Date is sensibly
written.

Year 0 (BC 1) was a leap year on the Gregorian & Julian calendars; but
not in actuality, since the Romans had been bad at counting up to 4.


For y >= 0, full Gregorian,

function LastOfMonth(y, m) { // m = 0..11 // new, undertested.
if (m==1) return 29 - ( y & 3 || y & 15 && !(y % 25) ) // ~ CGjrs
return [31,0,31,30,31,30, 31,31,30,31,30,31][m] }
 
L

Lasse Reichstein Nielsen

Dr John Stockton said:
The correction is over-complex,
Absolutely.

since for this purpose it is only necessary to add 400 years (and
only for February; and only for Year 0, I think). The year only
needs to be correct modulo 400.

Indeed. Only the year 0 fails, because only years between 0 and 99
are affected by the year-rewriting, and of those, only 0 differst
from the year + 1900.

The reason (well, excuse) for doing it this way was to not need to
know how the month lengths are calculated. That is left entirely
up to the Date object. The only rewriting was to avoid the problem
with years 0-99 as arguments to the constructor.
For y >= 0, full Gregorian,

function LastOfMonth(y, m) { // m = 0..11 // new, undertested.
if (m==1) return 29 - ( y & 3 || y & 15 && !(y % 25) ) // ~ CGjrs
return [31,0,31,30,31,30, 31,31,30,31,30,31][m] }

Except
LastOfMonth(2002,1) == 27
and
LastOfMonth(2003,1) == 26
since (y&3) becomes 2 and 3 in those years. If you change it to:

function LastOfMonth(y, m) { // m = 0..11 // new, undertested.
if (m==1) return 29 - ( !!(y & 3) || y & 15 && !(y % 25) ) // ~ CGjrs
return [31,0,31,30,31,30, 31,31,30,31,30,31][m] }

then it seems to work (the bit-fiddling is impressive :).

For efficiency, you only need to do the extra calculation for m == 1.

/L
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Tue, 27 Jul 2004
01:01:25, seen in Lasse Reichstein Nielsen
Dr John Stockton said:
The correction is over-complex,
Absolutely.

since for this purpose it is only necessary to add 400 years (and
only for February; and only for Year 0, I think). The year only
needs to be correct modulo 400.

Indeed. Only the year 0 fails, because only years between 0 and 99
are affected by the year-rewriting, and of those, only 0 differst
from the year + 1900.

The reason (well, excuse) for doing it this way was to not need to
know how the month lengths are calculated. That is left entirely
up to the Date object. The only rewriting was to avoid the problem
with years 0-99 as arguments to the constructor.
For y >= 0, full Gregorian,

function LastOfMonth(y, m) { // m = 0..11 // new, undertested.
if (m==1) return 29 - ( y & 3 || y & 15 && !(y % 25) ) // ~ CGjrs
return [31,0,31,30,31,30, 31,31,30,31,30,31][m] }

Except
LastOfMonth(2002,1) == 27
and
LastOfMonth(2003,1) == 26
since (y&3) becomes 2 and 3 in those years. If you change it to:

function LastOfMonth(y, m) { // m = 0..11 // new, undertested.
if (m==1) return 29 - ( !!(y & 3) || y & 15 && !(y % 25) ) // ~ CGjrs
return [31,0,31,30,31,30, 31,31,30,31,30,31][m] }

then it seems to work (the bit-fiddling is impressive :).


Yes, the tests that were exhaustive enough for the older methods proved
inadequate. I now test every month in 400 years, for safety.

29 - !! ( y & 3 || y & 15 && !(y % 25) )

looks nicer to me, and tests OK in js-dates4.htm. Thanks.


JRS: In article <[email protected]>, dated Mon, 26 Jul 2004
21:56:53, seen in Lasse Reichstein Nielsen
If speed is of the essence, not creating an array for each call is a
great saving in both Mozilla and Opera (and is marginally faster in IE
as well).

That will take a while to put neatly into page 4; I have several very
similar arrays, and only the unnecessary differences can be ignored in
merging them.
 
R

rh

Dr John Stockton said:
JRS: In article <[email protected]>, dated Mon, 26 Jul 2004
21:56:53, seen in Lasse Reichstein Nielsen

You may also wish to consider something along the lines of:

function LastOfMonth(y, m) { // m = 0..11
return 29 + (m*31/12 | 0) - ((m-1)*31/12 | 0) + !m
- ( !! (m-1) || 2
+ ( !!((y & 3) || y & 15 && !(y % 25) )) ) ; // ~ CGjrs }

which cadges from Zeller (at the very least, makes use of a function I
came across a couple of years ago in an implementation of Zeller's
congruence). It's considerably more efficient than creating the lookup
array within the js function, but will be slower (although perhaps
still not all that much slower) than your table lookup with the array
created outside.

../rh
 
D

Dr John Stockton

JRS: In article <[email protected]>,
dated Tue, 27 Jul 2004 19:42:56, seen in rh
You may also wish to consider something along the lines of:

function LastOfMonth(y, m) { // m = 0..11
return 29 + (m*31/12 | 0) - ((m-1)*31/12 | 0) + !m
- ( !! (m-1) || 2
+ ( !!((y & 3) || y & 15 && !(y % 25) )) ) ; // ~ CGjrs }

which cadges from Zeller (at the very least, makes use of a function I
came across a couple of years ago in an implementation of Zeller's
congruence). It's considerably more efficient than creating the lookup
array within the js function, but will be slower (although perhaps
still not all that much slower) than your table lookup with the array
created outside.

Moving the final } to before the comment mark helps. Then, assuming the
tests in js-date4.htm are now adequate, it is correct over a full cycle
of 400 years Gregorian and in IE4 is 2-3 times faster than the best of
the others there.

It does not, at first sight, though, look much like Zeller's Congruence.
though it has in common the idea of using arithmetic instead of table
lookup. Have you seen my Zeller pages, via <URL:http://www.merlyn.demon
..co.uk/zeller-c.htm> etc. ?


function MonthLength(y, m) { // m = 0..11
return new Date(y, m+1, 1, -9).getDate() }

is the best I've found for a function which can immediately be seen to
be correct, though; ten times slower.
 
R

rh

Dr said:
Moving the final } to before the comment mark helps.

Thats well and good. It seems that a newline got dropped in the
posting.
Then, assuming the
tests in js-date4.htm are now adequate, it is correct over a full cycle
of 400 years Gregorian and in IE4 is 2-3 times faster than the best of
the others there.

It does not, at first sight, though, look much like Zeller's Congruence.
though it has in common the idea of using arithmetic instead of table
lookup. Have you seen my Zeller pages, via <URL:http://www.merlyn.demon
.co.uk/zeller-c.htm> etc. ?

I have, and noted there was no direct comparable, which was in part
the reason for some minor, but perhaps not enough, caution in the
wording of the post. The first difference calculation I used is
equivalent to that found at:

<URL: http://users.aol.com/s6sj7gt/mikecal.htm>

which also makes reference to similarity "in spirit" with Zeller's
congruence.

While on the topic of js date calculations, a relatively
straightforward conversion of the C code to JavaScript found there
gives:

function DayOfWeek(d,m,y) { // d = 1..31; m = 1..12
return (d+=m<3? y--:y-2, d+4+ Math.floor(23*m/9)+ Math.floor(y/4)
- Math.floor(y/100)+Math.floor(y/400))%7;
}

which (under very limited testing) appears to give the expected result
in IE.

Aside from the commendable mathematical finesse and compactness of the
code, it provides an interesting look at use of the comma operator
(jslint notwithstanding, of course) -- a construct that seems to be
extremely rare (non-existent? :)) in demonstration js code posts.

../rh
 
R

Richard Cornford

... , assuming the tests in js-date4.htm are now adequate,
it is correct over a full cycle of 400 years Gregorian and
in IE4 is 2-3 times faster than the best of the others there.
function MonthLength(y, m) { // m = 0..11
return new Date(y, m+1, 1, -9).getDate() }

is the best I've found for a function which can immediately be seen to
be correct, though; ten times slower.

Looking at the - LastOfMonth - function I observer that the
calculation - 31/12 - is done twice. The result must be the same each
time, and could be pre-calculated. Given that the result is truncated by
the OR zero operation and the range of - m - is limited, an
approximation of - 31/12 - should be sufficient, and - 2.58 - (or its
internal IEEE double representation) seems to be close enough to give
identical results for m == [0 to 11].

Next, - (m-1) - is calculated twice. If - +!m - were moved forward the
first occurrence of - (m-1) - could be replaced with - (--m) - and the
second replaced with just - m -, producing:-

function LastOfMonth2(y, m) { // m = 0..11
return 29 + (m*2.58 | 0) + !m - ((--m)*2.58 | 0)
- ( !!(m) || 2
+ ( !!((y & 3) || y & 15 && !(y % 25) )) ) ;
}

However, for February in the year zero - MonthLength - returns 28 days,
while - LastOfMonth - returns 29. The expression - (!!(m-1)|| ... ) -
only moves into the OR clause in February, so a final additional
adjustment by - !y - (only 1 in the year zero, zero otherwise) would
correct the discrepancy at a minimal cost in processing time, producing
(in my version):-

function LastOfMonth3(y, m) { // m = 0..11
return 29 + (m*2.58 | 0) + !m - ((--m)*2.58 | 0)
- ( !!(m) || 2 +
(!!(
(y & 3)||
(
(y & 15 && !(y % 25))||
!y
/* or maybe:-
((y & 15)||!y)&&
!(y % 25)
or:-
((y & 15)?!(y % 25):!y)
*/
)
))
);
}

- and flipping some of the logic in the final expression produced:-

function LastOfMonth2(y, m) { // m = 0..11
return 29 + (m*2.58 | 0) + !m - ((--m)*2.58 | 0)
- ( !!(m) || 2
+ (!( !(y & 3)&&((y & 15)?(y % 25):y) )));
}

- losing some of the logical NOT operations along the way.

Those gives me identical results to - MonthLength - for all months in
years from -1000 to +3200 and is still very fractionally faster than
the original - LastOfMonth -.

Richard.
 
G

Grant Wagner

Richard said:
- and flipping some of the logic in the final expression produced:-

function LastOfMonth2(y, m) { // m = 0..11
return 29 + (m*2.58 | 0) + !m - ((--m)*2.58 | 0)
- ( !!(m) || 2
+ (!( !(y & 3)&&((y & 15)?(y % 25):y) )));
}

- losing some of the logical NOT operations along the way.

Those gives me identical results to - MonthLength - for all months in
years from -1000 to +3200 and is still very fractionally faster than
the original - LastOfMonth -.

Richard.

And any execution time you might have gained was time lost figuring all that
out. The users' time waiting for the number of days in a month is
effectively "free" (since it is so short on a per instance basis) and your
time is not. I get it though, producing the optimal algorithm for a given
problem can be fun, I do it myself sometimes. But in this case, I'd most
likely use some variation of the Date() object solution presented by Thomas.

Why would I use a clunky Date() object driven solution when I have a much
faster algorithm? Because a Date object solution is incredibly clear. If I
need to go back later and modify the code (although I can't imagine why I
would need to unless calendars spontaneously produce a 13th month), code
using a Date object is self-documenting. Even if I can't find the original
code, I could probably re-write it in about 2 minutes.

Again, don't get me wrong, I'm not criticizing at all. I'm just contributing
something to the discussion (I hope), but pointing out that sometimes the
"optimal" solution isn't necessarily the "best" solution (especially if
someone else is paying for the "optimal" solution).

In fact, given the parameter choices (year and month), here is my solution:

function LastOfMonth3(y, m) { // m = 0..11
var d = new Date(y, (m + 1), 1);
if (d && d.constructor == Date) {
d.setDate(0);
return d.getDate();
} else {
return -1;
}
}

Note that Netscape 4.78 does not properly handle constructing a date with a
day of 0, as a result, it is necessary to construct the date for the first
of the next month, then set it to zero.

My solution produces the same results as yours for all months 0..11 for all
years 1000..3199. Now for the fun part. For all months 0..11 for all years
1000..3199 (times in ms);

LastOfMonth -2- -3-
IE: 282 4172
NS4: 12719 30970
FF0.9: 391 25516 (two "slow script" warnings)
O7.53: 531 5094
M1.7.1: 500 25908 (two "slow script" warnings)

Okay, I take it all back. Given these performance figures I'd use Richard's
solution :D.
 
G

Grant Wagner

Richard said:
- and flipping some of the logic in the final expression
produced:-

function LastOfMonth2(y, m) { // m = 0..11
return 29 + (m*2.58 | 0) + !m - ((--m)*2.58 | 0)
- ( !!(m) || 2
+ (!( !(y & 3)&&((y & 15)?(y % 25):y) )));
}

- losing some of the logical NOT operations along the way.

Those gives me identical results to - MonthLength - for all
months in
years from -1000 to +3200 and is still very fractionally faster
than
the original - LastOfMonth -.

Richard.

And any execution time you might have gained was time lost
figuring all that out. The users' time waiting for the number of
days in a month is effectively "free" (since it is so short on a
per instance basis) and your time is not. I get it though,
producing the optimal algorithm for a given problem can be fun, I
do it myself sometimes. But in this case, I'd most likely use
some variation of the Date() object solution presented by Thomas.

Why would I use a clunky Date() object driven solution when I
have a much faster algorithm? Because a Date object solution is
incredibly clear. If I need to go back later and modify the code
(although I can't imagine why I would need to unless calendars
spontaneously produce a 13th month), code using a Date object is
self-documenting. Even if I can't find the original code, I could
probably re-write it in about 2 minutes.

Again, don't get me wrong, I'm not criticizing at all. I'm just
contributing something to the discussion (I hope), but pointing
out that sometimes the "optimal" solution isn't necessarily the
"best" solution (especially if someone else is paying for the
"optimal" solution).

In fact, given the parameter choices (year and month), here is my
solution:

function LastOfMonth3(y, m) { // m = 0..11
var d = new Date(y, (m + 1), 1);
if (d && d.constructor == Date) {
d.setDate(0);
return d.getDate();
} else {
return -1;
}
}

Note that Netscape 4.78 does not properly handle constructing a
date with a day of 0, as a result, it is necessary to construct
the date for the first of the next month, then set it to zero.

My solution produces the same results as yours for all months
0..11 for all years 1000..3199. Now for the fun part. For all
months 0..11 for all years 1000..3199 (times in ms);

LastOfMonth -2- -3-
IE: 282 4172
NS4: 12719 30970
FF0.9: 391 25516 (two "slow script" warnings)
O7.53: 531 5094
M1.7.1: 500 25908 (two "slow script" warnings)

Okay, I take it all back. Given these performance figures I'd use
Richard's solution :D.
 
R

Richard Cornford

Grant said:
Richard Cornford wrote:

And any execution time you might have gained was time lost
figuring all that out. The users' time waiting for the number
of days in a month is effectively "free" (since it is so short
on a per instance basis) and your time is not. I get it though,
producing the optimal algorithm for a given problem can be fun,
I do it myself sometimes.
<snip>

There is the (not everyone's idea of) fun aspect of optimum algorithms,
but I find that activity useful anyway because recognising an efficient
formulation in one context may make its applicability more obvious in
another (where it may be more significant).

In most respects the speed of date calculations in form validation is
not significant, but the Date object is not without its limitations.
There are, for example, implementations with a 32 bit milliseconds
representation so they are range limited to between some time in 1901
and 2039 (as I recall). Not a problem in most current (and especially
commercial) contexts but for something like a DHTML animation relating
star positions to date a combination of a large (and known) date range
and fast execution might be useful.

Again, don't get me wrong, I'm not criticizing at all. I'm just
contributing something to the discussion (I hope), but pointing
out that sometimes the "optimal" solution isn't necessarily the
"best" solution (especially if someone else is paying for the
"optimal" solution).
<snip>

I wouldn't argue with that.

Richard.
 
R

rh

Richard Cornford said:
Looking at the - LastOfMonth - function I observer that the
calculation - 31/12 - is done twice. The result must be the same each
time, and could be pre-calculated. Given that the result is truncated by
the OR zero operation and the range of - m - is limited, an
approximation of - 31/12 - should be sufficient, and - 2.58 - (or its
internal IEEE double representation) seems to be close enough to give
identical results for m == [0 to 11].

I think I was hoping there would be a "compile-time" constant
expression optimization, but I don't know whether such occurs in js
implementations. Probably not.
Next, - (m-1) - is calculated twice. If - +!m - were moved forward the
first occurrence of - (m-1) - could be replaced with - (--m) - and the
second replaced with just - m -, producing:-

function LastOfMonth2(y, m) { // m = 0..11
return 29 + (m*2.58 | 0) + !m - ((--m)*2.58 | 0)
- ( !!(m) || 2
+ ( !!((y & 3) || y & 15 && !(y % 25) )) ) ;
}

That's a good change.

- and flipping some of the logic in the final expression produced:-

function LastOfMonth2(y, m) { // m = 0..11
return 29 + (m*2.58 | 0) + !m - ((--m)*2.58 | 0)
- ( !!(m) || 2
+ (!( !(y & 3)&&((y & 15)?(y % 25):y) )));
}

- losing some of the logical NOT operations along the way.

Hey, tertiary conditionals aren't allowed!! :)
Those gives me identical results to - MonthLength - for all months in
years from -1000 to +3200 and is still very fractionally faster than
the original - LastOfMonth -.

Here's another version that executes as fast, or marginally faster,
than LastOfMonth2 in IE, Netscape, Mozilla and Opera, and gives the
same results over a similar (wider) range in my tests:


function LastOfMonth3(y, m) { // m = 0..11
return 29 + !m + (m*2.58 | 0) - (--m*2.58 | 0)
- ( !!m || 2 + !y
+ ( !!( (y & 3) || y & 15 && !(y % 25) )) ) ; // ~ CGjrs
}


../rh
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated
Wed, 28 Jul 2004 21:41:15, seen in rh
The first difference calculation I used is
equivalent to that found at:

<URL: http://users.aol.com/s6sj7gt/mikecal.htm>
Interesting.

which also makes reference to similarity "in spirit" with Zeller's
congruence.

While on the topic of js date calculations, a relatively
straightforward conversion of the C code to JavaScript found there
gives:

function DayOfWeek(d,m,y) { // d = 1..31; m = 1..12
return (d+=m<3? y--:y-2, d+4+ Math.floor(23*m/9)+ Math.floor(y/4)
- Math.floor(y/100)+Math.floor(y/400))%7;
}

which (under very limited testing) appears to give the expected result
in IE.

Now in the foot of zeller-c.htm; unless my testing has blundered, it is
OK for all months in 401 years, and for at least some days in the months
(the dependence on d is obviously correct).

It could be shortened a bit, but not by a line, by using |0 instead of
Math.floor.
 
R

Richard Cornford

I think I was hoping there would be a "compile-time" constant
expression optimization, but I don't know whether such occurs
in js implementations. Probably not.

That is certainly a possible optimisation, but not something that I
would necessarily expect from a JS implementation.

Hey, tertiary conditionals aren't allowed!! :)

I don't see why not. If the trade of between the less efficient tertiary
conditional and not having to do so many NOT operations were more
significant then it would be a good idea. In this context reducing the
number of NOT operations doesn't provide enough benefit to decide the
matter one way or the other.

Here's another version that executes as fast, or marginally
faster, than LastOfMonth2 in IE, Netscape, Mozilla and Opera,
and gives the same results over a similar (wider) range in my
tests:

function LastOfMonth3(y, m) { // m = 0..11
return 29 + !m + (m*2.58 | 0) - (--m*2.58 | 0)
- ( !!m || 2 + !y
+ ( !!( (y & 3) || y & 15 && !(y % 25) )) ) ; // ~ CGjrs
}

One of the reasons I was using the tertiary conditional, or one of my
other two proposals:-

(y & 3)||((y & 15 && !(y % 25))||!y)

(y & 3)||(((y & 15)||!y)&&!(y % 25))

- was to move the - !y - operation to the right of - (y & 3) - so that
in addition to only being done for one month in 12 it would then only be
done for one year in 4 (less, once it is to the right of - (y & 15) -).

My test still suggest that my version is (very fractionally) faster than
your latest, but I had to loop through 90,000 years to get a difference
of < 1% and a minority of tests came out the other way around so I
expect the influence of background tasks is too great to call this one
way or the other.

Richard.
 
D

Dr John Stockton

JRS: In article <[email protected]>, dated Thu, 29
Jul 2004 20:36:12, seen in Grant Wagner
In fact, given the parameter choices (year and month), here is my
solution:

function LastOfMonth3(y, m) { // m = 0..11
var d = new Date(y, (m + 1), 1);
if (d && d.constructor == Date) {
d.setDate(0);
return d.getDate();
} else {
return -1;
}
}

Note that Netscape 4.78 does not properly handle constructing a
date with a day of 0, as a result, it is necessary to construct
the date for the first of the next month, then set it to zero.

Does it handle new Date(y, m+1, 1, -9) ?

(optimisation : 9 is the digit nearest to the - key)

Okay, I take it all back. Given these performance figures I'd use
Richard's solution :D.

The real benefit is not in the result-code, but in what is learned in
getting the that code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top