Skip to content

Ruby 日期与时间

Ruby 通过其核心的 Time 类和标准库中的 Date 和 DateTime 类(位于 date 库中,可能需要 require)提供了强大的日期和时间处理支持。Time 类表示一个特定的时刻,包括日期和时间两个组件,在大多数现代系统上精确到微秒。

在现代 64 位系统上,Ruby 的 Time 对象可以表示非常大的日期范围,远远超过了影响了一些旧的 32 位系统的 1970-2038 限制。

您可以使用 Time.now 或其别名 Time.new(在不带参数调用时)获取当前系统日期和时间。

#!/usr/bin/env ruby
time_now = Time.now
puts "当前时间 (Time.now): #{time_now.inspect}"
# Time.new 在不带参数调用时是 Time.now 的别名
time_new = Time.new
puts "当前时间 (Time.new): #{time_new.inspect}"

这将产生类似以下的输出(具体的时间和时区会有所不同):

Current Time (Time.now): 2023-10-27 10:30:45.123456 -0700
Current Time (Time.new): 2023-10-27 10:30:45.123456 -0700

一个 Time 对象提供方法来访问其各种组成部分:

#!/usr/bin/env ruby
time = Time.now
puts "当前时间: #{time.inspect}"
puts "年份: #{time.year}" # 例如 2023
puts "月份: #{time.month}" # 1 (一月) 到 12 (十二月)
puts "日期: #{time.day}" # 1 到 31
puts "小时: #{time.hour}" # 0 到 23 (24 小时制)
puts "分钟: #{time.min}" # 0 到 59
puts "秒钟: #{time.sec}" # 0 到 59 (闰秒时可为 60)
puts "微秒: #{time.usec}" # 0 到 999999
puts "纳秒: #{time.nsec}" # 0 到 999999999 (比微秒更精确)
puts "一周中的第几天 (wday): #{time.wday}" # 0 (星期日) 到 6 (星期六)
puts "一年中的第几天 (yday): #{time.yday}" # 1 到 365 (闰年为 366)
puts "时区: #{time.zone}" # 例如 "PST", "UTC", "EDT" (可能为 nil)
puts "是夏令时吗?: #{time.dst?}" # 如果夏令时生效则为 true

您可以使用以下方法为特定日期和时间创建 Time 对象:

  • Time.local(year, month, day, hour, min, sec, usec_with_frac) 或 Time.mktime(…): 在系统的本地时区创建一个 Time 对象。
  • Time.utc(year, month, day, hour, min, sec, usec_with_frac) 或 Time.gm(…): 在协调世界时 (UTC),也称为格林威治标准时间 (GMT) 中创建一个 Time 对象。
#!/usr/bin/env ruby
# 2025 年 12 月 25 日 14:30:00 本地时间
local_time = Time.local(2025, 12, 25, 14, 30, 0)
puts "本地时间: #{local_time.inspect}"
# 2024 年 7 月 4 日 09:15:00 UTC
utc_time = Time.utc(2024, 7, 4, 9, 15, 0)
puts "UTC 时间: #{utc_time.inspect}"
# 从右到左可以省略部分;它们默认为最小值 (例如月/日为 1,时间组件为 0)
start_of_year = Time.local(2023) # 2023 年 1 月 1 日 00:00:00 本地时间
puts "2023 年初: #{start_of_year.inspect}"

Time 对象可以转换为各种其他表示形式:

  • time.to_a: 返回一个 10 元素的数组:[秒, 分, 时, 日, 月, 年, 一周中的第几天, 一年中的第几天, 是否夏令时, 时区缩写]。
  • time.to_i 或 time.tv_sec: 返回自 Unix 纪元 (1970 年 1 月 1 日 00:00:00 UTC) 以来的秒数。
  • time.to_f 或 time.tv_usec 与 tv_sec 结合使用: 返回自纪元以来的秒数 (浮点数),包括微秒。
  • Time.at(seconds_since_epoch): 从自纪元以来的秒数创建一个 Time 对象。
#!/usr/bin/env ruby
time = Time.now
components_array = time.to_a
puts "时间作为数组: #{components_array.inspect}"
# 示例:[15, 30, 10, 27, 10, 2023, 5, 300, true, "PDT"]
seconds_epoch = time.to_i
puts "自纪元以来的秒数: #{seconds_epoch}"
reconstructed_time = Time.at(seconds_epoch)
puts "从秒数重构的时间: #{reconstructed_time.inspect}"

Ruby 的 Time 对象是时区感知的。您可以在时区之间进行转换:

#!/usr/bin/env ruby
time = Time.now # 在本地时区创建
puts "原始 (本地): #{time.inspect} (时区: #{time.zone}, 夏令时: #{time.dst?})"
# 转换为 UTC
utc_time = time.utc
puts "UTC: #{utc_time.inspect} (时区: #{utc_time.zone}, 夏令时: #{utc_time.dst?})"
# 将 UTC 时间转换回本地时区
local_again = utc_time.localtime
puts "再次转换为本地时间: #{local_again.inspect} (时区: #{local_again.zone}, 夏令时: #{local_again.dst?})"
# 获取一个新的 UTC 或本地时区 Time 对象,而不修改原始对象
new_utc_obj = time.getutc
new_local_obj = utc_time.getlocal
puts "原始时间是 UTC 吗? #{time.utc?}" # false (如果本地时间不是 UTC)
puts "utc_time 对象是 UTC 吗? #{utc_time.utc?}" # true
puts "UTC 偏移量: #{time.utc_offset} 秒" # 例如 PDT 的 -25200 (-7 小时)

strftime (字符串格式化时间) 方法是一个强大的工具,用于将 Time 对象自定义格式化为字符串。它使用指令(以百分号开头的字符)来指定时间的每个部分应如何表示。

#!/usr/bin/env ruby
time = Time.now
puts "默认 to_s: #{time.to_s}" # 例如 2023-10-27 10:30:45 -0700
puts "ctime: #{time.ctime}" # 例如 Fri Oct 27 10:30:45 2023
puts "ISO 8601: #{time.iso8601}" # 例如 2023-10-27T10:30:45-07:00
puts "自定义格式 (YYYY-MM-DD HH:MM:SS): #{time.strftime("%Y-%m-%d %H:%M:%S")}"
puts "自定义格式 (月 日, 年): #{time.strftime("%B %d, %Y")}"
puts "自定义格式 (hh:mm AM/PM 时区): #{time.strftime("%I:%M %p %Z")}"
指令描述
%Y带世纪的年份 (例如 2023)
%y不带世纪的年份 (00-99)
%m年份中的月份 (01-12)
%B月份的全名 (例如 January)
%b 或 %h月份的缩写名 (例如 Jan)
%d月份中的日期 (01-31)
%e月份中的日期 (前面有空格填充) ( 1-31)
%A一周中的全名 (例如 Sunday)
%a一周中的缩写名 (例如 Sun)
%w一周中的第几天 (星期日为 0, 0-6)
%H一天中的小时 (24 小时制) (00-23)
%I一天中的小时 (12 小时制) (01-12)
%M小时中的分钟 (00-59)
%S分钟中的秒数 (00-60) (闰秒为 60)
%L秒中的毫秒数 (000-999)
%N秒中的纳秒数 (000000000-999999999)
%p上午/下午指示符 (AM 或 PM)
%Z时区缩写名称
%z与 UTC 的时区偏移量 (例如 +0900, -0700)
%:z带冒号的时区偏移量 (例如 +09:00)
%c特定于区域设置的日期和时间 (例如 Fri Oct 27 10:30:45 2023)
%x特定于区域设置的日期表示 (例如 10/27/23)
%X特定于区域设置的时间表示 (例如 10:30:45)
%%字面上的 ’%’ 字符

您可以对 Time 对象执行算术运算。添加或减去一个数值时,该数字被视为秒数。

#!/usr/bin/env ruby
now = Time.now
puts "当前时间: #{now}"
one_hour_ago = now - 3600 # 3600 秒 = 1 小时
puts "一小时前: #{one_hour_ago}"
two_days_from_now = now + (2 * 24 * 60 * 60) # 添加 2 天对应的秒数
puts "两天后: #{two_days_from_now}"
difference = two_days_from_now - now # 两个 Time 对象之间的差以秒为单位 (浮点数)
puts "差异 (秒): #{difference}"
puts "差异 (天): #{difference / (24 * 60 * 60)}"

输出 (示例):

Current time: 2023-10-27 10:45:00 -0700
One hour ago: 2023-10-27 09:45:00 -0700
Two days from now: 2023-10-29 10:45:00 -0700
Difference in seconds: 172800.0
Difference in days: 2.0

使用 Date 和 DateTime 进行特定日期操作

Section titled “使用 Date 和 DateTime 进行特定日期操作”

对于主要关注日期的操作(不带时间组件)或需要历史日期计算(例如儒略历/格里高利历改革)的场景,标准库 date 中的 Date 和 DateTime 类通常更适合。您需要 require ‘date’ 才能使用它们。

#!/usr/bin/env ruby
require 'date'
today = Date.today
puts "今天 (Date): #{today.inspect}" # 例如 #<Date: 2023-10-27 ((2460245j,0s,0n),+0s,2299161j)>
puts "格式化的日期: #{today.strftime('%A, %B %d, %Y')}"
yesterday = today - 1
puts "昨天: #{yesterday}"
next_month = today >> 1 # 增加一个月
puts "下个月: #{next_month}"
# DateTime 包括时间组件
dt_now = DateTime.now
puts "当前 DateTime: #{dt_now.inspect}"

详细信息请参考 Time、Date 和 DateTime 类的 Ruby 官方文档。