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?

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Apr 17, 2014 at 11:01
\$\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

answered Apr 17, 2014 at 11:16
\$\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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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).

answered Apr 17, 2014 at 23:04
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.