I want to have DateTimes with invalid days of the month to be validated. I'm using Mongoid 4alpha2 with Rails 4, and on my model I have a
field :date_of_birth, type: DateTime
When I do a regular "create" from a controller for a date_of_birth with "1988/02/30", the model gets saved with a date_of_birth of "1988/03/1" instead of getting a DateInvalid error, like a regular DateTime.new(1988,2,30) would in rails console. I'm not sure if moped or mongoid is circumventing the Rails validations on DateTime, has anyone else encountered this?
Here's the rails log
Started POST "/drivers" for 127.0.0.1 at 2014-01-16 10:42:40 -0500
Processing by DriversController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"LiKx1ZToNVtNL9FAEgyLNNWW7mABy2BPKPwVVcTtXKk=", "driver"=>{"field_worker_name"=>"Field Worker", "hack_number"=>"38924", "first_name"=>"test", "middle_initial"=>"", "last_name"=>"testing", "date_of_birth"=>"1988/02/30", "gender"=>"", "nationality"=>"", "language"=>"", "street"=>"something", "apartment_number"=>"", "city"=>"something", "state"=>"NY", "zip_code"=>"02398", "cell_phone"=>"", "other_phone"=>"", "email"=>"", "dmv_number"=>"", "state_of_residence"=>"", "has_health_insurance"=>"false", "health_plan"=>"", "date_of_recertification"=>""}, "commit"=>"Add Driver"}
MOPED: 127.0.0.1:27017 COMMAND database=admin command={:ismaster=>1} runtime: 0.5280ms
MOPED: 127.0.0.1:27017 QUERY database=healthfund_development collection=users selector={"$query"=>{"_id"=>BSON::ObjectId('52d59524544b38160c000000')}, "$orderby"=>{:_id=>1}} flags=[] limit=-1 skip=0 batch_size=nil fields=nil runtime: 0.4150ms
MOPED: 127.0.0.1:27017 QUERY database=healthfund_development collection=drivers selector={"hack_number"=>38924} flags=[] limit=-1 skip=0 batch_size=nil fields={:_id=>1} runtime: 1.1000ms
MOPED: 127.0.0.1:27017 INSERT database=healthfund_development collection=drivers documents=[{"field_worker_name"=>"Field Worker", "hack_number"=>38924, "first_name"=>"test", "middle_initial"=>"", "last_name"=>"testing", "date_of_birth"=>1988-03-01 00:00:00 UTC, "gender"=>"", "street"=>"something", "apartment_number"=>"", "city"=>"something", "state"=>"NY", "zip_code"=>"02398", "cell_phone"=>"", "other_phone"=>"", "email"=>"", "nationality"=>"", "language"=>"", "dmv_number"=>"", "state_of_residence"=>"", "has_health_insurance"=>false, "health_plan"=>"", "date_of_recertification"=>nil, "_id"=>38924, "updated_at"=>2014-01-16 15:42:40 UTC, "created_at"=>2014-01-16 15:42:40 UTC}] flags=[]
COMMAND database=healthfund_development command={:getlasterror=>1, :w=>1} runtime: 3.4590ms
Redirected to http://localhost:3000/drivers/38924
Completed 302 Found in 17ms
Looks like Mongoid is leaving the date parsing up to MongoDB. Check it in the MongoDB shell:
> new Date('1988/02/30')
ISODate("1988-03-01T08:00:00Z")
That's perfectly acceptable behavior for JavaScript's Date
constructor:
Note: Where Date is called as a constructor with more than one argument, if values are greater than their logical range (e.g. 13 is provided as the month value or 70 for the minute value), the adjacent value will be adjusted. E.g.
new Date(2013,13,1)
is equivalent tonew Date(2014,1,1)
, both create a date for2014-01-01
. Similarly for other values:new Date(2013,2,1,0,70)
is equivalent tonew Date(2013,2,1,1,10)
which both create a date for2013-02-01T01:10:00
.
So as far as MongoDB is concerned, 1988/02/30
is 1988 plus 2 months plus 30 days and since 1988 is a leap year, February plus 30 days in March 1st.
You should be using a :type => Date
for a date-of-birth anyway. Of course, Date
has the same "treat it like JavaScript" behavior:
class M
include Mongoid::Document
field :d, :type => Date
end
m = M.create(:d => '1988/02/30')
m.d
# Tue, 01 Mar 1988
so that doesn't help much.
You could report a bug and see what the Mongoid people think about this. In the mean time, if you need stricter parsing, you could do it yourself:
field :date_of_birth, :type => Date
def date_of_birth=(date)
super(Date.parse(date))
end
Then you'll get your exception when you say Model.create(:date_of_birth => '1988/02/30')
.