Tuesday, 2 May 2006

Weirdest javascript Date daylight savings bug

FINAL UPDATE: I have tried this today, 7th of May 2008, and the bug is gone from all versions of IE and FF. I think it is the result of some Windows XP patch.

I found out that in Javascript, the Date object can give weird results.
Imagine that new Date(2005,2,27) [Javascript months are 0=11] returns 26 March 2005. If you look closely, you will see that it returns 26.03.2005 23:00:00.
Why? Because then our computers are configured to change the time with one hour. In a sense, this is not an error, as 27.03.2005 00:00:00 does not exist. You either have 1:00 of the 27th or 23:00 of the 26th. It is weird that Javascript both on IE and Netscape (and probably all other browsers) chooses the second option.

[Update thanks to a comment]I live in Romania. 27 of March 2005 is a daylight saving date (a bit non standard). Maybe the same bug can be reproduced for other countries on their respective daylight saving dates.[/Update]

This can yield weird results, as validators sometimes fail to recognize 27.03.2005 as a valid date. The solution for Javascript code: always use new Date(year, month, day, 12, 0, 0). The solution for validators... well, except calling Microsoft and bitching about it or changing the validator JS
file on ALL deployment servers, is either use a home made validator or replace the faulty validator js function in the pages that need date validation.

That means adding in the common site js this function:

function ValidatorConvert(op, dataType, val) {
function GetFullYear(year) {
return (year + parseInt(val.century)) - ((year < val.cutoffyear) ? 0 : 100);
}
var num, cleanInput, m, exp;
if (dataType == "Integer") {
exp = /^\s*[-\+]?\d+\s*$/;
if (op.match(exp) == null)
return null;
num = parseInt(op, 10);
return (isNaN(num) ? null : num);
}
else if(dataType == "Double") {
exp = new RegExp("^\\s*([-\\+])?(\\d+)?(\\" + val.decimalchar + "(\\d+))?\\s*$");
m = op.match(exp);
if (m == null)
return null;
cleanInput = m[1] + (m[2].length>0 ? m[2] : "0") + "." + m[4];
num = parseFloat(cleanInput);
return (isNaN(num) ? null : num);
}
else if (dataType == "Currency") {
exp = new RegExp("^\\s*([-\\+])?(((\\d+)\\" + val.groupchar + ")*)(\\d+)"
+ ((val.digits > 0) ? "(\\" + val.decimalchar + "(\\d{1," + val.digits + "}))?" : "")
+ "\\s*$");
m = op.match(exp);
if (m == null)
return null;
var intermed = m[2] + m[5] ;
cleanInput = m[1] + intermed.replace(new RegExp("(\\" + val.groupchar + ")", "g"), "") + ((val.digits > 0) ? "." + m[7] : 0);
num = parseFloat(cleanInput);
return (isNaN(num) ? null : num);
}
else if (dataType == "Date") {
var yearFirstExp = new RegExp("^\\s*((\\d{4})|(\\d{2}))([-/]|\\. ?)(\\d{1,2})\\4(\\d{1,2})\\s*$");
m = op.match(yearFirstExp);
var day, month, year;
if (m != null && (m[2].length == 4 || val.dateorder == "ymd")) {
day = m[6];
month = m[5];
year = (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3],10))
}
else {
if (val.dateorder == "ymd"){
return null;
}
var yearLastExp = new RegExp("^\\s*(\\d{1,2})([-/]|\\. ?)(\\d{1,2})\\2((\\d{4})|(\\d{2}))\\s*$");
m = op.match(yearLastExp);
if (m == null) {
return null;
}
if (val.dateorder == "mdy") {
day = m[3];
month = m[1];
}
else {
day = m[1];
month = m[3];
}
year = (m[5].length == 4) ? m[5] : GetFullYear(parseInt(m[6], 10))
}
month -= 1;
var date = new Date(year, month, day,12,0,0); //BUG FIX
return (typeof(date) == "object" && year == date.getFullYear() && month == date.getMonth() && day == date.getDate()) ? date.valueOf() : null;
}
else {
return op.toString();
}
}

Resharper 2.0

I've just installed Resharper 2.0. It still has little bugs, but it has that system where you can remember and ignore a set of errors.

What can I say? It's a the greatest software tool EVER. It can generate code, complete code, suggest changes, basically what Resharper 1.5 did, but
now it's WAY better.

The best thing in 2.0 I think it's Live Templates. They work like macros, but from just a few key presses and are way easy to configure. Imagine you
want to do something like :

throw new Exception("Any message");

all you have to do is type thr[Tab] and you get

throw new Exception(""); and the cursor between the quotes.

Of course that means also: itterating through a dictionary, creating for loops, transforming types into other types, automatic creation of aspx code
like datagrid columns, etc.

You want to put a piece of code in an if block, a region or a try catch, just select the code , press ctrl-alt-J and choose the type of block.
You want to add the properties and methods of an object inside your class to your class, just press Alt-Insert and choose the object and the members and you have them all in your class, referenced to the internal object and commented!

The Live Template system includes a scope of the template. That means that the template for creating a new method will not work inside a method, or
aspx code will not be generated into a cs file, etc.

Other two nice tools that I've found are :

- CommentReFlower - which takes care of the comments in your code. Not terribly useful, but time saving when you need to comment code
- RegionsAddIn - it allows you to take pieces of code and move them into regions, or create new ones.

Both are addins for visual studio 2003.

How to stop an object from being garbage collected

You go to the MSDN site and you get this as a proper implementation of the IDisposable interface:

#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool isDisposing)
{
// Check to see if Dispose has already been called.
if (!_isDisposed)
{
_isDisposed=true; //this is what I added, I think it makes this thread safe
if (isDisposing)
{
//Clean up resources
}
}
_isDisposed = true;
}
#endregion

In other words, implement IDisposable to clean up unmanaged resources. Well, unmanaged could be considered managed code from another thread. If your object starts a thread that calls an event, then when you exit the code block where you declared the object, then the event will never get fired. A pending action is unmanaged, therefore you can do something like:

while (!actionFinished) Thread.Sleep(1000);

the action should, of course, set actionFinished to true;

The AS keyword in C# or Safe Casting

If you have ever used code like:
try {
obj1=(TypeObj1)obj2;
} catch {
obj1=null;
}

you need to read this.

The same thing can be written as:
obj1=obj2 as TypeObj1;

In case the conversion doesn't work, the value of obj1 will be null.
Be careful when you do this with value types that don't allow null.

Update:
Also, implicit casting will not work. A code like:
int i=5;
string s=i as string;

will have s being null, not "5".

You also have to be careful about using the "is" word. Code like
if (ctrl is Table) {
Table tbl=(Table)ctrl;
tbl...

actually casts ctrl twice!
a is b translates to (a as b)!=null.

So a nice piece of code would look like this:
Table tbl=ctrl as Table;
if (tbl!=null) {
tbl...

The 'implicit' operator in C#

Did you know that in a class you can do something like:
public static implicit operator type2(type1 value) {
return value.ToType2();
}


Now class type1 will be automatically converted into type2 when used as such.

Example:

public class NullBoolean {
private bool internalBool;

public static NullBoolean False {
return new NullBoolean(false);
}

public static NullBoolean True {
return new NullBoolean(true);
}
public NullBoolean(bool value) {
internalBool=value;
}

public static implicit operator bool(NullBoolean) {
return internalBool;
}
}


with this class all these statements are correct:
NullBoolean nb=null;
nb=NullBoolean.True;
if (nb==true) MessageBox.Show("nb is true"); // no cast

if the word implicit would have been explicit, then the compiler would have required you to cast nb to bool first:

if ((bool)nb==true)...


Add to this Equals, ToString and == overide and you have a nice boolean type that allows for null values.

Update:
First of all implicit operators are a kind of a shortcut for casting. Sometimes this doesn't work, like in foreach loops. Sometimes a code like this will not work:
foreach (RowAdapter ra in dt.Rows) ...

while this would work:
foreach (DataRow dr in dt.Rows) {
RowAdapter ra=dt;...

Second of all, an object like the one above can in NET 2.0 be easily formed with nullable types:
bool? nb;