History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: NH-164
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Mike Doerfler
Reporter: Sergey Koshcheyev
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
NHibernate

Problem with dates earlier than 1.1.1753

Created: 03/Dec/04 04:15 PM   Updated: 25/Feb/05 05:30 PM
Component/s: Core
Affects Version/s: alpha
Fix Version/s: beta-0.7


 Description  « Hide
There is some hack in NHibernate related to handling of Time properties which assumes the earliest year for dates is 1753. This causes problems with Oracle (see http://sourceforge.net/forum/forum.php?thread_id=1189402&forum_id=252014) and also with MySQL, since both allow for dates earlier than year 1753.

 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
Kevin Williams - 04/Dec/04 08:53 AM
I started the thread in the forum.

I suspect that many other dialects support different minimum and maximum date values, so IMHO the NHibernate.Type.DateTimeType needs to get those values from the current dialect before using a hard-coded default.

By checking http://www.analysisandsolutions.com/code/dates.htm, I see that MySQL's TIMESTAMP has the highest minimum and both Oracle and PostgreSQL can go back beyond .NET's capability.

I suggest that the NHibernate.Dialect.Dialect class have both a "public virtual DateTime MinDateTime { get; }" and a "public virtual DateTime MaxDateTime { get; }" signature that each dialect should override. This way, the NHibernate.Type.DateTimeType.Set(IDbCommand,object,int) method can call "Dialect.GetDialect().MinDateTime" to get the value to use in line 45.

As the site I referenced points out, some dialects would need to be artificially limited to .NET's DateTime.MinDate and DateTime.MaxDate.

Does that sound like a reasonable solution?

Mike Doerfler - 04/Dec/04 09:02 AM
I'm thinking about modifying (not really thinking - planning to modify) the DateTimeType, DateType, and TimeType to reflect the standards for DbType

DbType.DateTime
A type representing a date and time value.

DbType.Date:
Date and time data ranging in value from January 1, 1753 to December 31, 9999 to an accuracy of 3.33 milliseconds.

DbType.Time:
Date and time data ranging in value from January 1, 1753 to December 31, 9999 to an accuracy of 3.33 milliseconds.

No - DbType.Date & DbType.Time are not copy mistakes - they are the exact same thing in msdn docs - why???

This will break everyone's (atleast mine) mapping files who relied on DateTime.Min getting converted to null. The only thing that will need to be done is add type="Date" to DateTime properties if MsSql is being used. This is also what the NullableTypes in NHibernateContrib are meant to solve.

Any input?

Mike

Sergey Koshcheyev - 04/Dec/04 10:47 AM
I myself think you should allow for all possible DateTime values to be persisted, and let the database handle any errors. Of course this isn't a database-independent solution, but in this case I think it's appropriate.

By the way, what happens when I have a varchar(10) column in the database, mapped to a string property, and try to save an 11-character string? NHibernate doesn't check this either. MySQL silently ignores the last character for example, other databases may be less forgiving.

What about just mapping DateTime to DbType.Date or maybe DbType.DateTime and TimeSpan to DbType.Time by default and letting the user adjust the DbType in the mapping file?

Oh, and why was DateTime.MinValue converted to null? It doesn't seem to be the case in my copy of NHibernate (but I modified it quite a bit, and MySQL Connector.NET too, so maybe I just didn't notice :-)).

Sergey.

Kevin Williams - 04/Dec/04 01:41 PM
I think Microsoft's documentation is wrong. Their System.Data.OracleClient assembly has roughly (Reflector) this source for their DateTime type:

---------------8<---------------------
static OracleDateTime()
{
      OracleDateTime.MaxValue = new OracleDateTime(DateTime.MaxValue);
      OracleDateTime.MinValue = new OracleDateTime(DateTime.MinValue);
      OracleDateTime.Null = new OracleDateTime(true);
}
---------------8<---------------------

I checked the MySQL driver source code, and the only min date check they have is:

---------------8<---------------------
public bool IsValidDateTime
{
get
{
return year != 0 && month != 0 && day != 0;
}
}
... followed by ...
public static explicit operator DateTime( MySqlDateTime val )
{
if (! val.IsValidDateTime) return DateTime.MinValue;
return val.GetDateTime();
}
---------------8<---------------------

Both are roughly respecting the .NET limitations of the DateTime type. I feel that following the MSDN documentation and restricting the minimum value to 1/1/1753 will create confusion. I strongly feel NHibernate should respect the boundaries of the native provider, or the .NET value boundaries if the native driver allows a wider range of values. By having each dialect specify a DateTime property for minimum and maximum values, both boundary exceptions are avoided and developers aren't surprised.

We had a hard time finding this bug, knowing that Oracle supports a date < 1/1/1753. It was out of sheer frustration that we tried "what if the Oracle driver was limited the same way as MS SQL Server?". Our jaws dropped when the tests confirmed it. Funny, it's not that we had any data going back that far, but the testers put in the .NET min date value and the app stopped returning data.

Kevin Williams - 04/Dec/04 01:47 PM
Oh, to answer a question. The DateTime data type isn't nullable, so an unassigned value will be set to DateTime.MinDate.

For example:

public class MinDateTime
{
    private static DateTime birthdate;

    public static void Main( string[] args )
    {
        Console.WriteLine( birthdate.ToString() );
    }
}

will return "1/1/0001 12:00:00 AM".

Mike Doerfler - 16/Jan/05 08:37 PM
I kept DateType and TimeType just like they are.

The DateTimeType will no longer magically convert a value before 1753 to null. If that was a relied on feature you should convert the property to a NullableDateTime.

To get the most accurate use of System.DateTime use type="Timestamp" - see comments in class TimestampType for why.

Mike Doerfler - 25/Feb/05 05:30 PM
0.7 was released