您现在的位置是:网站首页> 编程资料编程资料

Ruby日期时间的比较,日期转换等时间日期处理方法大全_ruby专题_

2023-05-26 321人已围观

简介 Ruby日期时间的比较,日期转换等时间日期处理方法大全_ruby专题_

Ruby中DateTimeDateTime这3个类提供 了和日期时间相关的操作。

Date只能处理日期
Time能处理日期和时间
DateTime也能处理日期和时间

其中,DateTime 是Date的一个子类,是对时间部分数据的补充。要使用Date和DateTime类,只需导入date库就可以,要使用Time类,导入time库就行。

require 'date' # 提供Date和DateTime类 require 'time' # 提供Time类(可直接使用,但导入后有更多方法)

一般来说,操作日期时间的常用操作包括:

  • 创建(构建)日期、时间对象
  • 在字符串和日期时间对象之间进行转换
  • 日期时间的比较
  • 日期时间的运算
  • 操作时区
  • 等等…

而这3个类中,都各自提供了一些方法,很多方法是重叠的。据我个人测试,DateTime这个标准库效率是最高的。

下面,针对各种常见功能将这3个类结合在一起去介绍。

Ruby构建日期时间对象

这3个类都能直接构造日期、时间对象。其中Date只能构造日期不能构造时间对象。

Date构造日期对象

Date构造日期对象。如果提供了时间部分,则忽略时间部分。因为不涉及时间部分,所以不能也没必要指定时区。

Ruby

# 1.构造当前日期对象:Date.today >> Date.today => # >> puts Date.today 2019-08-05 # 2.构造指定日期:Date.new ## 可以构造超出2038年的日期 ## 如果没有给定月、日,则默认为1 ## 如果没有提供任何参数,默认是-4712年1月1日,这没什么意义 ## 如果给定时间部分,则报错 >> Date.new(2018,3,20) => # >> Date.new(2078,3,20) => # >> Date.new(1900) => # >> Date.new => # # 3.构造指定日期:Date.parse ## 根据字符串解析成日期格式 ## 如果给定时间部分,则忽略 ## 对于年份,可以给定1、2位数的和4位数的 ## 对于1、2位数,如果数值是大于等于69的,则默认加上1900 ## 对于0和68之间的数值,则默认加上2000 >> Date.parse('2007/09/12') => # >> Date.parse('2007/9/12') => # >> Date.parse('2007-9-12') => # >> Date.parse('2007-9-12 12:30:59') => # >> Date.parse('08-9-12') => # >> Date.parse('68-9-12') => # >> Date.parse('69-9-12') => # # 4.使用strptime方法根据给定格式的字符串转换成日期时间对象 ## 关于支持的格式,参见后文 >> Date.strptime('2001-02-03', '%Y-%m-%d') => # >> Date.strptime('02-03-2001', '%m-%d-%Y') => #

Time构造日期时间对象

常见的方法是:new(别名now)、at、local(别名mktime)、parse。

注:Time类没有strptime方法将给定格式的字符串转换成日期时间对象。

# 1.new()或now()构建当前日期时间对象 ## new也可以根据给定参数构建日期时间对象,无参时等价于now ## 可以指定时区 >> Time.now => 2019-08-05 14:12:30 +0800 >> Time.new => 2019-08-05 14:12:31 +0800 >> Time.new(2007,11,6,17,10,0, "+08:00") # 指定时区 => 2007-11-06 17:10:00 +0800 # 2.at()将epoch转换成日期时间对象 ## 支持时区,支持小数秒、毫秒、微秒、纳秒 >> Time.at(1553488199) => 2019-03-25 12:29:59 +0800 >> Time.at(1553488199, in: '+08:00') # 指定时区 => 2019-03-25 12:29:59 +0800 >> Time.at(1553488199.3).usec # 小数秒0.3秒,即3毫秒 => 299999 >> Time.at(1553488199,123.345,:millisecond).usec # 毫秒参数 => 123344 >> Time.at(1553488199,123.345).nsec # 微秒参数 => 123344 >> Time.at(1553488199,123.345,:usec).nsec # 微秒参数 => 123344 >> Time.at(1553488199,123,:nsec).nsec # 纳秒参数 => 123 # 3.mktime或local根据参数构建本地时区的日期时间对象 ## 要构建UTC时区时间,使用gm()或别名方法utc() ## mktime/local和gm/utc之间,除时区不同外,其它等价 >> Time.mktime(2009,10,23,14,3,6) => 2009-10-23 14:03:06 +0800 >> Time.mktime(2009,10,23) => 2009-10-23 00:00:00 +0800 >> Time.mktime(2009,10) => 2009-10-01 00:00:00 +0800 # 4.parse根据字符串转换成日期时间对象,同样可以指定时区 >> Time.parse("2009/12/25 12:29:59") => 2009-12-25 12:29:59 +0800 >> Time.parse("2009/12/25") => 2009-12-25 00:00:00 +0800 >> Time.parse("2009/12") => 2009-12-01 00:00:00 +0800 >> Time.parse("2009/12/25 12:29:59 +00:00") # 指定时区 => 2009-12-25 12:29:59 +0000 >> Time.parse('2009-07-12 16:32:40.00123').nsec # 指定小数秒 => 1230000

由于上面介绍的几种Time类方法构建出来的日期时间对象的时区默认是+08:00(东八区,中国所在时区即东八),所以指定时区与否可随意。

但下面DateTime类的几种方法构造日期时间对象的时区默认是+00:00,所以通常要指定时区。

DateTime构造日期时间对象

DateTime是Date的子集,且由于某些方法被重写,所以某些方法参数有一点点不同。最常使用的构造器是new、now、parse和strptime。

另外,DateTime对象是包含纳秒的。

# 1.new()方法构造DateTime对象 ## 可以指定时区、指定小数秒 >> DateTime.new(2009,1,2,3,4,5) => # >> DateTime.new(2009,1,2,3,4,5,'+7') # 指定时区 => # >> DateTime.new(2009,1,2,3,4,5,'+08:00') => # >> DateTime.new(2009,1,2,3,4,5.3,'+08:00') # 指定小数秒,即毫秒 => # >> DateTime.new(2009,1,2,3,4,5.3,'+08:00').sec_fraction => (3/10) # 2.now()获取当前时间: >> DateTime.now => # # 3.parse解析字符串为日期时间对象 >> DateTime.parse('2009-12-20 12:03:30') => # >> DateTime.parse('2009-12-20 12:03:30 +8') => # # 4.strptime解析给定格式的字符串为日期时间对象 ## 关于支持的格式,参见后文 >> DateTime.strptime('2009-12-20 12:03:30 +8','%Y-%m-%d %H:%M:%S %z') => #

期、时间有效性检测

构建日期时间对象时,有些日期、时间是无效的,但因为取值范围的不同、特殊日期特殊时间点的取值不同,导致处理比较麻烦。

比如,每月可能有29、30、31天,但11月31号是无效的。再比如,秒数的范围是在0-60,但第61秒仅作为闰秒时才是有效值,所以几乎所有时间的第61秒都是错误的秒数。

Time可以检测范围外的无效时间,但是不能检测范围内的无效时间。比如Time知道11月32号是错误的,但它不知道11月31号是错误的,实际上Time会将有效范围内超出的部分进位,比如11月31号进位到12月1号。

# 有效日期 >> Time.new(2007,11,30,17,10,30) => 2007-11-30 17:10:30 +0800 # 范围内的无效日期,进位到12月1号 >> Time.new(2007,11,31,17,10,30) => 2007-12-01 17:10:30 +0800 # 有效秒 >> Time.new(2007,11,31,17,10,59) => 2007-12-01 17:10:59 +0800 # 第61秒进位到下一分钟 >> Time.new(2007,11,31,17,10,60) => 2007-12-01 17:11:00 +0800 # 错误的秒,报错 >> Time.new(2007,11,31,17,10,61) ArgumentError: sec out of range from (pry):25:in `initialize' # 错误的日期,报错 >> Time.new(2007,11,32,17,10,59) ArgumentError: argument out of range from (pry):26:in `initialize'

Time会进位的特性有时候是有益的,但不利于检测。好在,Date、DateTime可以很好的检测无效的日期、时间,只要是无效的日期时间,它们都会报错,其中Date只能检测无效日期,DateTime可检测无效日期,也能检测无效时间。

# Date检测无效日期 >> Date.new(2007,11,30) => # >> Date.new(2007,11,31) ArgumentError: invalid date from (pry):28:in `initialize' # DateTime检测无效日期、时间 >> DateTime.new(2007,11,31,11,12,13) ArgumentError: invalid date from (pry):31:in `new' >> DateTime.new(2007,11,30,11,12,60) ArgumentError: invalid date from (pry):32:in `new'

既然它们会报错,那么只要加上异常捕获的代码即可完成日期时间的有效性检测。比如在Time类中使用DateTime类来检测有效的日期和时间:

class Time def self.valid?(y,m=1,d=1,H=0,M=0,S=0,us=0) require 'date' begin dt = DateTime(y,m,d,H,M,S,us) rescue returin nil end dt end end

ate、Time、DateTime对象之间的类型转换

这三个类的渊源:

  • Time类是对底层C库的时间函数的封装,它们通常基于UNIX epoch,因此不能表示1970年以前的时间
  • Date类是对Time类的补充,用于处理1970年之前的时间,但是它只能处理日期不能处理时间
  • DateTime继承自Date,可以处理任意时间点的日期时间,但有些Time的功能DateTime没有

所有有些时候有必要对它们进行类型的转换。

这3个类都提供了下面3个方法,在各自之间进行转换:

to_date to_time to_datetime

例如:

# Date对象转换 >> Date.today.to_date => # >> Date.today.to_time => 2019-08-05 00:00:00 +0800 >> Date.today.to_datetime => # # Time对象转换 >> Time.now.to_date >> Time.now.to_time >> Time.now.to_datetime # DateTime对象转换 >> DateTime.now.to_date >> DateTime.now.to_time >> DateTime.now.to_datetime

需要注意,Date类对象由于不包含时间部分,它也没有时区。所以:

  • Time/DateTime转成Date后会丢失时间和时区部分的数据
  • Date对象to_time转成Time对象,设置的时区是其默认时区『+08:00』,而to_datetime转成DateTime对象,设置的时区是其默认时区『+00:00』。但是Time和DateTime之间的转换不会变换时区
d = Date.new(2019,5,23) d.to_time #=> 2019-05-23 00:00:00 +0800 d.to_datetime.to_s #=> "2019-05-23T00:00:00+00:00"

日期时间和字符串、数值之间的转换

这是常用的需求。

  • 日期时间到字符串:
    • to_s转成特定的字符串格式
    • strftime转成自定义的字符串格式,参见下文
  • 日期时间到数值:只有包含时间部分(即Date不在讨论范围)才能转成数值
    • 通常转成的是epoch时间,支持整数、浮点数、分数
  • 字符串转成日期时间:前文构造日期时间对象部分已介绍
  • 数值转日期时间:前文构造日期时间对象部分已介绍

日期时间对象还支持转换成数组。

先看Time类型对象,它支持to_s、to_f、to_i、to_r(转分数)、to_a(转数组)。

# 转字符串 >> Time.now.to_s => "2019-08-05 15:29:49 +0800" # 转数值,即整数epoch,和Time.at是相反的功能 >> Time.now.to_i => 1564990192 # 转浮点数,即小数epoch ## 默认保留6位小数,即微秒级别 ## 可以格式化保留成纳秒级别的字符串 ## 但无法转成纳秒级别的浮点数,因为超出了float精度范围 ## 可以使用BigDecimal保存成科学记数法的浮点数 >> Time.now.to_f => 1564990197.671165 # 微秒 >> "%.9f" % Time.now.to_f => "1564990502.569714785" # 纳秒字符串 >> ("%.9f" % Time.now.to_f).to_f => 1564991500.1034229 # 超出,被剪掉 ## 使用BigDecimal保存十进制科学记数法浮点数 >> require 'bigdecimal' >> BigDecimal("%.9f" % Time.now.to_f) # 纳秒浮点数 => 0.1564991123879058599e10 # 转分数 >> Time.now.to_r => (3912475500217301/2500000) # 转数组 ## 10个数组元素 ## [sec,min,hour,day,month,year,wday,yday,isdst,zone] ## [秒/分/时/日/月/年/周中天/年中天/是否夏令时/时区] >> Time.now.to_a => [4, 30, 15, 5, 8, 2019, 1, 217, false, "DST"]

注意,Time所查看的时区以DST、CST、UTC、GMT这样的方式显示,而DateTime查看的时区则是以类似于『+08:00』这种方式显示。

再看Date和DateTime转这些类型。其实Date/DateTime支持的转换方法很少,它们只支持to_s,其它方法都不支持。所以,要想转成整数,可以先to_time,再to_i。

>> Date.today.to_s => "2019-08-05" >> Date.today.to_time => 2019-08-05 00:00:00 +0800 >> Date.today.to_time.to_i => 1564934400 >> DateTime.now.to_s => "2019-08-05T15:55:05+08:00" >> DateTime.now.to_time.to_f => 1564991712.1981702

strftime方法

strptime是将给定格式的字符串转成

-六神源码网