Using distance_of_time_in_words in Rails - ruby-on-rails

I have a start month (3), start year (2004), and I have an end year (2008). I want to calculate the time in words between the start and end dates. This is what I'm trying and it's not working..
# first want to piece the start dates together to make an actual date
# I don't have a day, so I'm using 01, couldn't work around not using a day
st = (start_year + "/" + start_month + "/01").to_date
ed = (end_year + "/01/01").to_date
# the above gives me the date March 1st, 2004
# now I go about using the method
distance_of_time_in_words(st, ed)
..this throws an error, "string can't me coerced into fixnum". Anyone seen this error?

You can't just concatenate strings and numbers in Ruby. You should either convert numbers to strings as mliebelt suggested or use string interpolation like that:
st = "#{start_year}/#{start_month}/01".to_date
But for your particular case I think there is no need for strings at all. You can do it like that:
st = Date.new(start_year, start_month, 1)
ed = Date.new(end_year, 1, 1)
distance_of_time_in_words(st, ed)
or even like that:
st = Date.new(start_year, start_month)
ed = Date.new(end_year)
distance_of_time_in_words(st, ed)
See Date class docs for more information.

Given that the context in which you are calling the method is one that knows the methods from ActionView::Helpers::DateHelper, you should change the following:
# first want to piece the start dates together to make an actual date
# I don't have a day, so I'm using 01, couldn't work around not using a day
st = (start_year.to_s + "/" + start_month.to_s + "/01").to_date
ed = (end_year.to_s + "/01/01").to_date
# the above gives me the date March 1st, 2004
# now I go about using the method
distance_of_time_in_words(st, ed)
=> "almost 3 years"
So I have added calls to to_s for the numbers, to ensure that the operation + is working. There may be more efficient ways to construct a date, but yours is sufficient.

Related

formatting function output similar os.date() in lua

I have a function that get current time and do some calculation that return a table. Something like this:
functuin newtime()
t1 = os.date("*t")
-- do some calculation that calculate this variable chha_ye , chha_mo , chha_da
t={}
t["ye"] = chha_ye
t["mo"] = chha_mo
t["da"] = chha_da
return t
end
Now I can get newtime().ye.
I need formatting output of this function something similar os.date()
for example, if chhaa_ye = 4 and chha_mo = 9 :
newtime("%ye")
4
newtime("%ye - %mo")
4 - 9
Default os.date do this, for example
os.date("%Y")
22
os.date("%Y - %m")
22 - 10
How should I do this?
I will provide an answer based on the comment from Egor, slightly modified to make the answer directly testable in a Lua interpreter. First, the os.date function from Lua is quite handy, it returns a hashtable with the corresponding field/values:
if format is the string "*t", then date returns a table with the following fields: year, month (1–12), day (1–31), hour (0–23), min (0–59), sec (0–61, due to leap seconds), wday (weekday, 1–7, Sunday is 1), yday (day of the year, 1–366), and isdst (daylight saving flag, a boolean). This last field may be absent if the information is not available.
We could test the function with the following code snippet:
for k,v in pairs(os.date("*t")) do
print(k,v)
end
This will print the following:
month 10
year 2022
yday 290
day 17
wday 2
isdst false
sec 48
min 34
hour 9
The gsub function, intended for string substitutions, is not limited to a single string, but could take a table as argument.
string.gsub (s, pattern, repl [, n])
If repl is a table, then the table is queried for every match, using the first capture as the key.
function my_stringformat (format)
local date_fields = os.date("*t")
local date_string = format:gsub("%%(%a+)", date_fields)
return date_string
end
The function can be used as most would expect:
> my_stringformat("%year - %month")
2022 - 10
Obviously, it's trivial to rename or add the fields names:
function my_stringformat_2 (format)
local date_fields = os.date("*t")
-- Rename the fields with shorter name
date_fields.ye = date_fields.year
date_fields.mo = date_fields.month
-- Delete previous field names
date_fields.year = nil
date_fields.month = nil
-- Interpolate the strings
local date_string = format:gsub("%%(%a+)", date_fields)
-- return the new string
return date_string
end
And the behavior is the same as the previous one:
> my_stringformat_2("%ye - %mo")
2022 - 10

mapping date with unique id ruby on rails

I want to write an action where I can return one quote per day from DB. my db look like this
|id|quote|
so,if I call this action today it should return the same quote throughout the day but tomorrow it should return another quote from the db. I thought of mapping the quote ids to the today's date but not sure how to do it.
You can get consecutive quotes via:
Quote.order(:id).offset(0).first
Quote.order(:id).offset(1).first
Quote.order(:id).offset(2).first
# ...
To get a quote for the current day, you can utilize Date#jd which returns the date's Julian day:
Date.parse('2020-06-01').jd #=> 2459002
Date.parse('2020-06-02').jd #=> 2459003
Date.parse('2020-06-03').jd #=> 2459004
Since you (probably) don't have that many quotes, this value needs to be transformed into a value we can pass to offset, i.e. a value between 0 and the total number of quotes. This is where the modulo operator % can help – it returns just that, looping over at the end: (e.g. for 22 quotes)
Date.parse('2020-06-01').jd % 22 #=> 18
Date.parse('2020-06-02').jd % 22 #=> 19
Date.parse('2020-06-03').jd % 22 #=> 20
Date.parse('2020-06-04').jd % 22 #=> 21
Date.parse('2020-06-05').jd % 22 #=> 0
Date.parse('2020-06-06').jd % 22 #=> 1
Putting it all together:
require 'date'
def quote_of_the_day(date = Date.current)
offset = date.jd % Quote.count
Quote.order(:id).offset(offset).first
end
Note that this runs two queries: one to get the total number of quotes and another one to get the quote for the given date. You might want to cache the result.
Also note that adding new quotes might have unexpected results because the modulo operation would return a totally different offset:
Date.parse('2020-06-03').jd % 22 #=> 20
# after adding a new Quote
Date.parse('2020-06-03').jd % 23 #=> 5
You can compensate this by adjusting the jd result accordingly:
ADJUSTMENT = 15
(Date.parse('2020-06-03').jd + ADJUSTMENT) % 23 #=> 20

Rails absolute time from reference + french "ago" string

I need to reimport some data that was exported using the "ago" stringification helper, in French.
I have a reference Time/DateTime date at which the import was done, and from there I need to substract this "time ago" difference to find the absolute time.
I need to code the parse_relative_time method below
Some sample input/output of what I'm trying to achieve
IMPORT_DATE = Time.parse('Sat, 11 Jun 2016 15:15:19 CEST +02:00')
sample_ago_day = 'Il y a 5j' # Note : 'Il y a 5j" = "5d ago"
parse_relative_time(from: IMPORT_DATE, ago: sample_ago_day)
# => Should output sthing like Sat, 6 Jun 2016 (DateTime object)
sample_ago_month = 'Il y a 1 mois' # Note : 'Il y a 5j" = "1 month ago"
parse_relative_time(from: IMPORT_DATE, ago: sample_ago_month)
# => 11 May 2016 (it's not big deal if it's 10 or 11 or 12 because of months with odd numbers, just need something approximate)
EDIT
Range of values
"il y a xj" -> x belongs to (1..31)
"il y a y mois" -> y belongs to (2..10) and "un"
(for one)
Let's divide the problem into 2 sub-tasks:
Parse the 'ago' string
Since there is no reversible way in ruby to parse an 'ago' string, lets use regular expressions to extract the data as seconds:
def parse_ago(value)
# If the current value matches 'il y a Xj'
if match = /^il y a (.*?)j$/i.match(value)
# Convert the matched data to an integer
value = match[1].to_i
# Validate the numeric value (between 1 and 31)
raise 'Invalid days value!' unless (1..31).include? value
# Convert to seconds with `days` rails helper
value.days
# If the current value matches 'il y a X mois'
elsif match = /^il y a (.*?) mois$/i.match(value)
# If the matched value is 'un', then use 1. Otherwise, use the matched value
value = match[1] == 'un' ? 1 : match[1].to_i
# Validate the numeric value (between 1 and 10)
raise 'Invalid months value!' unless (1..10).include? value
# Convert to seconds with `months` rails helper
value.months
# Otherwise, something is wrong (or not implemented)
else
raise "Invalid 'ago' value!"
end
end
Substract from current time
This is pretty straightforward; once we have the seconds from the 'ago' string; just call the ago method on the seconds extracted from the 'ago' string. An example of usage of this method for Ruby on Rails:
5.months.ago # "Tue, 12 Jan 2016 15:21:59 UTC +00:00"
The thing is, you are substracting it from IMPORT_DATE, and not from current time. For your code, you need to specify the current time to IMPORT_DATE:
parse_ago('Il y a 5j').ago(IMPORT_DATE)
Hope this helps!

Lua ISO 8601 datetime parsing pattern

I'm trying to parse a full ISO8601 datetime from JSON data in Lua.
I'm having trouble with the match pattern.
So far, this is what I have:
-- Example datetime string 2011-10-25T00:29:55.503-04:00
local datetime = "2011-10-25T00:29:55.503-04:00"
local pattern = "(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)%.(%d+)"
local xyear, xmonth, xday, xhour, xminute,
xseconds, xmillies, xoffset = datetime:match(pattern)
local convertedTimestamp = os.time({year = xyear, month = xmonth,
day = xday, hour = xhour, min = xminute, sec = xseconds})
I'm stuck at how to deal with the timezone on the pattern because there is no logical or that will handle the - or + or none.
Although I know lua doesn't support the timezone in the os.time function, at least I would know how it needed to be adjusted.
I've considered stripping off everything after the "." (milliseconds and timezone), but then i really wouldn't have a valid datetime. Milliseconds is not all that important and i wouldn't mind losing it, but the timezone changes things.
Note: Somebody may have some much better code for doing this and I'm not married to it, I just need to get something useful out of the datetime string :)
The full ISO 8601 format can't be done with a single pattern match. There is too much variation.
Some examples from the wikipedia page:
There is a "compressed" format that doesn't separate numbers: YYYYMMDD vs YYYY-MM-DD
The day can be omited: YYYY-MM-DD and YYYY-MM are both valid dates
The ordinal date is also valid: YYYY-DDD, where DDD is the day of the year (1-365/6)
When representing the time, the minutes and seconds can be ommited: hh:mm:ss, hh:mm and hh are all valid times
Moreover, time also has a compressed version: hhmmss, hhmm
And on top of that, time accepts fractions, using both the dot or the comma to denote fractions of the lower time element in the time section. 14:30,5, 1430,5, 14:30.5, or 1430.5 all represent 14 hours, 30 seconds and a half.
Finally, the timezone section is optional. When present, it can be either the letter Z, ±hh:mm, ±hh or ±hhmm.
So, there are lots of possible exceptions to take into account, if you are going to parse according to the full spec. In that case, your initial code might look like this:
function parseDateTime(str)
local Y,M,D = parseDate(str)
local h,m,s = parseTime(str)
local oh,om = parseOffset(str)
return os.time({year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s})
end
And then you would have to create parseDate, parseTime and parseOffset. The later should return the time offsets from UTC, while the first two would have to take into account things like compressed formats, time fractions, comma or dot separators, and the like.
parseDate will likely use the "^" character at the beginning of its pattern matches, since the date has to be at the beginning of the string. parseTime's patterns will likely start with "T". And parseOffset's will end with "$", since the time offsets, when they exist, are at the end.
A "full ISO" parseOffset function might look similar to this:
function parseOffset(str)
if str:sub(-1)=="Z" then return 0,0 end -- ends with Z, Zulu time
-- matches ±hh:mm, ±hhmm or ±hh; else returns nils
local sign, oh, om = str:match("([-+])(%d%d):?(%d?%d?)$")
sign, oh, om = sign or "+", oh or "00", om or "00"
return tonumber(sign .. oh), tonumber(sign .. om)
end
By the way, I'm assuming that your computer is working in UTC time. If that's not the case, you will have to include an additional offset on your hours/minutes to account for that.
function parseDateTime(str)
local Y,M,D = parseDate(str)
local h,m,s = parseTime(str)
local oh,om = parseOffset(str)
local loh,lom = getLocalUTCOffset()
return os.time({year=Y, month=M, day=D, hour=(h+oh-loh), min=(m+om-lom), sec=s})
end
To get your local offset you might want to look at http://lua-users.org/wiki/TimeZone .
I hope this helps. Regards!
There is also the luadate package, which supports iso8601. (You probably want the patched version)
Here is a simple parseDate function for ISO dates. Note that I'm using "now" as a fallback. This may or may not work for you. YMMV 😉.
--[[
Parse date given in any of supported forms.
Note! For unrecognised format will return now.
#param str ISO date. Formats:
Y-m-d
Y-m -- this will assume January
Y -- this will assume 1st January
]]
function parseDate(str)
local y, m, d = str:match("(%d%d%d%d)-?(%d?%d?)-?(%d?%d?)$")
-- fallback to now
if y == nil then
return os.time()
end
-- defaults
if m == '' then
m = 1
end
if d == '' then
d = 1
end
-- create time
return os.time{year=y, month=m, day=d, hour=0}
end
--[[
--Tests:
print( os.date( "%Y-%m-%d", parseDate("2019-12-28") ) )
print( os.date( "%Y-%m-%d", parseDate("2019-12") ) )
print( os.date( "%Y-%m-%d", parseDate("2019") ) )
]]

Date Formatting in actionscript

I have the following problem: I take a date (as a string data type) from the user. Now, I want to know if there is a a function in actionscript that will convert it to a date format. Right now, I am just parsing the string and concatenating the pieces back together. Ie:
changeDateString = date.getFullYear().toString() + '/' + (date.getMonth()+1).toString() + '/' + date.getDate();
But for months like May, it will return "5" and not "05". I have similar problems for days like "9" or "7." Is there something in the library that will do this for me? (For the moment, I can go ahead and manually concatenate the "0" in front, but this seems like a hassle to do.)
I know this is a simple question, but I a novice.
Thanks.
Use a date formatter for that:
http://help.adobe.com/en_US/FlashPlatform/beta/reference/actionscript/3/mx/formatters/DateFormatter.html
You configure your formatter to use the format based on the types listed and use it to output your date.
var formatter:DateFormatter = new DateFormatter();
formatter.formatString = "m/d/Y";
var example:Date = new Date(2010, 0, 5, 10, 25);
trace(formatter.format(example)); // Displays: 01/05/2010
Just use the Pattern Letter/Description grid in the docs to find the right format for your needs.

Resources