How can I shorten this (working) code to create future dates in ruby for use as params in URLs?
I'm doing:
require 'cgi'
require 'time'
now=Time.new()
now_year=now.strftime('%Y').to_i
now_month=now.strftime('%m').to_i
now_day=now.strftime('%d').to_i
now_hour=now.strftime('%H').to_i
start_hour=(now_hour + 1)
end_hour=(now_hour + 2)
start_datetime=Time.new(now_year, now_month, now_day, start_hour)
end_datetime=Time.new(now_year, now_month, now_day, end_hour)
starts=CGI.escape(start_datetime.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ"))
ends=CGI.escape(end_datetime.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ"))
and it works, but it seems long and clumsy. Is there an easier way?
2 Answers 2
You can add seconds directly to a Time
object, so you could do:
now = Time.now.utc
starts = now + 3600 # Add 1 hour's worth of seconds (60 * 60)
ends = now + 7200 # Add 2 hours
and then do the strftime
-formatting.
Of course, since you need to do this twice, I'd suggest adding a method to avoid the repetition, i.e.:
def offset_utc_timestamp(offset, format = "%Y-%m-%dT%H:%M:%S.%3NZ")
time = Time.now.utc + offset
time.strftime(format)
end
You may want to split it up differently (i.e. have the method just return a Time
object, which you make into a string elsewhere; or have the method also do the URL escaping).
I'd also recommend using the URI.escape
method instead, if this is intended for URLs
With all that your code becomes:
starts = URI.escape(offset_utc_timestamp(3600))
ends = URI.escape(offset_utc_timestamp(7200))
Alternatively, the ActiveSupport gem has a ton of helpful time stuff, letting you do things like 2.hours.from_now.utc
-
\$\begingroup\$ Looks good. btw I had tried using the URI.escape but it didn't code things correctly for me (or at all) so I switched to CGI.escape at the suggestion of a colleague \$\endgroup\$Michael Durrant– Michael Durrant2014年04月17日 13:31:12 +00:00Commented Apr 17, 2014 at 13:31
-
\$\begingroup\$ @MichaelDurrant Gotcha. I am curious, though, how these URLs should end up looking; there shouldn't really be anything that needs escaping, really. Also, you can pass a regexp or string to
URI.escape
(see docs), telling it what should be forcibly escaped. With your input,URI.escape(string, ":")
will produce the exact same output asCGI.escape(string)
\$\endgroup\$Flambino– Flambino2014年04月17日 13:43:46 +00:00Commented Apr 17, 2014 at 13:43 -
\$\begingroup\$ I should have been more specific, I need it to end up with characters like ":" as
%3A
as I am trying to just replicate what a current param is in our system. \$\endgroup\$Michael Durrant– Michael Durrant2014年04月17日 13:55:03 +00:00Commented Apr 17, 2014 at 13:55 -
\$\begingroup\$ @MichaelDurrant Ah, ok. Well, it's totally up you. I'd probably end up sticking with
CGI.escape
as well, since it does what you want by default. As mentioned, you can useURI.escape
too, if you provide the 2nd argument, but it's more work. \$\endgroup\$Flambino– Flambino2014年04月17日 14:02:06 +00:00Commented Apr 17, 2014 at 14:02
Once you start storing some strings in percent-encoded form, you have the added burden of keeping track of which strings are encoded, and which strings aren't.1 Therefore, you should avoid ad hoc escaping.
Instead, I recommend keeping all strings in unescaped form, and performing the escaping at the last minute when constructing the URL. That also forces you to use a URL-construction procedure that handles all escaping for you — the way it should be done. For example,
import urllib
start = (Time.now.utc + 3600).strftime(...)
end = (Time.now.utc + 7200).strftime(...)
query_string = urllib.urlencode({ 'start': start, 'end', end })
1 One way to keep track of which variables contain encoded strings is to use some form of Hungarian Notation (see the section called The Real Solution).