fabrik42/acts_as_api

DateTime objects are truncating milliseconds

coneybeare opened this issue · 5 comments

I have a test where I create an object, grab the json response (as rendered by acts_as_api), then compare it's values against the object's attributes.to_json.

While this works for nearly all objects, I have noticed something very weird on Rails 4 with DateTime objects.

>> puts apple.updated_at
2014-02-07 18:35:55 UTC
>> puts apple.updated_at.to_json
"2014-02-07T18:35:55.423Z"

...
(make get :show call to server, returning json on apple)
...

>> puts json['apple']['updated_at']
"2014-02-07T18:35:55.000Z"

Here you can see that the controller, calling render_for_api, on the same object, is zeroing (or possible rounding) the milliseconds at the end. Otherwise, they are equal. Do you have any ideas as to why, or how I can resolve this?

Hi Matt,

I don't think milliseconds in as_json/to_json is a default setting. Did you patch anything, e.g. ActiveSupport::TimeWithZone?

Generally speaking - there is no converter in acts_as_api itself to format DateTime or similar objects. This task is done by Rails itself.

Is your database engine truncating the value?

class User < ActiveRecord::Base
  acts_as_api

  api_accessible :public do |template|
     template.add :email
     template.add :current_sign_in_at
   end  
end

class UsersController < ApplicationController
  respond_to :json

  def index
    @users = User.all
    @users << User.new(current_sign_in_at: Time.now)
    respond_with(@users, :api_template => :public, :root => :users)
  end
end

Using MySQL I get truncated values for all records except for the one I'm adding at the end of the collection (which is not saved)

That's exactly what is happening. MySQL doesn't store milliseconds so when read from disk it gets truncated. The object I am comparing it to is in memory newly created.

It seems like Rails itself omits milliseconds when rendering a time as json.

This is from a Rails 3.2 app using Postgres (which stores milliseconds)

User.last.updated_at.as_json
# => "2014-02-09T11:37:26Z" 
User.last.updated_at_before_type_cast
# => "2014-01-31 11:37:26.061182"
User.last.updated_at.usec
# => 61182 

If you need to render milliseconds, you probably need to overwrite ActiveSupport::TimeWithZone::as_json

I don't need to, I just needed them to match for rspec purposes. No biggie