3

我正在使用一个 JSON API,它以以下格式将日期时间值作为字符串返回:

/日期(1370651105153)/

如何将这样的值解析为 Rails 中的日期时间变量?

4

5 回答 5

6

这似乎是一个 UNIX 时间戳(自纪元以来的秒数)。此外,它似乎是自纪元以来的毫秒数。

所以你可以像这样转换它 - 假设这value是一个看起来像这样的字符串:

value = "/Date(1370651105153)/"

if value =~ /\/Date\((\d+)\)\//
  timestamp = $1.to_i
  time = Time.at(timestamp / 1000)
  # time is now a Time object
end

您需要除以 1000,因为Time#at预计它的参数是自纪元以来的秒数而不是毫秒数。

于 2013-06-08T00:38:59.717 回答
4

andyisnowskynet 的回答中有一些错误(https://stackoverflow.com/a/25817039/5633460):有一个小的 RegEx 错误,这意味着它不会检测到正时区;并且它不支持与完整时间不一致的时区,例如-0430( VET )。此外,Time它返回的对象可能会尊重正确的 Ruby 时区处理,而不是添加时间偏移量,这将是更传统的方法。

这是我对该答案的改进版本...

首先,对于以这种格式表示的日期时间值,时间序列部分(例如1370651105153在 OP 的示例中)始终是自 以来的毫秒数1970-01-01 00:00:00 GMT,并且不受时区后缀是否存在的影响。因此,作为字符串一部分包含的任何时区都不会改变它所代表的历史点。它仅用于说明“观察者”当时所在的时区。

RubyTime对象能够处理这两条信息(即实际的日期时间“值”和时区“元数据”)。展示:

a = Time.at(-1).utc
# => 1969-12-31 23:59:59 UTC
b = Time.at(-1).getlocal('+09:30')
# => 1970-01-01 09:29:59 +0930
a == b
# => true

如您所见,即使两者看起来像不同的值(由于它们表达它们的时区不同),==相等运算符显示它们实际上引用了同一时刻

知道了这一点,我们可以改进parse_json_datetime如下(也纠正其他错误):

def parse_json_datetime(datetime)
  # "/Date(-62135575200000-0600)/" seems to be the default date returned
  # if the value is null:
  if datetime == "/Date(-62135575200000-0600)/"
    # Return nil because it is probably a null date that is defaulting to 0.
    # To be more technically correct you could just return 0 here if you wanted:
    return nil
  elsif datetime =~ %r{/Date\(([-+]?\d+)([-+]\d+)?\)/}
    # We've now parsed the string into:
    # - $1: Number of milliseconds since the 1/1/1970 epoch.
    # - $2: [Optional] timezone offset.
    # Divide $1 by 1000 because it is in milliseconds and Time uses seconds:
    seconds_since_epoch = $1.to_i / 1000.0
    time = Time.at(seconds_since_epoch.to_i).utc 
    # We now have the exact moment in history that this represents,
    # stored as a UTC-based "Time" object.

    if $2
      # We have a timezone, so convert its format (adding a colon)...
      timezone = $2.gsub(/(...)(..)/, '\1:\2')
      # ...then apply it to the Time object:
      time = time.getlocal(timezone)
    end
    time
  else
    raise "Unrecognized date format."
  end
end

然后我们可以测试:

# See: http://momentjs.com/docs/#/parsing/asp-net-json-date/
parse_json_datetime("/Date(1198908717056-0700)/")
# => 2007-12-28 23:11:57 -0700

# 1 minute before the epoch, but in ACST:
parse_json_datetime("/Date(-60000+0930)/")
# => 1970-01-01 09:29:00 +0930

# Same as above, but converted naturally to its UTC equivalent:
parse_json_datetime("/Date(-60000+0930)/").utc
# => 1969-12-31 23:59:00 UTC

# Same as above, with timezone unspecified (implying UTC):
parse_json_datetime("/Date(-60000)/")
# => 1969-12-31 23:59:00 UTC

# OP's example:
parse_json_datetime("/Date(1370651105153)/")
# => 2013-06-08 00:25:05 UTC

# Same as above, but stated in two different timezones:
aaa = parse_json_datetime("/Date(1370651105153-0200)/")
# => 2013-06-07 22:25:05 -0200
bbb = parse_json_datetime("/Date(1370651105153-0800)/")
# => 2013-06-07 16:25:05 -0800

# As "rendered" strings they're not the same:
aaa.to_s == bbb.to_s
# => false
# But as moments in time, they are equivalent:
aaa == bbb
# => true
# And, for the sake of the argument, if they're both expressed in the
# same timezone (arbitrary '-04:00' in this case) then they also render
# as equal strings:
aaa.getlocal('-04:00').to_s == bbb.getlocal('-04:00').to_s
于 2015-12-03T06:25:31.507 回答
1

该整数似乎是一个 unixtime(以毫秒为单位)。只需剪掉最后三位数字并将其余数字输入Time.at

Time.at(1370651105) # => 2013-06-08 04:25:05 +0400
于 2013-06-08T00:38:54.693 回答
1

对于需要包含时区的更强大解决方案的任何人,这是我想出的解析器:

def parse_json_datetime(datetime)
  # "/Date(-62135575200000-0600)/" seems to be the default date returned if the value is null
  if datetime == "/Date(-62135575200000-0600)/"
    # return nil because it is probably a null date that is defaulting to 0.
    # to be more technically correct you could just return 0 here if you wanted.
    return nil
  elsif datetime =~ /\/Date\(([-+]?\d+)(-+\d+)?\)\// # parse the seconds and timezone (if present)
    milliseconds_since_epoch = $1
    time = Time.at(milliseconds_since_epoch.to_i/1000.0).utc # divide by 1000 because it is in milliseconds and Time uses seconds

    if timezone_hourly_offset = $2
      timezone_hourly_offset = timezone_hourly_offset.gsub("0","").to_i
      time = time+(timezone_hourly_offset*60*60) # convert hourly timezone offset into seconds and add onto time
    end
    time
  else
    raise "Unrecognized date format."
  end
end
于 2014-09-12T21:21:33.380 回答
0

主动支持使执行此操作变得容易

#!/usr/bin/env ruby

require 'active_support/json'
require 'active_support/time'

Time.zone = 'Eastern Time (US & Canada)'
ActiveSupport.parse_json_times = true

puts ActiveSupport::JSON.decode('{"hi":"2009-08-10T19:01:02Z"}')

这将输出:

{"hi"=>Mon, 10 Aug 2009 15:01:02 EDT -04:00}

一旦你打开这个ActiveSupport.parse_json_times标志,那么你解码的任何 JSON 有效负载都会在任何时候自动转换为ActiveSupport::TimeWithZone对象。

Active Support使用正则表达式来检测时间。您可以阅读测试以更好地了解它支持的时间格式。看起来它支持 ISO 8601,但不支持Unix/POSIX/Epoch 时间戳。

当我针对 Rails 创建此问题时,我在 Active Support 中被告知此功能。

于 2018-04-02T18:00:44.667 回答