Retrieve Hash values with key in a specific date format - ruby-on-rails

I'm using Rails. I've stored a count by month in a postgres db as a hash using hstore.
The stored hash is formatted as follows:
{"2017-03-01 00:00:00 UTC"=>"10", "2017-04-01 00:00:00 UTC"=>"3"}
I'm struggling to find a great way to retrieve specific month counts from this hash due to the date format used for the key.
QUESTION
What is the best way to format a string to match the current hash key date format?
For example for March in the Hash the key is "2017-03-01 00:00:00 UTC"
However, a new DateTime for March 1 2017 is formatted as "2017-03-01T00:00:00+00:00"
Or is it best to change the format of how I am storing the hash in the first place?

If you need a timestamp in a specific format, the standard tool to use is DateTime#strftime (all the time-ish classes will have a strftime method and they all behave the same). In your case:
some_datetime.utc.strftime('%Y-%m-%d %H:%M:%S %Z')
And hooking that up to ActiveRecord:
Model.where('your_hstore -> :key', :key => some_datetime.utc.strftime('%Y-%m-%d %H:%M:%S %Z'))
Or:
Model.where('your_hstore -> ?', some_datetime.utc.strftime('%Y-%m-%d %H:%M:%S %Z'))
%Z should be the "Time zone abbreviation name" and for me it produces strings like 'UTC', 'PDT', ... If your strftime (which almost certainly is just a wrapper around the system's libc version of strftime) doesn't produce the strings that you want then you have some options:
Drop the timezone completely if it will always be UTC. Then the keys would look like 2017-03-01 00:00:00 and you'd use '%Y-%m-%d %H:%M:%S' as your strftime format string.
If they keys are actually just dates as they appear to be, then use dates and drop the time-of-day. Then your keys would look like 2017-03-01, you'd use Date instances in Ruby rather than DateTimes, and you'd say some_date.strftime('%Y-%m-%d') or some_date.iso8601 in Ruby to get your hstore keys.
If you are using non-UTC timezones, then convert everything to UTC and go with 1 or 2.
If you don't want any of the above, switch to numeric timezone offsets (2017-05-10 18:05:57 +0000, 2017-05-10 18:06:48 +00:00, ...) and use %z, %:z, or %::z in the strftime format string (see the docs for difference between these three).
These of course require reworking any data you already have in the database but it is best to get the out of the way sooner rather than later.

Related

Ruby date time parse to get 'th'

I've got date as string '2020-02-10 8,00' which I want to convert into Monday, 10th of February. I'm aware of this old topic however I cannot find (or use) any related information.
All I have is just parsed string to date - Date.parse '2020-02-10 8,00'
You are halfway there! Date.parse '2020-02-10 8,00' produces a ruby Date object, as you have noted. You now have to apply strftime. However strftime doesn't have any ordinalization so that piece has to be done manually.
date = Date.parse('2020-02-10 8,00')
date.strftime("%A, #{date.day.ordinalize} of %B") #=> Monday, 10th of February
the ordinalize method is provided by ActiveSupport.
If this format will be used multiple times in your app, you may wish to add an app-wide format:
# in config/initializers/time_formats.rb
Date::DATE_FORMATS(:ordinalized_day) = lambda{|date| date.strftime("%A, #{date.day.ordinalize} of %B")}
# anywhere in the app
Date.today.to_formatted_s(:ordinalized_day)

Sort by date in jsonb postgres

I have a model where the data is stored in json format in a jsonb column in postgres.
I want to sort the output by a data field using an activerecord query.
Model.all.order("json_data -> 'date'")
gives me an output but orders it alphabetically based on the date string.
Is there an easy way I can sort this as a date?
Note: The dates are in the following format:
"Fri, 24 Jun 2016 04:13:26 -0700"
If the date is in a sensible format Postgres will deal with this automatically.
Model.all.order("(json_data ->> 'date')::timestamp with time zone DESC")
or
Model.all.order("(json_data ->> 'date')::timestamptz DESC")
If your date field string is a little unorthodox, you can do the following
Model.all.order("to_timestamp(json_data->>'date','Dy, DD Mon YYYY HH24:MI:SS ') DESC")
Details here
Note the ->> there to output the string rather than the json object.
You can of course just create an extra column and store your information there as per #Uzbekjon's answer below.
Is there an easy way I can sort this as a date?
Not as part of the jsonb field, since JSON doesn't know anything about dates.
So, an easy alternative would be to store them as a separate table column.
If you really have to store them as an element of your json field, then I would suggest one of the two options:
Store your field as timestamp. Since, json fields in postgresql support numeric values and it could (potentially) optimize the sorting.
Store your date in ISO 8601 format string. It would sort correctly, even if it is a string.

Ruby - I need to convert string into a timestamp

I need to convert a string that represents a date to a timestamp object in Ruby.
For example:
date_string = "18-Feb-2016 09:01:04"
convert to a timestamp like so
2016-02-18 14:01:04
I need to save this to a mysql database were the column is type timestamp.
I have researched this for most of the day and can not find a solution. I know you can use Time.parse but that includes timezone and DateTime.parse().to_time includes the timezone. Since it has to be a timestamp i can not use strftime method.
I need the time to be included because it will be used for calculation purposes.
Any help would be greatly appreciated.
Thank you
TL;DR
datetime = DateTime.parse("18-Feb-2016 09:01:04").to_s(:db)
returns
"2016-02-18 09:01:04"
Here's a quick explanation...
1. Convert your string to a Date object with DateTime.parse
You can use the .parse method from the Date or DateTime class in order to parse a string. The parse method will return a Date object like this:
$ DateTime.parse("18-Feb-2016 09:01:04")
$ => #<DateTime: 2016-02-18T09:01:04+00:00 ((2457437j,32464s,0n),+0s,2299161j)>
.parse is a method provided by Ruby.
2. Format the string with DateTime.parse.to_s
Ruby on Rails gives you access to the DateTime.to_formatted_s method to change the formatting of the Date object prior to storing it in your database.
To match the format that you specified:
$ datetime = DateTime.parse("18-Feb-2016 09:01:04").to_formatted_s
Note: to_s is aliased from to_formatted_s and to_formatted_s is a method provided by Rails, not Ruby.
Use to_datetime method in Rails.
"12-10-2015".to_datetime
=> Mon, 12 Oct 2015 10:36:00 +0000
http://apidock.com/rails/String/to_datetime
Edited to add precise answer.
You can use .to_time or .to_datetime, the .to_time returns the date and time with timezone but the .to_datetime returns full date with week name but it shows +0000 as timezone, you will see the difference in both the formats, see the following example.
# used .to_time
"18-Feb-2016 09:01:04".to_time
## Output
2016-02-18 09:01:04 +0530
# used .to_datetime
"18-Feb-2016 09:01:04".to_datetime
## Output
Thu, 18 Feb 2016 09:01:04 +0000
I've interpreted the question to be that you wish to convert the string "18-Feb-2016 09:01:04" to the string "2016-02-18 14:01:04" (generalized to arbitrary date-time strings, of course).
Let:
str = "18-Feb-2016 09:01:04"
What you want is done in two steps. The first is to convert this string to a DateTime object, that is, an instance of the class DateTime. The second step is to construct the desired string from the DateTime object.
One way to create the DateTime object is to use the method DateTime::parse:
require 'date'
DateTime.parse(str)
#=> #<DateTime: 2016-02-18T09:01:04+00:00 ((2457437j,32464s,0n),+0s,2299161j)>
That works fine for the string format you gave, but can be problematic with other formats. For example:
DateTime.parse "4-5-16 09:01:04"
#=> #<DateTime: 2004-05-16T09:01:04+00:00 ((2453142j,32464s,0n),+0s,2299161j)>
As long as you know the format that will be used, it's generally better to use DateTime#strptime with the appropriate pattern comprised of format directives:
pattern = "%d-%m-%y %H:%M:%S"
DateTime.strptime("4-5-16 09:01:04", pattern)
#=> #<DateTime: 2016-05-04T09:01:04+00:00((2457513j,32464s,0n),+0s,2299161j)>
See DateTime#strftime for the format directives.
For the problem at hand:
dt = DateTime.strptime(str, "%d-%b-%Y %H:%M:%S")
#=> #<DateTime: 2016-02-18T09:01:04+00:00 ((2457437j,32464s,0n),+0s,2299161j)>
The second step is to construct the desired string with the above-referenced strftime method:
dt.strftime("%Y-%m-%d %H:%M:%S")
#=> "2016-02-18 09:01:04"

Ruby: get TimeZone component of string

I'm working with Date strings in Ruby and want to determine if a string was passed with a timezone or not (beyond simply parsing the text). If no timezone is supplied, I want to convert it to a certain timezone dependent on other factors.
Example:
DateTime.parse("2014-01-01T23:59:00")
=> #<DateTime: 2014-01-01T23:59:00+00:00 ((2456659j,86340s,0n),+0s,2299161j)>
DateTime.parse("2014-01-01T23:59:00Z")
=> #<DateTime: 2014-01-01T23:59:00+00:00 ((2456659j,86340s,0n),+0s,2299161j)>
DateTime.parse("2014-01-01T23:59:00PST")
=> #<DateTime: 2014-01-01T23:59:00-08:00 ((2456660j,28740s,0n),-28800s,2299161j)>
The problem is that parsing "2014-01-01T23:59:00" and "2014-01-01T23:59:00Z" (with Zulu) yields the same result, when one is specifying "Zulu" and the other isn't. I'm sure the default behavior is to assume UTC if no zone is supplied.
To check that the date string doesn't end with a time zone, you can check that the minutes and seconds are the end of the string, using this regex: \d{2}:\d{2}$
Here are some examples using debuggex to illustrate:
\d{2}:\d{2}$
Debuggex Demo

Ruby string to Date object

I have a Mongoid field that is of type Date. I'm having all sorts of trouble searching for documents against this specific field. I receive dates as a string in this format: 10/20/2013. I thought something like Date.parse("10/20/2013") or "10/20/2013".to_date would be good enough to let me do something like MyModel.find_by(datefield: date_result) but this is giving me a ton of ArgumentError out of range type issues.
What's the easiest way to turn "10/20/2013" into a simple Date object that I can use to query against databases?
You get this:
Date.parse("10/20/2013")
ArgumentError: invalid date
The problem is 10/20. Ruby is an international language, and the values 10 and 20 are somewhat ambiguous. In the U.S. the "standard" date format is "MMDDYYYY", or %m%d%Y in date parsing terms. The majority of the world uses a different standard though, "DDMMYYYY" or %d%m%Y. Ruby uses the second format, with day first.
Looking at the difference, it's easy to see why Date.parse would be confused and complain. 10 is a sensible day, but 20 is nonsense as far as a month, so Ruby rejects it.
You can fix this by forcing the pattern used for parsing:
Date.strptime('10/20/2013', '%m/%d/%Y')
# => #<Date: 2013-10-20 ((2456586j,0s,0n),+0s,2299161j)>
You can use strptime:
Date.strptime('10/20/2013', '%m/%d/%Y')
=> <Date: 2013-10-20 ((2456586j,0s,0n),+0s,2299161j)>
Read this a list of possible formats
Date.parse("10/20/2013")
=> ArgumentError: invalid date
to
Date.parse("20/10/2013")
=> Sun, 20 Oct 2013

Resources