Ruby 日期 & 时间
在编程中,正确处理日期和时间是非常重要的。Ruby提供了强大的日期和时间处理功能,包括内置的[Date](file:///D:/Workspace/Coding/VueProjects/tutorials-web/docs/ruby/../../../../../../Ruby30-x64/lib/ruby/3.0.0/date.rb#L75-L2470)、[Time](file:///D:/Workspace/Coding/VueProjects/tutorials-web/docs/ruby/../../../../../../Ruby30-x64/lib/ruby/3.0.0/time.rb#L1-L466)和[DateTime](file:///D:/Workspace/Coding/VueProjects/tutorials-web/docs/ruby/../../../../../../Ruby30-x64/lib/ruby/3.0.0/date.rb#L2472-L3114)类,以及更现代的[Time](file:///D:/Workspace/Coding/VueProjects/tutorials-web/docs/ruby/../../../../../../Ruby30-x64/lib/ruby/3.0.0/time.rb#L1-L466)类扩展。本章将详细介绍Ruby中日期和时间的创建、操作和处理方法。
🎯 日期和时间基础
引入必要的库
ruby
# Date和DateTime类需要显式引入
require 'date'
# Time类是内置的,但可以引入扩展功能
require 'time'当前日期和时间
ruby
# 获取当前时间
current_time = Time.now
puts current_time # 2023-12-25 14:30:45 +0800
# 获取当前日期
current_date = Date.today
puts current_date # 2023-12-25
# 获取当前日期时间
current_datetime = DateTime.now
puts current_datetime # 2023-12-25T14:30:45+08:00
# 使用Time获取日期部分
time_date = Time.now.to_date
puts time_date # 2023-12-25📅 Date类
创建Date对象
ruby
require 'date'
# 创建特定日期
date1 = Date.new(2023, 12, 25)
puts date1 # 2023-12-25
# 解析字符串创建日期
date2 = Date.parse("2023-12-25")
puts date2 # 2023-12-25
date3 = Date.parse("December 25, 2023")
puts date3 # 2023-12-25
# 从纪元日创建(从公元1年1月1日开始的天数)
date4 = Date.jd(2451545) # 2000-01-01
puts date4 # 2000-01-01
# 从商业日期创建(年,周,星期)
date5 = Date.commercial(2023, 52, 1) # 2023年第52周的星期一
puts date5 # 2023-12-25
# 从天文儒略日创建
date6 = Date.ordinal(2023, 359) # 2023年第359天
puts date6 # 2023-12-25Date属性和方法
ruby
date = Date.new(2023, 12, 25)
# 基本属性
puts date.year # 2023
puts date.month # 12
puts date.day # 25
puts date.wday # 1 (星期一,0表示星期日)
puts date.yday # 359 (一年中的第几天)
# 星期信息
puts date.monday? # true
puts date.sunday? # false
puts date.strftime("%A") # Monday
# 月份和季节
puts date.strftime("%B") # December
puts date.mday # 25 (月份中的天数)
# 闰年判断
puts date.leap? # false (2023年不是闰年)
# 日期比较
date1 = Date.new(2023, 12, 25)
date2 = Date.new(2023, 12, 26)
puts date1 < date2 # true
puts date1 == date2 # false
puts date1 > date2 # false
# 日期运算
future_date = date + 10
puts future_date # 2024-01-04
past_date = date - 5
puts past_date # 2023-12-20
# 计算两个日期之间的天数
days_difference = (date2 - date1).to_i
puts days_difference # 1🕐 Time类
创建Time对象
ruby
# 当前时间
current_time = Time.now
puts current_time # 2023-12-25 14:30:45 +0800
# 创建特定时间
time1 = Time.new(2023, 12, 25, 14, 30, 45)
puts time1 # 2023-12-25 14:30:45 +0800
# 指定时区
time2 = Time.new(2023, 12, 25, 14, 30, 45, "+09:00")
puts time2 # 2023-12-25 14:30:45 +0900
# 解析字符串创建时间
time3 = Time.parse("2023-12-25 14:30:45")
puts time3 # 2023-12-25 14:30:45 +0800
time4 = Time.parse("December 25, 2023 2:30 PM")
puts time4 # 2023-12-25 14:30:00 +0800
# 从纪元时间创建
epoch_time = Time.at(1703485845)
puts epoch_time # 2023-12-25 14:30:45 +0800
# 本地时间和UTC时间
local_time = Time.local(2023, 12, 25, 14, 30, 45)
puts local_time # 2023-12-25 14:30:45 +0800
utc_time = Time.utc(2023, 12, 25, 14, 30, 45)
puts utc_time # 2023-12-25 14:30:45 UTCTime属性和方法
ruby
time = Time.new(2023, 12, 25, 14, 30, 45, "+08:00")
# 基本属性
puts time.year # 2023
puts time.month # 12
puts time.day # 25
puts time.hour # 14
puts time.min # 30
puts time.sec # 45
# 星期信息
puts time.wday # 1 (星期一)
puts time.yday # 359 (一年中的第几天)
# 时间戳
puts time.to_i # 1703485845 (秒级时间戳)
puts time.to_f # 1703485845.0 (浮点数时间戳)
# 时区信息
puts time.zone # +08:00
puts time.utc? # false
puts time.utc_offset # 28800 (秒数,+08:00 = 8 * 3600)
# 时间比较
time1 = Time.new(2023, 12, 25, 14, 30, 45)
time2 = Time.new(2023, 12, 25, 15, 0, 0)
puts time1 < time2 # true
puts time1 == time2 # false
# 时间运算
future_time = time + 3600 # 加1小时
puts future_time # 2023-12-25 15:30:45 +0800
past_time = time - 1800 # 减30分钟
puts past_time # 2023-12-25 14:00:45 +0800
# 计算时间差
seconds_difference = time2 - time1
puts seconds_difference # 1755.0 (秒数)
puts seconds_difference / 60 # 29.25 (分钟数)📆 DateTime类
创建DateTime对象
ruby
require 'date'
# 创建特定日期时间
datetime1 = DateTime.new(2023, 12, 25, 14, 30, 45)
puts datetime1 # 2023-12-25T14:30:45+00:00
# 指定时区
datetime2 = DateTime.new(2023, 12, 25, 14, 30, 45, "+08:00")
puts datetime2 # 2023-12-25T14:30:45+08:00
# 解析字符串创建日期时间
datetime3 = DateTime.parse("2023-12-25 14:30:45")
puts datetime3 # 2023-12-25T14:30:45+00:00
# 从纪元日创建
datetime4 = DateTime.jd(2451545.5)
puts datetime4 # 2000-01-01T12:00:00+00:00
# ISO 8601格式
datetime5 = DateTime.iso8601("2023-12-25T14:30:45+08:00")
puts datetime5 # 2023-12-25T14:30:45+08:00DateTime属性和方法
ruby
datetime = DateTime.new(2023, 12, 25, 14, 30, 45, "+08:00")
# 基本属性
puts datetime.year # 2023
puts datetime.month # 12
puts datetime.day # 25
puts datetime.hour # 14
puts datetime.min # 30
puts datetime.sec # 45
# 精确的小数秒
puts datetime.sec_fraction # (0/1) (秒的小数部分)
# 时区信息
puts datetime.zone # +08:00
puts datetime.offset # (1/3) (时区偏移量,+08:00 = 8/24 = 1/3)
# 星期信息
puts datetime.wday # 1 (星期一)
puts datetime.yday # 359 (一年中的第几天)
# 日期时间比较
dt1 = DateTime.new(2023, 12, 25, 14, 30, 45)
dt2 = DateTime.new(2023, 12, 25, 15, 0, 0)
puts dt1 < dt2 # true
puts dt1 == dt2 # false
# 日期时间运算
future_dt = datetime + Rational(1, 24) # 加1小时 (1/24天)
puts future_dt # 2023-12-25T15:30:45+08:00
past_dt = datetime - Rational(30, 1440) # 减30分钟 (30/1440天)
puts past_dt # 2023-12-25T14:00:45+08:00🔄 日期时间格式化
strftime方法
ruby
time = Time.new(2023, 12, 25, 14, 30, 45, "+08:00")
# 常用格式
puts time.strftime("%Y-%m-%d") # 2023-12-25
puts time.strftime("%H:%M:%S") # 14:30:45
puts time.strftime("%Y-%m-%d %H:%M:%S") # 2023-12-25 14:30:45
# 详细格式
puts time.strftime("%A, %B %d, %Y") # Monday, December 25, 2023
puts time.strftime("%a, %b %d, %Y") # Mon, Dec 25, 2023
# 中文格式
puts time.strftime("%Y年%m月%d日 %H时%M分%S秒") # 2023年12月25日 14时30分45秒
# 12小时制
puts time.strftime("%I:%M:%S %p") # 02:30:45 PM
# 带时区信息
puts time.strftime("%Y-%m-%d %H:%M:%S %z") # 2023-12-25 14:30:45 +0800
puts time.strftime("%Y-%m-%d %H:%M:%S %Z") # 2023-12-25 14:30:45 +08:00
# 星期和周信息
puts time.strftime("%A is day %w of the week") # Monday is day 1 of the week
puts time.strftime("Day %j of the year") # Day 359 of the year
# ISO 8601格式
puts time.strftime("%Y-%m-%dT%H:%M:%S%:z") # 2023-12-25T14:30:45+08:00解析日期时间字符串
ruby
require 'time'
# 解析各种格式的日期时间字符串
time_strings = [
"2023-12-25 14:30:45",
"December 25, 2023 2:30 PM",
"2023/12/25 14:30:45",
"Mon, 25 Dec 2023 14:30:45 +0800",
"2023-12-25T14:30:45+08:00"
]
time_strings.each do |str|
begin
parsed_time = Time.parse(str)
puts "#{str} => #{parsed_time}"
rescue ArgumentError => e
puts "无法解析: #{str} (#{e.message})"
end
end
# ISO 8601格式解析
iso_string = "2023-12-25T14:30:45+08:00"
iso_time = Time.iso8601(iso_string)
puts iso_time # 2023-12-25 14:30:45 +0800
# RFC 2822格式解析
rfc_string = "Mon, 25 Dec 2023 14:30:45 +0800"
rfc_time = Time.rfc2822(rfc_string)
puts rfc_time # 2023-12-25 14:30:45 +0800⏱️ 时间计算和操作
时间运算
ruby
# 时间加减运算
time = Time.now
# 加减秒数
future_time = time + 60 # 1分钟后
past_time = time - 300 # 5分钟前
# 加减天数
next_day = time + 86400 # 1天后 (24 * 60 * 60)
last_week = time - 604800 # 1周前 (7 * 24 * 60 * 60)
# 使用Rational进行精确计算
exact_time = time + Rational(1, 2) # 0.5天 = 12小时后
# 计算时间差
time1 = Time.new(2023, 12, 25, 14, 30, 0)
time2 = Time.new(2023, 12, 25, 16, 45, 30)
difference = time2 - time1
puts "相差 #{difference} 秒" # 相差 8130.0 秒
# 转换为更友好的格式
minutes = difference / 60
hours = minutes / 60
puts "相差 #{hours} 小时" # 相差 2.2583333333333333 小时时间范围和迭代
ruby
# 创建时间范围
start_time = Time.new(2023, 12, 25, 9, 0, 0)
end_time = Time.new(2023, 12, 25, 17, 0, 0)
time_range = start_time..end_time
# 检查时间是否在范围内
check_time = Time.new(2023, 12, 25, 12, 0, 0)
puts time_range.include?(check_time) # true
# 迭代时间范围(按小时)
current = start_time
while current <= end_time
puts current.strftime("%H:%M")
current += 3600 # 加1小时
end
# 使用step方法
start_time.step(end_time, 3600) do |t|
puts t.strftime("%H:%M")
end工作日计算
ruby
class BusinessDays
def self.add_business_days(start_date, days)
current_date = start_date
days_added = 0
while days_added < days
current_date += 1
next if current_date.saturday? || current_date.sunday?
days_added += 1
end
current_date
end
def self.business_days_between(start_date, end_date)
return 0 if start_date > end_date
business_days = 0
current_date = start_date
while current_date <= end_date
unless current_date.saturday? || current_date.sunday?
business_days += 1
end
current_date += 1
end
business_days
end
end
# 使用工作日计算
start_date = Date.new(2023, 12, 25) # 星期一
end_date = Date.new(2023, 12, 31) # 星期日
# 计算5个工作日后的日期
future_date = BusinessDays.add_business_days(start_date, 5)
puts "5个工作日后: #{future_date}" # 2023-12-39 => 2024-01-02
# 计算两个日期之间的工作日数
business_days = BusinessDays.business_days_between(start_date, end_date)
puts "工作日数: #{business_days}" # 工作日数: 5🌍 时区处理
时区转换
ruby
# 创建不同时区的时间
utc_time = Time.utc(2023, 12, 25, 14, 30, 45)
puts "UTC时间: #{utc_time}" # UTC时间: 2023-12-25 14:30:45 UTC
# 转换为本地时间
local_time = utc_time.getlocal
puts "本地时间: #{local_time}" # 本地时间: 2023-12-25 22:30:45 +0800
# 转换为指定时区
# 注意:Ruby标准库不直接支持时区转换,需要使用tzinfo等gem
# 这里演示概念:
class TimeZoneConverter
# 简单的时区偏移转换
def self.convert_timezone(time, offset_hours)
# offset_hours: 正数表示东时区,负数表示西时区
time + (offset_hours * 3600)
end
end
# 从UTC转换到不同时区
beijing_time = TimeZoneConverter.convert_timezone(utc_time, 8)
puts "北京时间: #{beijing_time}" # 北京时间: 2023-12-25 22:30:45 +0800
tokyo_time = TimeZoneConverter.convert_timezone(utc_time, 9)
puts "东京时间: #{tokyo_time}" # 东京时间: 2023-12-25 23:30:45 +0800
new_york_time = TimeZoneConverter.convert_timezone(utc_time, -5)
puts "纽约时间: #{new_york_time}" # 纽约时间: 2023-12-25 09:30:45 +0800夏令时处理
ruby
# 夏令时相关的计算需要使用专门的库如tzinfo
# 这里展示基本概念:
class DSTHandler
# 简化的夏令时判断(美国规则)
def self.dst?(time)
month = time.month
return false if month < 3 || month > 11
return true if month > 3 && month < 11
# 3月的第二个星期日和11月的第一个星期日
# 这里简化处理
if month == 3
time.day >= 8 && time.wday == 0 # 3月第二个星期日之后
elsif month == 11
time.day < 8 && time.wday == 0 # 11月第一个星期日之前
end
end
# 应用夏令时偏移
def self.apply_dst_offset(time, is_dst)
if is_dst
time + 3600 # 加1小时
else
time
end
end
end
# 使用夏令时处理
time = Time.new(2023, 7, 15, 14, 30, 45, "-05:00") # 美国东部时间
is_dst = DSTHandler.dst?(time)
adjusted_time = DSTHandler.apply_dst_offset(time, is_dst)
puts "夏令时调整后: #{adjusted_time}"🎯 实用示例
日志时间戳生成器
ruby
class TimestampGenerator
# 生成标准日志时间戳
def self.log_timestamp
Time.now.strftime("%Y-%m-%d %H:%M:%S.%3N")
end
# 生成ISO 8601时间戳
def self.iso_timestamp
Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%3NZ")
end
# 生成文件友好时间戳
def self.file_timestamp
Time.now.strftime("%Y%m%d_%H%M%S")
end
# 生成人类可读时间戳
def self.human_timestamp
Time.now.strftime("%B %d, %Y at %I:%M %p")
end
end
# 使用时间戳生成器
puts "日志时间戳: #{TimestampGenerator.log_timestamp}" # 日志时间戳: 2023-12-25 14:30:45.123
puts "ISO时间戳: #{TimestampGenerator.iso_timestamp}" # ISO时间戳: 2023-12-25T06:30:45.123Z
puts "文件时间戳: #{TimestampGenerator.file_timestamp}" # 文件时间戳: 20231225_143045
puts "人类时间戳: #{TimestampGenerator.human_timestamp}" # 人类时间戳: December 25, 2023 at 02:30 PM时间计算器
ruby
class TimeCalculator
# 计算年龄
def self.age(birth_date)
today = Date.today
age = today.year - birth_date.year
age -= 1 if today < birth_date.next_year(age)
age
end
# 计算两个日期之间的完整年数、月数、天数
def self.date_difference(start_date, end_date)
years = end_date.year - start_date.year
months = end_date.month - start_date.month
days = end_date.day - start_date.day
if days < 0
months -= 1
days += Date.new(start_date.year, start_date.month + 1, 0).day
end
if months < 0
years -= 1
months += 12
end
{ years: years, months: months, days: days }
end
# 计算距离未来某个日期的天数
def self.days_until(target_date)
(target_date - Date.today).to_i
end
# 格式化持续时间
def self.format_duration(seconds)
days = (seconds / 86400).to_i
hours = (seconds % 86400 / 3600).to_i
minutes = (seconds % 3600 / 60).to_i
seconds = (seconds % 60).to_i
parts = []
parts << "#{days}天" if days > 0
parts << "#{hours}小时" if hours > 0
parts << "#{minutes}分钟" if minutes > 0
parts << "#{seconds}秒" if seconds > 0
parts.join(" ")
end
end
# 使用时间计算器
birth_date = Date.new(1990, 5, 15)
age = TimeCalculator.age(birth_date)
puts "年龄: #{age}岁" # 年龄: 33岁
start_date = Date.new(2020, 1, 1)
end_date = Date.new(2023, 8, 15)
diff = TimeCalculator.date_difference(start_date, end_date)
puts "时间差: #{diff[:years]}年#{diff[:months]}月#{diff[:days]}天" # 时间差: 3年7月14天
future_date = Date.new(2024, 1, 1)
days_until = TimeCalculator.days_until(future_date)
puts "距离#{future_date}还有#{days_until}天"
duration = 7890 # 秒数
formatted_duration = TimeCalculator.format_duration(duration)
puts "持续时间: #{formatted_duration}" # 持续时间: 2小时11分钟30秒时间验证器
ruby
class TimeValidator
# 验证日期格式
def self.valid_date?(date_string)
Date.parse(date_string)
true
rescue ArgumentError
false
end
# 验证时间格式
def self.valid_time?(time_string)
Time.parse(time_string)
true
rescue ArgumentError
false
end
# 验证日期是否在合理范围内
def self.reasonable_date?(date, min_year = 1900, max_year = 2100)
return false unless date.is_a?(Date)
date.year >= min_year && date.year <= max_year
end
# 验证时间是否在营业时间内
def self.business_hours?(time, start_hour = 9, end_hour = 18)
return false unless time.is_a?(Time)
hour = time.hour
hour >= start_hour && hour < end_hour
end
# 验证日期是否为工作日
def self.weekday?(date)
return false unless date.is_a?(Date)
!(date.saturday? || date.sunday?)
end
end
# 使用时间验证器
puts TimeValidator.valid_date?("2023-12-25") # true
puts TimeValidator.valid_date?("invalid-date") # false
puts TimeValidator.valid_time?("14:30:45") # true
puts TimeValidator.valid_time?("25:00:00") # false
date = Date.new(2023, 12, 25)
puts TimeValidator.reasonable_date?(date) # true
puts TimeValidator.weekday?(date) # true (星期一)
time = Time.new(2023, 12, 25, 14, 30, 45)
puts TimeValidator.business_hours?(time) # true📊 性能优化
高效的时间处理
ruby
# 避免重复创建时间对象
# 低效方式
def inefficient_time_check
1000.times do
if Time.now.hour > 12
# 处理逻辑
end
end
end
# 高效方式
def efficient_time_check
current_time = Time.now # 只创建一次
1000.times do
if current_time.hour > 12
# 处理逻辑
end
end
end
# 缓存时间计算结果
class TimeCache
def initialize
@cache = {}
@last_update = Time.now
end
def current_date
now = Time.now
if now - @last_update > 1 # 超过1秒才更新
@cache[:current_date] = now.to_date
@last_update = now
end
@cache[:current_date]
end
end
# 使用时间缓存
cache = TimeCache.new
puts cache.current_date # 2023-12-25批量时间处理
ruby
# 批量处理时间数据
def process_time_data(time_array)
# 预先计算常用值
base_time = Time.now
one_day = 86400
results = []
time_array.each do |time_str|
begin
time = Time.parse(time_str)
# 执行计算
diff = (base_time - time).abs
days = (diff / one_day).to_i
results << { time: time, days_ago: days }
rescue ArgumentError
results << { time: time_str, error: "Invalid time format" }
end
end
results
end
# 使用批量处理
time_strings = [
"2023-12-20 10:30:00",
"2023-12-22 15:45:30",
"invalid-time",
"2023-12-24 09:15:20"
]
results = process_time_data(time_strings)
results.each { |result| puts result }🎯 最佳实践
1. 选择合适的时间类
ruby
# 只需要日期时使用Date
def calculate_age(birth_date_string)
birth_date = Date.parse(birth_date_string)
today = Date.today
today.year - birth_date.year - ((today.month > birth_date.month ||
(today.month == birth_date.month && today.day >= birth_date.day)) ? 0 : 1)
end
# 需要时间精度时使用Time
def log_user_activity(user_id, action)
timestamp = Time.now
puts "[#{timestamp.strftime('%Y-%m-%d %H:%M:%S')}] User #{user_id}: #{action}"
end
# 需要高精度和时区支持时使用DateTime
def schedule_event(event_name, datetime_string)
event_time = DateTime.parse(datetime_string)
puts "事件 '#{event_name}' 安排在 #{event_time.strftime('%Y-%m-%d %H:%M:%S %z')}"
end2. 时区处理最佳实践
ruby
class TimeZoneBestPractice
# 始终以UTC存储时间
def self.store_as_utc(local_time)
local_time.utc
end
# 显示时转换为本地时区
def self.display_in_timezone(utc_time, timezone_offset = 8)
utc_time.getlocal(timezone_offset * 3600)
end
# 处理用户输入的时间
def self.parse_user_input(time_string, user_timezone = "+08:00")
# 假设用户输入的时间是其本地时间
time = Time.parse(time_string)
# 转换为UTC存储
time.utc
end
end
# 使用时区最佳实践
user_input = "2023-12-25 14:30:00"
utc_time = TimeZoneBestPractice.parse_user_input(user_input)
puts "存储的UTC时间: #{utc_time}"
display_time = TimeZoneBestPractice.display_in_timezone(utc_time)
puts "显示的本地时间: #{display_time}"3. 时间格式化最佳实践
ruby
class TimeFormattingBestPractice
# 定义标准格式常量
LOG_FORMAT = "%Y-%m-%d %H:%M:%S.%3N"
ISO_FORMAT = "%Y-%m-%dT%H:%M:%S.%3NZ"
HUMAN_FORMAT = "%B %d, %Y at %I:%M %p"
# 标准化日志时间戳
def self.log_timestamp(time = Time.now)
time.strftime(LOG_FORMAT)
end
# 标准化ISO时间戳
def self.iso_timestamp(time = Time.now)
time.utc.strftime(ISO_FORMAT)
end
# 标准化人类可读时间戳
def self.human_timestamp(time = Time.now)
time.strftime(HUMAN_FORMAT)
end
# 解析标准时间格式
def self.parse_standard(time_string)
Time.parse(time_string)
rescue ArgumentError
nil
end
end
# 使用格式化最佳实践
current_time = Time.now
puts "日志格式: #{TimeFormattingBestPractice.log_timestamp(current_time)}"
puts "ISO格式: #{TimeFormattingBestPractice.iso_timestamp(current_time)}"
puts "人类格式: #{TimeFormattingBestPractice.human_timestamp(current_time)}"📚 下一步学习
掌握了Ruby日期和时间处理后,建议继续学习:
- Ruby 范围(Range) - 学习范围对象的使用
- Ruby 迭代器 - 深入学习迭代模式
- Ruby 异常处理 - 掌握错误处理机制
- Ruby 文件处理及I/O - 了解文件读写操作
继续您的Ruby学习之旅吧!