Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a time-based string to ISO format

I have an Song object that has a duration attribute (string based) that goes by minutes and seconds. I want to output this as a datetime attribute for a <time/> tag (via Rails) but needed to convert it to ISO 8601 format in order for it to be semantically valid.

So, I have the duration, "2:30". The characters 'PT' would need to be prepended while removing the colon in between the two numbers and finally appending the letter 'S' at the end:

PT2M30S

Final output:

<time itemprop="duration" datetime="PT2M30S">2:30</time>

How would I go about in accomplishing this?

Is there a Ruby method that accomplishes this already? Does a regular expression have to be written out in order to find/replace the aforementioned characters?

like image 488
Carl Edwards Avatar asked Apr 18 '26 18:04

Carl Edwards


2 Answers

It doesn't take a regex or anything fancy to do this. I'd simply split on the colon, and pass the result into a format string:

'PT%sM%sS' % "2:30".split(':') # => "PT2M30S"

How does the modulo operator work in this case? Never seen something done like this before.

It's not the "modulo operator" unless it's being applied to a number. It's a String method named % that is an equivalent to format or sprintf. See String.% followed by Kernel::sprintf for more information, but basically:

  s   | Argument is a string to be substituted.  If the format
      | sequence contains a precision, at most that many characters
      | will be copied.

So:

'%s'   % 'foo' # => "foo"
'%5s'  % 'foo' # => "  foo"
'%-5s' % 'foo' # => "foo  "
'%1s'  % 'foo' # => "foo"
'%.1s' % 'foo' # => "f"

'%s = %s' % ['a', 1] # => "a = 1"

In particular, note that in the last one 1 is converted to "1" when inserted into the string. This can be useful but it isn't the only way, (or even a preferable way) to convert from an object to a string.


Finally, don't assume regular expressions are the way to go for every problem requiring extracting information from a string. If you can do it without resorting to weird work-arounds, use the built-in methods, like split:

require 'fruity'

compare do
  _split { 'PT%sM%sS' % "2:30".split(':') }
  _regex { "2:30".sub(/(.*):(.*)/, "PT\\1M\\2S") }
end

Which results in:

# >> Running each test 2048 times. Test will take about 1 second.
# >> _split is faster than _regex by 19.999999999999996% ± 10.0%

Regular expressions are flexible but that usually comes at the price of being slower.

Also, we see a lot of misuse of gsub when sub should be used. sub does a lot less work than gsub; Where sub fires once, gsub will continue to loop until it hits the end of the string. That is a measurable difference, even on a short string such as "2:30":

compare do
  _sub { "2:30".sub(/(.*):(.*)/, "PT\\1M\\2S") }
  _gsub { "2:30".gsub(/(.*):(.*)/, "PT\\1M\\2S") }
end

# >> Running each test 2048 times. Test will take about 1 second.
# >> _sub is faster than _gsub by 2x ± 0.1
like image 80
the Tin Man Avatar answered Apr 20 '26 09:04

the Tin Man


If you want to do it in elegant way try ruby-duration gem

Example code

Duration.new(:weeks => 1, :days => 20).iso8601 => "P3W6DT0H0M0S"
like image 33
Zahid Avatar answered Apr 20 '26 09:04

Zahid



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!