我正在使用一个 JSON API,它以以下格式将日期时间值作为字符串返回:
/日期(1370651105153)/
如何将这样的值解析为 Rails 中的日期时间变量?
我正在使用一个 JSON API,它以以下格式将日期时间值作为字符串返回:
/日期(1370651105153)/
如何将这样的值解析为 Rails 中的日期时间变量?
这似乎是一个 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
预计它的参数是自纪元以来的秒数而不是毫秒数。
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
该整数似乎是一个 unixtime(以毫秒为单位)。只需剪掉最后三位数字并将其余数字输入Time.at
:
Time.at(1370651105) # => 2013-06-08 04:25:05 +0400
对于需要包含时区的更强大解决方案的任何人,这是我想出的解析器:
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
#!/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 中被告知此功能。