Rails is wrong about 0000-00-00

Rails is wrong in its coercion of empty dates to nil and requiring a table field to allow NULL as the only alternative to a valid date entry.

NULL = unknown, unavailable, "still waiting" etc.

It's a well established discussion that NULL is needed to allow that third option to zero and non-zero, empty and non-empty.

Rails is wrong in insisting that logic does not extend to dates.

A valid date = an affirmative akin a non-empty string, a non-zero number.

A 0000-00-00 is an affirmative akin to an empty string, or a zero.

NULL is the _third_ option.

A valid date says an event happened. A NULL says we're still waiting for confirmation that the event happened. A 0000-00-00 says that the event did not happen, never will happen. It is not "unknown." It is not "unavailable."

Rails is wrong in asserting that 0000-00-00 is poor database design, and Rails is wrong to prevent the value from being used.

Rails is wrong in asserting that 0000-00-00 is poor database design, and Rails is wrong to prevent the value from being used.

Your approach is wrong in attempting to generate discussion. Your attempt to make your point clear is wrong in that I don't follow what it is you're trying to achieve.

I'm not sure what problem you've hit, what solution you propose, or how you want to implement it, but I do know you think rails is wrong?

Michael Koziarski wrote:

Rails is wrong in asserting that 0000-00-00 is poor database design, and Rails is wrong to prevent the value from being used.

Your approach is wrong in attempting to generate discussion. Your attempt to make your point clear is wrong in that I don't follow what it is you're trying to achieve.

I'm not sure what problem you've hit, what solution you propose, or how you want to implement it, but I do know you think rails is wrong?

Yeah, you're right, I Ieft too many "Rails is wrong" in there. I wrote each one planning for that to be the one after I got done editing, and I got interrupted and sent the message thinking I was done. It reads flamey for sure. My appologies. Anyway...

If a (MySQL) database table declares a datetime field as NOT NULL with a default value of 0000-00-00 00:00:00 and an empty value is submitted to that field via Rails, Rails complains that the field cannot be null. If a string literal of 0000-00-00 00:00:00 is submitted, Rails coerces the value to nil, and once again complains about not null.

I tracked down some bug reports and other posts, and it appears that the Rails core position is that using 0000-00-00 is poor data design, and it is Rails' duty to not allow it to be used, further taking the position that that's what NULL is for.

Well, NULL is for that third alternative. As it stands, Rails allows only two states for dates. A valid date or NULL. There are occassions, rare as they may be, to interpret date data in three ways just like numerics or strings.

-- gw

Greg Willits wrote:

Michael Koziarski wrote:

I don't follow what it is you're trying to achieve.

Dang. Did it again. You wanted scenarios...

A typical example would be a Date Due field where a valid date = when it was done, a NULL value = not done yet that we know of, and a 0000-00-00 value = it has been declared that it never will be done. Reports for this data must be able to distinguish between all three casees.

Additionally, I have legacy data/logic being replicated in Rails that expects to find and handle 0000-00-00 date values a specific way. AFAICT I have no way to continue the legacy data/logic systems using Rails as I cannot populate a date field with 0000-00-00 to remain consistent with existing data.

-- gw

>> I don't follow what it is you're trying to achieve.

Dang. Did it again. You wanted scenarios...

No worries, sorry if I came across a little harsh.

If you can find the trac tickets where this has been discussed and perhaps when it was first introduced we could figure out what was going on.

A date field should contain only valid dates or be null. If you need a third alternative, use a code/switch field or store your date in a string. I don't believe there are any valid scenarios where storing invalid dates in a date field is more appropriate than using a separate code/switch to indicate the additional context.

... snipped parts of prior included posts

A typical example would be a Date Due field where a valid date = when it was done, a NULL value = not done yet that we know of, and a 0000-00-00 value = it has been declared that it never will be done. Reports for this data must be able to distinguish between all three casees.

Ugly Hax and total abuse of a magic value, imo. Use a flag, as per Josh's post.

Additionally, I have legacy data/logic being replicated in Rails that expects to find and handle 0000-00-00 date values a specific way. AFAICT I have no way to continue the legacy data/logic systems using Rails as I cannot populate a date field with 0000-00-00 to remain consistent with existing data.

I don't think rails should be designed to play nicely with your invalid legacy data, personally. Designing the framework around individual cases of legacy usage is a very, very bad idea. Anyways, I can imagine core members responses already, once they figure out what you are asking: "Sounds like a great idea for a plug-in." :wink:

Tim Connor wrote:

A typical example would be a Date Due field where a valid date = when it was done, a NULL value = not done yet that we know of, and a 0000-00-00 value = it has been declared that it never will be done. Reports for this data must be able to distinguish between all three casees.

Ugly Hax and total abuse of a magic value, imo. Use a flag, as per Josh's post.

Just because I've seen it, doesn't mean it's "right" (and v.v.) but I've seen it often enough. Database providers allow the value as legal, so why shouldn't it be used? Nothing magic about it IMO. Most date parsing and validating code I've ever seen accepts it or uses it in some way (not that I've seen everything). It is logical and it avoids the need of exactly the type of field you're proposing (which of course is the typical alternative).

Additionally, I have legacy data/logic being replicated in Rails that expects to find and handle 0000-00-00 date values a specific way. AFAICT I have no way to continue the legacy data/logic systems using Rails as I cannot populate a date field with 0000-00-00 to remain consistent with existing data.

I don't think rails should be designed to play nicely with your invalid legacy data, personally.

Well, there's the difference I guess. I don't see it as an invalid date -- when databases allow/require it as the value in a non-null date field, I see it is as being valid as a "non-date" or "zero date."

Designing the framework around individual cases of legacy usage is a very, very bad idea. Anyways, I can imagine core members responses already, once they figure out what you are asking: "Sounds like a great idea for a plug-in." :wink:

And if I can be pointed to how/where to override this behavior I would be happy -- I've started to dig into Rails internals, but just barely, and still trying to push my way through the multple layers of abstractions for things. I've downloaded the acts_as_monkey outline for plugins, but haven't experimented yet.

-- gw

Michael Koziarski wrote:

>> I don't follow what it is you're trying to achieve.

Dang. Did it again. You wanted scenarios...

No worries, sorry if I came across a little harsh.

Nah, I started it :wink:

If you can find the trac tickets where this has been discussed and perhaps when it was first introduced we could figure out what was going on.

http://dev.rubyonrails.org/ticket/2391

If the answer is that this particular group of people have a binary view of dates, then waddya gonna do?

If I can override the behavior somehow, that would be fine with me too. I can design around it in the future, but I would like the option if it's possible.

-- gw

> If you can find the trac tickets where this has been discussed and > perhaps when it was first introduced we could figure out what was > going on.

http://dev.rubyonrails.org/ticket/2391

If the answer is that this particular group of people have a binary view of dates, then waddya gonna do?

If I can override the behavior somehow, that would be fine with me too. I can design around it in the future, but I would like the option if it's possible.

Unfortunately even if we did want to fix this, I'm not sure what the accessors could return:

Date.civil(0,0,0)

ArgumentError: invalid date   from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/date.rb:727:in `civil'   from (irb):9

Date.ordinal(0,0)

ArgumentError: invalid date   from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/date.rb:707:in `ordinal'   from (irb):10

Ruby's date libraries are having none of that.

Michael Koziarski wrote:

If I can override the behavior somehow, that would be fine with me too. I can design around it in the future, but I would like the option if it's possible.

Unfortunately even if we did want to fix this, I'm not sure what the accessors could return:

Date.civil(0,0,0)

ArgumentError: invalid date   from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/date.rb:727:in `civil'   from (irb):9

Date.ordinal(0,0)

ArgumentError: invalid date   from /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/date.rb:707:in `ordinal'   from (irb):10

Ruby's date libraries are having none of that.

Well, the target is MySQL and not Ruby, so I am approaching this from the perspective of how to get what I want into MySQL, an not so much from what Rails or Ruby would natively prefer to do.

If we skip Rails/Ruby altogether and work directly with MySQL, then here's what we discover (and is the source of my working with 0000-00-00 formatted empty dates):

(these tables below are probably going to get mangled by this email system that's bent on hard wrapping short lines)

CREATE TABLE `time_tests` (   `id` int(11) NOT NULL auto_increment,   `null_date` datetime NULL default NULL,   `zero_date` datetime NOT NULL default '0000-00-00 00:00:00',   PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

Do we want to follow MYSQL, or any specific DB provider, practices, over ruby's, though? And/or the standards? (Don't honestly know what ANSI SQL has to say about 0000-00-00).

Tim Connor wrote:

Do we want to follow MYSQL, or any specific DB provider, practices, over ruby's, though? And/or the standards? (Don't honestly know what ANSI SQL has to say about 0000-00-00).

Isn't that one of the reasons for choosing one database over another--its support of different functionalities?

What's the point of having different dbs if app frameworks reduce them to some wimpy lowest common denominator.

-- gw

What's the point of having different dbs if app frameworks reduce them to some wimpy lowest common denominator.

Why even bother with SQL, then :)? Why not just give every database their own query language that's designed just for them. There are other reasons than API to pick different databases (like performance, storage engines, replication facilities, etc).

DHH wrote:

What's the point of having different dbs if app frameworks reduce them to some wimpy lowest common denominator.

Why even bother with SQL, then :)? Why not just give every database their own query language that's designed just for them. There are other reasons than API to pick different databases (like performance, storage engines, replication facilities, etc).

ok, well, that fork is headed nowhere, but I still don't see what the heck the catastrophe is behind allowing the database to accept defaults it deems as valid, even resorts to, in its own API.

This is the biggest issue for me - the coercion. How “agnostic” is Rails attempting to be if it’s actively overriding or erroring on what the selected DB allows for values?

The 1992 draft SQL standard (sorry, all I have handy at the moment)
says "The <datetime field>s other than SECOND contain non-negative
integer values, constrained by the natural rules for dates using the
Gregorian calendar." This leaves it up to the Gregorian calendar,
which doesn't have a year zero, much less month zero and day zero as
far as I can tell. The ISO 8601 standard allows zero and negative year
values to represent BC dates, but still requires non-zero month and
day values. I've been mostly a DB2 user for 20 years or so, but I
believe that most RDBMS implementations follow the standard and
disallow 0000-00-00 as an invalid date.

John Maenpaa wrote:

The 1992 draft SQL standard (sorry, all I have handy at the moment) says "The <datetime field>s other than SECOND contain non-negative integer values, constrained by the natural rules for dates using the Gregorian calendar." This leaves it up to the Gregorian calendar, which doesn't have a year zero, much less month zero and day zero as far as I can tell. The ISO 8601 standard allows zero and negative year values to represent BC dates, but still requires non-zero month and day values. I've been mostly a DB2 user for 20 years or so, but I believe that most RDBMS implementations follow the standard and disallow 0000-00-00 as an invalid date.

I would argue this is not an issue of "most" or even "standard" -- most code on the planet is probably written with camelCase names -- which doesn't seem to have much impact on Rails' opinion.

This is a simple case of having the database adaptor allow what the database itself considers legal. Not only legal, but Rails is preventing the db from using its own default behavior. It doesn't matter if the DB2, Oracle or Sybase worlds think its dumb. Its an adaptor for MySQL.

-- gw

BTW everyone -- I appreciate the discussion, and the points being made. Not convinced I'm wrong yet, but I do appreciate the time being taken to read & respond.

-- gw

I would argue this is not an issue of "most" or even "standard" -- most code on the planet is probably written with camelCase names -- which doesn't seem to have much impact on Rails' opinion.

The real problem is that you *can't* retrieve that data again. Rails needs to coerce those values into a Date or Time, neither of which accept 0000-00-00. So we can't support it when reading data, there's not much point letting you write it. After all, you can't construct the time object to save in the first place.

Given that ruby can't support that value, coercing to nil seems at least reasonable.