"Fuzzy" date with optional day/month - ruby-on-rails

I would like to store date for some data in Rails app. User can select a date, sometimes it would be specific (like "1 Jan 2010") but sometimes he knows only month & year ("Jan 2010") or even just the year (2010). Is there a standard method to store (and provide input) for that in Rails? I know I could just create 3 separate columns in model, but perhaps there is a nice gem for it.

I created a gem to do exactly this:
http://rubygems.org/gems/date_time_precision
For example:
require 'date_time_precision/format/string'
Date.new(2010, 1).to_s(:long)
# => "January 2010"

Have you ever heard of the Chronic gem?
These are some usage examples from the Github documentation.
require 'chronic'
Time.now #=> Sun Aug 27 23:18:25 PDT 2006
Chronic.parse('tomorrow')
#=> Mon Aug 28 12:00:00 PDT 2006
Chronic.parse('monday', :context => :past)
#=> Mon Aug 21 12:00:00 PDT 2006
Chronic.parse('this tuesday 5:00')
#=> Tue Aug 29 17:00:00 PDT 2006
Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
#=> Tue Aug 29 05:00:00 PDT 2006
Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
#=> Sat May 27 12:00:00 PDT 2000
Chronic.parse('may 27th', :guess => false)
#=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
Chronic.parse('6/4/2012', :endian_precedence => :little)
#=> Fri Apr 06 00:00:00 PDT 2012
Chronic.parse('INVALID DATE')
#=> nil

Related

strftime(%Z) returns wrong result

I have this date
date = Mon, 15 Aug 2016 13:00:00 UTC +00:00
which is ActiveSupport::TimeWithZone class
Then, I need to get the time in time zone "Fiji"
start_in_time_zone = date.in_time_zone("Fiji")
This returns Tue, 16 Aug 2016 01:00:00 +12 +12:00
Then, I need to present the date with the name of the time zone, so
time_zone_abbr = start_in_time_zone.strftime("%Z")
It should return "FJT"
but returns "+12"
Any idea why?
I am using ruby 2.3.7 and rails 4.2.7
UPDATE
If I do
start_in_time_zone = date.in_time_zone("Madrid")
it returns
"CEST"
UPDATE 2
I have tried to see where the problem is by setting different time.
date=Time.utc(2018, 07, 25, 20, 30, 45)
date.class #=> Time
date.in_time_zone("Madrid") #=> Wed, 25 Jul 2018 22:30:45 CEST +02:00
date.in_time_zone("Fiji") #=> Thu, 26 Jul 2018 08:30:45 +12 +12:00
date.in_time_zone("EST") #=> Wed, 25 Jul 2018 15:30:45 EST -05:00
Sadly, it seems there is no 'FJT' abbreviation assigned to 'Fiji' in timezone data used by Rails. Also, support for those abbreviations seems patchy regarding Pacific timezones.
irb(main):002:0> DateTime.now.in_time_zone('Samoa').strftime('%Z')
=> "+13"
irb(main):003:0> DateTime.now.in_time_zone('Midway Island').strftime('%Z')
=> "SST"
irb(main):004:0> DateTime.now.in_time_zone('Samoa').strftime('%Z')
=> "+13"
irb(main):005:0> DateTime.now.in_time_zone('Tokelau Is.').strftime('%Z')
=> "+13"
irb(main):006:0> DateTime.now.in_time_zone('Wellington').strftime('%Z')
=> "NZST"
UTC offset is displayed as fallback. If it's any help, remember that full name and additional information can be retrieved with .time_zone.tzinfo on ActiveSupport::TimeWithZone objects. 'FJ' code is recognized by TZInfo::Country.
irb(main):056:0> TZInfo::Country.get('FJ')
=> #<TZInfo::Country: FJ>
irb(main):057:0> TZInfo::Country.get('FJ').zone_info
=> [#<TZInfo::CountryTimezone: Pacific/Fiji>]

How do get a random DateTime rounded to beginning of hour in Rails?

Basically I'd like to get a random datetime within the last year:
rand(1.year).ago #=> Sun, 22 Sep 2013 18:37:44 UTC +00:00 (example)
But how do I go about specifying or limiting this to times on the hour? For example:
Sun, 22 Sep 2013 18:00:00 UTC +00:00
Sat, 02 Nov 2013 10:00:00 UTC +00:00
Fri, 12 Apr 2013 21:00:00 UTC +00:00
I finally found what I was looking for. #Stoic's answer is very good but I found this available method (http://api.rubyonrails.org/classes/DateTime.html):
rand(1.year).ago.beginning_of_hour
Does exactly the same thing but looks neater and prevents you from having to write your own function.
Rounding datetime to the nearest hour in Rails would be
(DateTime.now + 30.minutes).beginning_of_hour
Not the answer to the actual question, but it does answer the title of the question (which is how i got here).
Try this:
def random_time_to_nearest_hour
time = rand(1.year).ago
time - time.sec - 60 * time.min
end
Examples:
[1] pry(main)> random_time_to_nearest_hour
=> Sun, 28 Apr 2013 16:00:00 UTC +00:00
[2] pry(main)> random_time_to_nearest_hour
=> Sat, 08 Jun 2013 15:00:00 UTC +00:00
[3] pry(main)> random_time_to_nearest_hour
=> Thu, 22 Aug 2013 23:00:00 UTC +00:00
[4] pry(main)> random_time_to_nearest_hour
=> Tue, 29 Jan 2013 14:00:00 UTC +00:00
[5] pry(main)> random_time_to_nearest_hour
=> Tue, 13 Aug 2013 06:00:00 UTC +00:00
[6] pry(main)> random_time_to_nearest_hour
=> Mon, 03 Jun 2013 08:00:00 UTC +00:00
[7] pry(main)>
Note that, this method will always floor down to the nearest hour, but since you are anyways generating a random time, it wont matter if this time is getting floor'ed down or getting round'ed. :)

active record query over datetime array

Given I have an array of datetimes:
array = [Sat, 30 Jul 2011 00:00:00 CEST +02:00, Sat, 30 Jul 2011 00:15:00 CEST +02:00, Sat, 30 Jul 2011 00:30:00 CEST +02:00, Sat, 30 Jul 2011 00:45:00 CEST +02:00
I want my model class method to return the datetimes that dont match (aren't scheduled)
Sat, 30 Jul 2011 00:00:00 CEST +02:00
#appointment.rb (with colum `date` as DateTime)
def self.booked(array)
where("date NOT IN (?)", array)
end
Thx for advise!
It's because your array is malformed:
array = [Sat, 30 Jul 2011 00:00:00 CEST +02:00, Sat, 30 Jul 2011 00:15:00 CEST +02:00, Sat, 30 Jul 2011 00:30:00 CEST +02:00, Sat, 30 Jul 2011 00:45:00 CEST +02:00
notice it's separating the days as individual elements. Or is this your doing? If not, you should probably convert them all to actual DateTime objects (using #parse probably) and then put them in the array.
Also, you might want to make that method into a scope:
scope :booked, lambda { |datetimes| where("date NOT IN (?)", datetimes) }
I'm guessing that date is actually a date in the database, not a datetime or timestamp. So, you need to convert your Ruby Datetimes to just Date instances:
def self.booked(array)
where("date NOT IN (?)", array.map(&:to_date))
end
If you don't convert them to Dates by hand, AR won't know that it is supposed to convert them to dates for the database; then, depending on the underlying database, you could get empty results or possibly errors.
Your code look fine to me, but you need to provide an array, like this:
where(['date NOT IN(?)', array])
Here is an example with a Webinar class with a date column
>> array = Webinar.all[0..2].map(&:date)
=> [Wed, 04 May 2011 02:16:00 PDT -07:00, Tue, 05 Apr 2011 06:00:00 PDT -07:00, Thu, 30 Jun 2011 07:30:00 PDT -07:00]
>> count_without_array = (Webinar.count - array.size)
>> Webinar.where(['date NOT IN(?)', array]).count == count_without_array
=> true
Keep in mind that date must be exact match, if there is 1 second difference its not going to work.

In Rails, what's the nicest way to create a specific time (not now) in a particular time zone?

Here's one way, but can you think of a more idiomatic way?
>> Time.use_zone('Sydney'){ Time.zone.parse('2011-04-12 2pm') }
=> Tue, 12 Apr 2011 14:00:00 EST +10:00
I think you're looking for
Time.find_zone('Alaska').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 AKST -09:00
Time.find_zone('Amsterdam').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 CET +01:00
Time.find_zone('Sydney').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 EST +11:00
Time.find_zone('Wellington').local(2011,1,1)
=> Sat, 01 Jan 2011 00:00:00 NZDT +13:00
This also works with parse
Time.find_zone('Sydney').parse('2011-04-12 2pm')
=> Tue, 12 Apr 2011 14:00:00 EST +10:00
For parsing a date within a specific time zone, you can use ActiveSupport::TimeZone
> ActiveSupport::TimeZone["Sydney"].parse("2011-04-12 2pm")
=> Tue, 12 Apr 2011 14:00:00 EST 10:00
TimeZone API documentation is here:
http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html#method-c-5B-5D
This is what I use:
Time.zone.local(2011, 4, 12, 14, 0)
As said above, to create a time in a specific timezone (e.g., 4/10/2014 1:30pm New York):
#event.start_time = Time.find_zone('Eastern Time (US & Canada)').local(2014,4,10,13,30)
=> Thu, 10 Apr 2014 13:30:00 EDT -04:00
#event.start_time.utc
=> 2014-04-10 17:30:00 UTC
When it is saved to your db, it will be converted to UTC (in Postgres at least if using a timestamp type in your migration), and on future access it will be displayed relative to the application timezone set in config/application.rb
To properly display the local time, we also store the timezone name (e.g., 'Eastern Time (US & Canada)' ) in our database. So, when we want to print the time in our views, we do...
#event.start_time.in_time_zone(#event.timezone)
=> Thu, 10 Apr 2014 13:30:00 EDT -04:00
To get the abbreviated timezone (e.g., EST)
#event.start_time.in_time_zone(#event.timezone).zone
=> "EDT"
How about using the *in_time_zone* helper..
Time.now.in_time_zone('Sydney')

How can I customize Rails' date parsing from params?

I want to format dates in jQuery using the UI datepicker like this:
Sunday - 11/28/2010
How/where can I plug in a custom parser for Rails to make it so this works:
def create
puts params[:event] #=> {"my_date"=>"Sunday - 11/28/2010"}
event = Event.new(params[:event])
puts event.my_date #=> nil
end
This works:
def create
puts params[:event] #=> {"my_date"=>"11/28/2010"}
event = Event.new(params[:event])
puts event.my_date #=> Mon, 22 Nov 2010
end
What's the Rails way to do this?
You could use Chronic, a natural language date/time parser
Time.now #=> Sun Aug 27 23:18:25 PDT 2006
Chronic.parse('tomorrow')
#=> Mon Aug 28 12:00:00 PDT 2006
Chronic.parse('monday', :context => :past)
#=> Mon Aug 21 12:00:00 PDT 2006
Chronic.parse('this tuesday 5:00')
#=> Tue Aug 29 17:00:00 PDT 2006
Chronic.parse('may 27th', :guess => false)
#=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
http://chronic.rubyforge.org/
params[:event][:my_date] = Date.parse(params[:event][:my_date])
event = Event.new(params[:event])
Exactly what you want:
http://www.mobalean.com/blog/2010/11/19/seamless-rails-integration-with-jquery-tools-dateinput

Resources