1

I have a string timestamp:

ts = '1550989255.000300'

How can I parse ts in a Time or Datetime object as t, and have t.strftime('%6N') print correctly?

I did:

t = Time.at(ts.to_f)
t.strftime '%6N' #=> "000299"

Ruby chops off one microsecond when converting a timestamp to a string.

sawa
  • 165,429
  • 45
  • 277
  • 381
GeekJock
  • 11,066
  • 13
  • 43
  • 44
  • Just a wild guess, but might this be caused by the inaccuracies of floats? In any case, what is your question? So far, you’ve only described the issue, but not asked an actual question. – jdno Feb 24 '19 at 07:45
  • @jdno I just updated my question. It is a Ruby issue, not an ActiveRecord issue. – GeekJock Feb 24 '19 at 07:47
  • Possible duplicate of [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Yossi Feb 24 '19 at 07:49
  • @Yossi I think you're right. Looking at that now – GeekJock Feb 24 '19 at 07:50
  • Note: `ts = 1.003; t = Time.at(ts); t.strftime '%6N' #=> "002999"`. – Cary Swoveland Feb 24 '19 at 07:59

3 Answers3

4

You are observing floating point math inaccuracy. If you need higher precision, you should use BigDecimal.

require 'bigdecimal'

ts = BigDecimal('1550989255.000300')
t  = Time.at(ts)
t.strftime '%6N'

=> "000300"
Yossi
  • 11,778
  • 2
  • 53
  • 66
2

Rationals are more exact than Floats, and they are used more and more in Ruby.

ts = '1550989255.000300'
t  = Time.at(ts.to_r)  # to_r: a rational
p t.strftime '%6N'     # => "000300"
steenslag
  • 79,051
  • 16
  • 138
  • 171
1

This works:

ts = "1550989255.000300"
arr = ts.split('.')
t = Time.at(arr[0].to_i, arr[1].to_i)
t.strftime '%6N'
GeekJock
  • 11,066
  • 13
  • 43
  • 44
  • 1
    I am removing it – I think him and I typed it at the same time, I hadn't seen his when I typed mine. – GeekJock Feb 24 '19 at 08:03
  • We did type it simultaniously – Yossi Feb 24 '19 at 08:06
  • I prefer this to using `BigDecimal` for three reasons: it employs the appropriate version of `Time::at` directly (no trickery), avoids the need to load the `BigDecimal` libary and the use of a `BigDecimal` object as the argument of `Time::at` is undocumented. – Cary Swoveland Feb 24 '19 at 19:20
  • To reduce repetition and avoid the creation of the variable `arr` you may wish to write `Time.at(*ts.split('.').map(&:to_i)) #=> 2019-02-24 06:20:55 +0000`. – Cary Swoveland Feb 24 '19 at 19:28