5
\$\begingroup\$

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?

\$\endgroup\$

2 Answers 2

7
\$\begingroup\$

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

\$\endgroup\$
4
  • \$\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\$ 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 as CGI.escape(string) \$\endgroup\$
    – Flambino
    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\$ 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 use URI.escape too, if you provide the 2nd argument, but it's more work. \$\endgroup\$
    – Flambino
    Apr 17, 2014 at 14:02
2
\$\begingroup\$

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).

\$\endgroup\$
1
  • \$\begingroup\$ +1 for advocating "just in time"-encoding \$\endgroup\$
    – Flambino
    Apr 18, 2014 at 10:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.