Description
After upgrading from grape-entity 1.0.1 to 1.0.3, all Time fields in API responses changed format for apps using ActiveSupport:
- Before (1.0.1):
"2026-04-16T10:55:10.597Z" (ISO 8601)
- After (1.0.3):
"2026-04-16 10:55:10 UTC"
This is a silent breaking change for API consumers expecting ISO 8601 timestamps.
Root cause
PR #385 replaced MultiJson.dump with JSON.dump in Entity#to_json: https://github.com/ruby-grape/grape-entity/pull/385/changes#diff-dede7a9b2a44333c10da3b7b006d011d8fddfc23ebaa64b415b5686c7798d296R561
MultiJson.dump internally calls Hash#to_json, which ActiveSupport intercepts to run as_json on all values — converting Time to ISO 8601. JSON.dump bypasses ActiveSupport entirely and serializes Time via its C extension using Time#to_s.
Reproduction
require 'json'
require 'active_support'
require 'active_support/core_ext/object/json'
require 'active_support/core_ext/time'
require 'grape_entity'
class TestEntity < Grape::Entity
expose :published_at
end
t = Time.utc(2026, 4, 16, 10, 55, 10, 597000)
obj = OpenStruct.new(published_at: t)
TestEntity.represent(obj).to_json
# grape-entity 1.0.1 => {"published_at":"2026-04-16T10:55:10.597Z"} ✅
# grape-entity 1.0.3 => {"published_at":"2026-04-16 10:55:10 UTC"} ❌
Environment
- Ruby 4.0.0
- Rails 8.0.5
- grape-entity 1.0.3
Description
After upgrading from grape-entity 1.0.1 to 1.0.3, all
Timefields in API responses changed format for apps using ActiveSupport:"2026-04-16T10:55:10.597Z"(ISO 8601)"2026-04-16 10:55:10 UTC"This is a silent breaking change for API consumers expecting ISO 8601 timestamps.
Root cause
PR #385 replaced
MultiJson.dumpwithJSON.dumpinEntity#to_json: https://github.com/ruby-grape/grape-entity/pull/385/changes#diff-dede7a9b2a44333c10da3b7b006d011d8fddfc23ebaa64b415b5686c7798d296R561MultiJson.dumpinternally callsHash#to_json, which ActiveSupport intercepts to runas_jsonon all values — convertingTimeto ISO 8601.JSON.dumpbypasses ActiveSupport entirely and serializesTimevia its C extension usingTime#to_s.Reproduction
Environment