Rails - Model Method for Default DateTime Format - ruby-on-rails

In the DB, everything is in DateTime format. However, in my Post model, I would like to specify a default date and time format that is output whenever records are GET. Since this is for an API, I think I'll go with Twitter's format, unless anyone has any better suggestions:
"created_at":"Thu, 06 Oct 2011 19:41:12 +0000"

You could try overriding the as_json method on your model:
def as_json(options)
super(:methods => [:formatted_date])
end
Then have a method that formats the date they way you want
def formatted_date
# format date string here
end
This is assuming your controller is rendering json like format.json { render :json => #object }.
I would suggest using a builder to serialize data (jbuilder or active_model_serializers)

Related

Overriding an active support method with a ruby refinement

I'd like to use a Ruby Refinement to monkey patch an ActiveSupport method for outputting time in a specific format.
My ultimate goal is to have JSON.pretty_generate(active_record.as_json) print all timestamps in UTC, iso8601, 6 decimals. And I want to have all other timestamp printing behave normally.
This is what I have so far:
module ActiveSupportExtensions
refine ActiveSupport::TimeWithZone do
def to_s(_format = :default)
utc.iso8601(6)
end
end
end
class Export
using ActiveSupportExtensions
def export
puts JSON.pretty_generate(User.last.as_json(only: [:created_at]))
end
end
Export.new.export
Which outputs the following (not what I want).
{
"created_at": "2022-04-05 14:36:07 -0700"
}
What's interesting, is if I monkey patch this the regular way:
class ActiveSupport::TimeWithZone
def to_s
utc.iso8601(6)
end
end
puts JSON.pretty_generate(User.last.as_json(only: [:created_at]))
I get exactly what I want:
{
"created_at": "2022-04-05T21:36:07.878101Z"
}
The only issue is that this overrides the entire applications TimeWithZone class, which is not something I want to do for obvious reasons.
Thanks to Lam Phan comment, it's not possible via a refinement unfortunately.
However I was able to do it by override the default timestamp format.
# save previous default value
previous_default = ::Time::DATE_FORMATS[:default]
# set new default to be the utc timezone with iso8601 format
::Time::DATE_FORMATS[:default] = proc { |time| time.utc.iso8601(6) }
puts JSON.pretty_generate(User.last.as_json(only: [:created_at]))
# set the default back if we have one
if previous_default.blank?
::Time::DATE_FORMATS.delete(:default)
else
::Time::DATE_FORMATS[:default] = previous_default
end

Date is not saving in correct format controller in RoR

my controller's action
time = Time.now.strftime("%d-%m-%Y")
# render text: time
u=SectionMst.new( :section_name => params[:section_name], :date_added => time , date_updated => time)
u.save
My modal code is
class SectionMst < ActiveRecord::Base
attr_accessible :date_added, :date_updated, :id, :section_name
end
render text:time is giving correct desired format but saving in %Y-%m-%d format
not able to get WHY??
The default db date format is: %Y-%m-%d %H:%M:%S. You can check this by executing Time::DATE_FORMATS[:db] in your rails console.
You can update this format by defining the format of your choice in an initializer file inside config/initializers/. Example:
# config/initializers/date_time_format.rb
Date::DATE_FORMATS[:db] = "%d-%m-%Y"
Time::DATE_FORMATS[:db] = "%d-%m-%Y %H:%M:%S"
This is the problem of your database, actualy DB support different format of dates, when you send date, after formatting it convert into own way so for avoid that use DB function to convert date. as date_format(date, format)
eg. SELECT ProductName, Price, FORMAT(Now(),'YYYY-MM-DD') AS PerDate
FROM Products
for making that sql query take an help from this link
Format used when converting and saving a date string to database with rails

Parsing a custom DATE_FORMAT with DateTime in Rails

How do I get DateTime to parse a custom date format(i.e. 'x-%Y')?
I've set the format within an initializer with (as per the RoR API):
Time::DATE_FORMATS[:x_year] = 'x-%Y'
Date::DATE_FORMATS[:x_year] = 'x-%Y'
and when I call:
DateTime.strptime('x-2011', 'x-%Y')
The correct result is returned, but
DateTime.parse('x-2011')
Throws an
ArgumentError: invalid date
never heard of such a possibility. However, you could still do something like:
class DateTime
class << self
alias orig_parse parse
end
def self.parse(string, format = nil)
return DateTime.orig_parse(string) unless format
DateTime.strptime(string, Date::DATE_FORMATS[format])
end
end
in your example it might look like that:
Date::DATE_FORMATS.merge!({:xform => "x-%Y"})
DateTime.parse('x-2011', :xform) #=> Sat, 01 Jan 2011 00:00:00 +0000
You could get rid of 'format' attribute and iterate && validate/rescue through DATE_FORMATS instead

Rails ActiveRecord date parsing for i18n (specifically european date formats)

I'm working on a rails project for an Australian website. As a result, they want to be able to enter date formats in the more european-standard of 'dd/mm/yyyy' rather than the US-centric 'mm/dd/yyyy'. I have an ActiveRecord model with a Date field. I'm using jQuery's datepicker to provide the date select on a text field, and have it setting the date to a proper format. But, when I try to save the record, it gets the date wrong. Even when I've set the custom date formats in an intializer according to the i18n guide.
>> b = BlogPost.new
>> b.posted_on = '20/07/2010'
=> "20/07/2010"
>> b.posted_on
=> nil
>> b.posted_on = '07/20/2010'
=> Tue, 20 Jul 2010
It seems that Rails is just using Date.parse to convert the string into a Date object. Is there any way to fix this for the whole project? I don't want to have to write custom code for each model.
class Date
class << self
def _parse_with_us_format(date, *args)
if date =~ %r{^(\d+)/(\d+)/(\d+)$}
_parse_without_us_format("#{$3.length == 2 ? "20#{$3}" : $3}-#{$1}-#{$2}", *args)
else
_parse_without_us_format(date, *args)
end
end
alias_method_chain :_parse, :us_format
end
end
Try to change the default date format (in config/environment.rb)
ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.
merge!(default => '%d/%m/%Y %H:%M')
Find out more here http://blog.nominet.org.uk/tech/2007/06/14/date-and-time-formating-issues-in-ruby-on-rails/

XML Serialization is not including milliseconds in datetime field from Rails model

By default, the datetime field from the database is being converted and stripping off the milliseconds:
some_datetime => "2009-11-11T02:19:36Z"
attribute_before_type_cast('some_datetime') => "2009-11-11 02:19:36.145"
If I try to overrride the accessor for this attribute like;
def some_datetime
attribute_before_type_cast('some_datetime')
end
when I try "to_xml" for that model, I get the following error:
NoMethodError (undefined method
`xmlschema' for "2009-11-11
02:19:36.145":String):
I have tried to parse the String to a Time object but can't get one to include the milliseconds;
def some_datetime
Time.parse(attribute_before_type_cast('some_datetime').sub(/\s/,"T").sub(/$/,"Z"))
end
Can anyone help get get a datetime with milliseconds rendered by to_xml?
As it turns out, I can exclude the original datetime field, and add a custom method which in turn renders the datetime as a string to the to_xml. This feels hackish, but it's working.. Is there another way to get milliseconds directly in the original datetime field?
In each model, I exclude "except" the field names that have datetimes that I want changed, and I include "methods" with the same name returning the attribute before it is typecasted.
def to_xml(options = {})
options[:methods] = [:some_datetime]
options[:except] = [:some_datetime]
super
end
def some_datetime
attribute_before_type_cast('some_datetime')
end
Rendering to_xml is working great with models included and any other options I pass in.
I have started to learn Ruby and was impressed by Mats "Principle of Least Surprise".
But the Date and Time implementation in Ruby ( and Rails ) is full of surprises:
Starting with a plain irb:
require 'time'
=> true
dt = Time.now
=> 2010-05-31 17:18:39 +0100
Time.parse(dt.to_s) == dt
=> false !?!?!?!?
dt.to_s(:db)
ArgumentError: wrong number of arguments(1 for 0)
from (irb):5:in to_s'
from (irb):5
from C:/Ruby19/bin/irb:12:in'
ok lets take some Rails:
sqlserver_test/development?: dt2 = Time.zone.now
=> Mon, 31 May 2010 17:24:54 CEST +02:00
sqlserver_test/development: dt2.class
=> ActiveSupport::TimeWithZone
sqlserver_test/development: Time.zone.parse(dt2.to_s) == dt2
=> false
sqlserver_test/development: dt2.to_s(:db)
=> "2010-05-31 15:24:54"
sqlserver_test/development: dt2.to_s(:iso8601)
=> "2010-05-31 17:24:54 +0200"
sqlserver_test/development: dt2.to_s(:iso8601) == dt2.iso8601
=> false
( all running on Ruby 1.9.1 with Rails 2.3.5 on Windows Xp )
Currently I only find several "hacks" regarding DateTime fields and databases
but no clean solution WITHOUT surprises ...

Resources