# **第 20 章 Time 类与 Date 类**
在本章中,我们将会介绍操作时间的 `Time` 类、以及操作日期的 `Date` 类。
-
**Time 类与 Date 类**
对 `Time` 类以及 `Date` 类的概要进行说明
-
**时间、日期的获取**
介绍获取当前时间、日期的方法。
-
**时间、日期的计算**
介绍时间、日期的比较及运算方法。
-
**转换为字符串**
介绍把时间、日期转换为字符串的方法。
-
**解析字符串**
介绍把表示时间、日期的字符串分别转换为 `Time` 对象、`Date` 对象的方法。
### **20.1 Time 类与 Date 类**
`Time` 类用于表示时间。时间除了表示年月日时分秒的信息以外,还包含了表示地域时差的时区(time zone)信息。例如我们可以计算中国当前时间是国际协调时间的几点。
`Date` 类只用于表示年月日。因此,相对于 `Time` 类以秒为单位计算时间,`Date` 类则是以天为单位进行计算的。`Date` 类还可以求下个月的同一天、本月末等日期。
`Time` 类与 `Date` 类表示时间、日期时并没有什么特别限制(前提是现在的日历能一直用下去,甚至“西历 100 亿年”这样的时间、日期也都是可以表示的)。但实际文件的时间戳、程序的运行时间等系统内的时间、以及数据库中的时间类型数据等的情况下,有时候就会受到执行环境的限制。
### **20.2 时间的获取**
-
**`Time.new`
`Time.now`**
通过 `Time.new` 方法或者 `Time.now` 方法获取表示当前时间的 `Time` 对象。
~~~
p Time.new #=> 2013-03-30 03:06:00 +0900
sleep 1 #=> 等待1 秒
p Time.now #=> 2013-03-30 03:06:01 +0900
~~~
-
***t*.`year`
*t*.`month`
*t*.`day`**
也可以获取时间对象中的年、月、日。
~~~
t = Time.now
p t #=> 2013-03-30 03:07:13 +0900
p t.year #=> 2013
p t.month #=> 3
p t.day #=> 30
~~~
表 20.1 列举了时间的相关方法。
**表 20.1 时间的相关方法**
| 方法名 | 意义 |
|-----|-----|
| `year` | 年 |
| `month` | 月 |
| `day` | 日 |
| `hour` | 时 |
| `min` | 分 |
| `sec` | 秒 |
| `usec` | 秒以下的位数(以毫秒为单位) |
| `to_i` | 从 1970 年 1 月 1 日到当前时间的秒数 |
| `wday` | 一周中的第几天(0 表示星期天) |
| `mday` | 一个月中的第几天(与 `day` 方法一样) |
| `yday` | 一年中的第几天(1 表示 1 月 1 日) |
| `zone` | 时区(`JST` 等) |
-
**`Time.mktime`(
*year*[*, month*[*,* day[*, hour* [*, min*[*, sec*[*, usec*]]]]]]])**
通过 `Time.mktime` 方法可以根据指定时间获取 `Time` 对象。
~~~
t = Time.mktime(2013, 5, 30, 3, 11, 12)
p t #=> 2013-05-30 03:11:12 +900
~~~
文件的创建时间、更新时间等也都能以 `Time` 对象的形式获取。详情请参考 18.3 节。
### **20.3 时间的计算**
`Time` 对象之间可以互相比较、运算。
~~~
t1 = Time.now
sleep(10) # 等10 秒
t2 = Time.now
p t1 < t2 #=> true
p t2 - t1 #=> 10.005073
~~~
还可以增加或减少 `Time` 对象的秒数。
~~~
t = Time.now
p t #=> 2013-03-30 03:11:44 +0900
t2 = t + 60 * 60 * 24 #=> 增加24 小时的秒数
p t2 #=> 2013-03-31 03:11:44 +0900
~~~
### **20.4 时间的格式**
-
***t*.`strftime`(*format*)
*t*.`to_s`**
通过 `Time#strftime` 方法可以把时间转换为遵循某种格式的字符串。表 20.2 为格式(*format*)中可以使用的字符串。
**表 20.2 Time#strftime 中的格式字符串**
| 格式 | 意义与范围 |
|-----|-----|
| `%A` | 星期的名称(`Sunday`、 `Monday`……) |
| `%a` | 星期的缩写名称(`Sun`、 `Mon`……) |
| `%B` | 月份的名称(`January`、 `February`……) |
| `%b` | 月份的缩写(`Jan`、 `Feb`……) |
| `%c` | 日期与时间 |
| `%d` | 日(01 ~ 31) |
| `%H` | 24 小时制(00 ~ 23) |
| `%I` | 12 小时制(01 ~ 12) |
| `%j` | 一年中的天(001 ~ 366) |
| `%M` | 分(00 ~ 59) |
| `%m` | 表示月的数字(01 ~ 12) |
| `%p` | 上午或下午(AM、PM) |
| `%S` | 秒(00 ~ 60) |
| `%U` | 表示周的数字。以星期天为一周的开始(00 ~ 53) |
| `%W` | 表示周的数字。以星期一为一周的开始(00 ~ 53) |
| `%w` | 表示星期的数字。0 表示星期天(0 ~ 6) |
| `%X` | 时间 |
| `%x` | 日期 |
| `%Y` | 表示西历的数字 |
| `%y` | 西历的后两位(00 ~ 99) |
| `%Z` | 时区( `JST` 等) |
| `%z` | 时区(+0900 等) |
| `%%` | 原封不动地输出 `%` |
例如,`Time#to_s` 方法得到的字符串格式与 `"%Y-%m-%d %H:%M:%S %z"` 是等价的。
~~~
t = Time.now
p t.to_s #=> 2013-03-30 03:13:14 +0900
p t.strftime("%Y-%m-%d %H:%M:%S %z") #=> 2013-03-30 03:13:14 +0900
~~~
> **备注** `Time#strftime` 方法的格式是与平台相关的,不同平台下的执行结果可能不一样。例如,在 Windows 中,`"%Z"` 的执行结果会显示“中国标准时间”。
-
***t*.`rfc2822`**
通过 `Time#rfc2822` 方法可以生成符合电子邮件头部信息中的 Date :字段格式的字符串。在互联网的相关文档 RFC(Request For Comments)中,有一个关于电子邮件形式定义的 RFC 2822 文档,`rfc2822` 这个方法名就来自于此。使用这个方法前,需要预先通过 `require "time"` 引用 `time` 库。
~~~
require "time"
t = Time.now
p t.rfc2822 #=> "Sat, 30 Mar 2013 03:13:34 +0900"
~~~
-
***t*.`iso8601`**
通过 `Time#iso8691` 方法生成符合 ISO 8601 国际标准的时间格式的字符串。使用这个方法时也需要引用 `time` 库。
~~~
require "time"
t = Time.now
p t.iso8601 #=> "2013-03-30T03:13:34+09:00"
~~~
### **20.5 本地时间**
世界各地都有时差。大家的计算机中也设有时区,一般计算机中的时间都是根据时区来设定的。
-
***t*.`utc`
*t*.`localtime`**
我们可以用 `Time#utc` 方法把 `Time` 对象的时区变更为国际协调时间(UTC)。反之,用 `Time#localtime` 方法则可以把 UTC 变更为本地时间。
~~~
t = Time.now
p t #=> 2013-03-30 03:15:19 +0900
t.utc
p t #=> 2013-03-29 18:15:19 UTC
t.localtime
p t #=> 2013-03-30 03:15:19 +0900
~~~
### **20.6 从字符串中获取时间**
可以将以字符串形式表示的时间转换为 `Time` 对象。
-
**`Time.parse`(*str*)**
通过使用 `require "time"`,我们就可以使用 `Time.parse` 方法,来操作以字符串形式表现的时间。`Time.parse` 方法会解析参数字符串 *str*,返回对应的 `Time` 对象。
`Time.parse` 方法除了可以返回与 `Time#to_s` 方法相同的格式,还可以返回 "*yyyy/mm/dd*" 等多种格式。
~~~
require "time"
p Time.parse("Sat Mar 30 03:54:15 UTC 2013")
#=> 2013-03-30 03:54:15 UTC
p Time.parse("Sat, 30 Mar 2013 03:54:15 +0900")
#=> 2013-03-30 03:54:15 +0900
p Time.parse("2013/03/30")
#=> 2013-03-30 00:00:00 +0900
p Time.parse("2013/03/30 03:54:15")
#=> 2013-03-30 03:54:15 +0900
p Time.parse("H25.03.31")
#=> 2013-03-31 00:00:00 +0900
p Time.parse("S48.9.28")
#=> 1973-09-28 00:00:00 +0900
~~~
### **20.7 日期的获取**
`Date` 类用于处理不包含时间的日期。使用 `Date.today` 方法可以得到表示当前日期的 `Date` 对象。使用 `Date` 类需要引用 `date` 库。
~~~
require "date"
d = Date.today
puts d #=> 2013-03-30
~~~
与 Time 类一样,日期也有其相关的方法。
~~~
require "date"
d = Date.today
p d.year # 年 => 2013
p d.month # 月 => 3
p d.day # 日 => 30
p d.wday # 一周中的第几天(0 表示星期天) => 6
p d.mday # 一个月中的第几天(与 day 方法一样) => 30
p d.yday # 一年中的第几天(1 表示 1 月 1 日) => 89
~~~
还可以用指定日期生成 `Date` 对象。
~~~
require "date"
d = Date.new(2013, 3, 30)
puts d #=> 2013-03-30
~~~
`Date` 类有一个特点是,可以对月末的日期做-1 处理(-2 表示月末的前一天)。当然也可以应对闰年。
~~~
require "date"
d = Date.new(2013,2,-1)
puts d #=> 2013-02-28
d = Date.new(2016, 2, -1)
puts d #=> 2016-02-29
~~~
### **20.8 日期的运算**
`Date` 对象之间的运算以天为单位。因此,`Date` 对象之间进行减法运算时,返回的是两者之间的天数。日期减法运算的结果不是整数,而是 `Rational` 对象。此外,还可以将 `Date` 对象与整数进行加法、减法等运算,这时会返回该对象前后的日期。
~~~
require "date"
d1 = Date.new(2013, 1, 1)
d2 = Date.new(2013, 1, 4)
puts d2 - d1 #=> 3/1 (3 天的意思)
d = Date.today
puts d #=> 2013-03-30
puts d + 1 #=> 2013-03-31
puts d + 100 #=> 2013-07-08
puts d - 1 #=> 2013-03-29
puts d -100 #=> 2012-12-20
~~~
通过使用 `>>` 运算符,我们就可以获取后一个月相同日期的 `Date` 对象。同理,使用 `<<` 运算符得到的是表示前一个月相同日期的 `Date` 对象。如果该月中没有相同的日期(例如 2 月 30 日),则会返回月末的日期。
~~~
require "date"
d = Date.today
puts d #=> 2013-03-30
puts d >> 1 #=> 2013-04-30
puts d >> 100 #=> 2021-07-30
puts d << 1 #=> 2013-02-28
puts d << 100 #=> 2004-11-30
~~~
### **20.9 日期的格式**
与 `Time` 类一样,通过 `strftime` 方法也可以将日期按指定的格式转换为字符串。但结果中时间的部分会全部变为 0。
~~~
require "date"
t = Date.today
p t.strftime("%Y/%m/%d %H:%M:%S")
#=> "2013/03/30 00:00:00"
p t.strftime("%a %b %d %H:%M:%S %Z %Y")
#=> "Sat Mar 30 00:00:00 +00:00 2013"
p t.to_s #=> "2013-03-30"
~~~
### **20.10 从字符串中获取日期**
使用 `Date.parse` 方法可以将字符串转换为日期。这个方法可以应对多种日期格式。
~~~
require "date"
puts Date.parse("Sat Mar 30 03:50:12 JST 2013") #=> 2013-03-30
puts Date.parse("H25.05.30") #=> 2013-05-30
puts Date.parse("S48.9.28") #=> 1973-09-28
~~~
### **练习题**
1. 定义 `cparsedate` 方法,把“2013 年 5 月 30 日下午 8 点 17 分 50 秒”这种使用了“年月日时分秒”的字符串转换为 `Time` 对象。
2. 定义 `ls_t` 方法,把指定目录下的文件按时间顺序排列,就像 Unix 的 ls -t 命令那样。这个方法只有一个参数。
**`ls_t`( 目录名)**
将指定目录下的文件名按照时间从小到大的顺序排列并输出。
3. 使用 `Date` 类获取本月中每天所对应的星期,并按以下日历格式输出结果。
~~~
May 2013
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
~~~
> 参考答案:请到图灵社区本书的“随书下载”处下载([http://www.ituring.com.cn/book/1237](http://www.ituring.com.cn/book/1237))。
- 推荐序
- 译者序
- 前言
- 本书的读者对象
- 第 1 部分 Ruby 初体验
- 第 1 章 Ruby 初探
- 第 2 章 便利的对象
- 第 3 章 创建命令
- 第 2 部分 Ruby 的基础
- 第 4 章 对象、变量和常量
- 第 5 章 条件判断
- 第 6 章 循环
- 第 7 章 方法
- 第 8 章 类和模块
- 第 9 章 运算符
- 第 10 章 错误处理与异常
- 第 11 章 块
- 第 3 部分 Ruby 的类
- 第 12 章 数值类
- 第 13 章 数组类
- 第 14 章 字符串类
- 第 15 章 散列类
- 第 16 章 正则表达式类
- 第 17 章 IO 类
- 第 18 章 File 类与 Dir 类
- 第 19 章 Encoding 类
- 第 20 章 Time 类与 Date 类
- 第 21 章 Proc 类
- 第 4 部分 动手制作工具
- 第 22 章 文本处理
- 第 23 章 检索邮政编码
- 附录
- 附录 A Ruby 运行环境的构建
- 附录 B Ruby 参考集
- 后记
- 谢辞