Day7. 活用Ruby的Time,人人都可以成为时间魔术师

时间永远是人生的一大问题,但在`Ruby的世界中,却不是什麽问题。只要我们熟悉一些时间上的技巧,基本上都难不倒我们。

Time.now && Time.current

Time.nowTime.current 的区别是Ruby中非常重要的概念:

  • Time.now 使用当前机器作业系统的时区
  • Time.current 使用 Rails 中设定的时区 (ActiveSupport 的方法)

接着,我们再来看下列例子来观察不同电脑,因时区设定的不同,而导致的不同结果。

⭐️ 自己的电脑时区(Mac)

Time.now
#=> 电脑的时区
#=> 2019-11-05 10:44:09 +0800
Time.current
#=> Rails的时区
#=> Tue, 05 Nov 2019 18:44:16 CST +08:00

Time.parse("2019-11-04 16:20:23")
#=> 2019-11-04 16:20:23 +0800
Time.zone.parse("2019-11-04 16:20:23")
#=> Mon, 04 Nov 2019 16:20:23 CST +08:00

Time.zone.parse("2019-11-04 16:20:23").to_i
#=> 1572855623
Time.parse("2019-11-04 16:20:23").to_i
#=> 1572855623

⭐️ 测试站的时区

Time.now
#=> 电脑的时区
#=> 2019-11-05 10:44:09 +0000
Time.current
#=> Rails的时区
#=> Tue, 05 Nov 2019 18:44:16 CST +08:00

Time.parse("2019-11-04 16:20:23")
#=> 2019-11-04 16:20:23 +0000
Time.zone.parse("2019-11-04 16:20:23")
#=> Mon, 04 Nov 2019 16:20:23 CST +08:00

Time.parse("2019-11-04 16:20:23").to_i
#=> 1572884423
Time.zone.parse("2019-11-04 16:20:23").to_i
#=> 1572855623

不过云端服务中,我们可以将电脑的时区设定自己想要的时区。譬如,我们就可以将测试站的时区设定+8:00

Datetime

Datetime 是一种可以表现时间的类别,为Ruby的方法

> DateTime.tomorrow
#=> Sat, 28 Aug 2021

> DateTime.now.tomorrow
#=> Sat, 28 Aug 2021 10:51:58 +0800

Class

Ruby & Rails中,有很多物件都可以拿来处理时间。我们必须知道哪些方法是哪种Object,会比较方便使用。

由於ActiveSupport::TimeWithZoneRails的库,无法在irb里面执行,可以应证Time.current 使用的是Rails中设定的时区,才会需要使用Rails提供的物件。

DateTime.now.class
#=> DateTime

DateTime.new.class
#=> DateTime

DateTime.strptime("1571393643",'%s').class
#=> DateTime

Time.now.class
#=> Time

Time.new.class
#=> Time

Time.at(1571393643).class
#=> Time

Time.current.class
#=> ActiveSupport::TimeWithZone

1.day.from_now.class
#=> ActiveSupport::TimeWithZone

2.days.ago.class
#=> ActiveSupport::TimeWithZone

Time.zone.class
#=> ActiveSupport::TimeZone

互动式irb 内看不懂 Time.current

Time.current
# Traceback (most recent call last):
# NoMethodError (undefined method `current' for Time:Class)

Time.now
#=> 2021-09-07 09:29:53.888093 +0800

时间戳的转换

下列介绍时间戳转换成时间物件的方法

# 时间戳转 Datetime
DateTime.strptime("1571393643",'%s')
#=> Fri, 18 Oct 2019 10:14:03 +0000

# 时间戳转 Time
Time.at(1571393643)
#=> 2019-10-18 18:14:03 +0800

# 时间戳转 Time
Time.zone.at(1571393643)
#=> Fri, 18 Oct 2019 18:14:03 CST +08:00

# 时间戳转 Time
Time.zone.at(1571393643).strftime("%Y-%m-%d %H:%M:%S")
#=> => "2019-10-18 18:14:03"

# 时间字串转为时间戳
#=== *1000 为13码
Time.zone.parse('2019.12.25 17:00:00').to_i*1000

转时区

Ruby 也有提供特别的方法转换时间

Time.now.localtime("+05:30")
#=> 2019-11-06 07:17:41 +0530

Time.now.localtime("+05:30")
#=> 2019-11-06 07:17:46 +0530

Time.now.localtime("+00:30")
#=> 2019-11-06 02:17:54 +0030

Time.now.localtime("+00:00")
#=> 2019-11-06 01:47:58 +0000

Time.now.localtime("+08:00")
#=> 2019-11-06 09:48:08 +0800

Tomorrow

下列方法指的是明天的同一个时间

Date.tomorrow
Date.current.tomorrow
Date.now.tomorrow
DateTime.now.tomorrow.to_date
Time.now.tomorrow.to_date
Date.current + 1

end_of_year & end_of_month & beginning_of_day

Ruby提供的这些方法,可以使我们能够轻松应付复杂的问题,譬如说要将某一笔资料的红利点数设定结束时间为今年的最後一天,或者下年的同月月底结束的话,可以用以下的方法写

# 该年最後一天
Date.today.end_of_year
# 该年最後一月
Date.today.end_of_month

# 某日隔天最开头 (00:00)
DateTime.tomorrow.beginning_of_day 
# 某日隔天最结尾时间 (23:59)
DateTime.tomorrow.end_of_day       

# 从今天算起一年後的该月最後一天
1.year.from_now.end_of_month 

from_now

几天後

1.day.from_now    # 一天後
2.days.from_now   # 两天後
3.days.from_now   # 三天後

指定日期的几天後

#======= start_time 可填入任意时间
1.day.from_now(start_time) 

# 明天
1.day.from_now(Time.current)
#=> Wed, 08 Sep 2021 15:35:47 CST +08:00

# 昨天的明天
1.day.from_now(1.day.ago)
#=> Tue, 07 Sep 2021 15:33:17 CST +08:00

# 昨天的明天
1.day.from_now(DateTime.now.yesterday)
#=> Tue, 07 Sep 2021 15:33:28 +0800

strftime

strftime跟正规表达式一样,都是每次用的时候,都要重新查询的用法,以下例举自己常用的几种方法。

# 下列两者意思等同
Time.current.strftime("%Y-%m-%d %H:%M:%S")  #=> "2021-09-06 22:49:05" 
Time.current.strftime("%F %T")              #=> "2021-09-06 22:49:05"

DateTime.now.strftime('%Q')
#=> "1630999913620"

DateTime.now.strftime("%FT%T.%5N")
#=> "2021-09-07T15:32:28.76346""

to_time

Time.current
#=> Tue, 07 Sep 2021 09:14:01 CST +08:00

Time.current.to_time
#=> 2021-09-07 09:14:03 +0800

DateTime.now.to_time
#=> 2021-09-07 09:15:00 +0800

to_datetime

'2019.12.18 11:10:00'.to_datetime
#=> Wed, 18 Dec 2019 11:10:00 +0000

取得时间区间

首先我们先讲send_time回传的第一值为boolean,其中true代表意义为立即,相对来说,false代表的意义为排程。在这里,回传的第一个值并不是重点,重点是在回传的第二个值。

下列为Tool的使用情境:

  • 推播时间

    电商平台实作推播,必须要顾及顾客在合理的时间收到手机通知,要不然在半夜或清晨让顾客收到的话,顾客会有不好的观感,甚至有可能会想要删除App,因此9-17点为当下推播,其余时间设定排程推播

  • 物流取货时间:

    实作物流方面,我们也必须顾及店家营业时间,在营业时间以外我们没有办法让物流进店取货,因此我们必须将营业时间以外的时间排到其他天的营业时间,以供顺丰物流取货。11点到17点可以请物流到府取货,当天11点以前的物流单排在11点,而当天17点以後的物流单则排在隔天的11点。

我们会在 Day11详细的介绍类别方法的使用,这边只会提到如何使用。

class Tool
  class << self
    # 推播时间: 9点到17点
    def notification_sent_at
      send_time(9, 17)
    end

    # 物流取货时间: 11点到17点
    def sf_sent_at
      send_time(11, 17).last.strftime("%F %T")
    end

    def send_time(start_hour, end_hour)
      zero_am = DateTime.now.beginning_of_day
      started_at = DateTime.now.beginning_of_day.change(hour: start_hour)
      ended_at = DateTime.now.beginning_of_day.change(hour: end_hour)

      if Time.current.between?(zero_am, started_at)
        [false, started_at]
      elsif Time.current.between?(ended_at, DateTime.now.tomorrow.beginning_of_day)
        [false, DateTime.now.tomorrow.beginning_of_day.change(hour: start_hour)]
      else
        [true, Time.current]
      end
    end
  end
end

⭐️ 寻找指定开始和结束时间的订单

Order.where(created_at: params[:startedAt]..params[:endedAt])
SELECT `orders`.* FROM `orders` WHERE `orders`.`created_at` >= '2021-08-19 03:10:25.041651' AND `orders`.`created_at` < '2021-08-20 03:10:25.042367' LIMIT 11

总结

下列问题留给大家想,以下也是解时间问题时,工程师常常思考的问题。

  • 有几种方法可以将时间转换为时间戳?
  • 有几种方法可以将时间戳转换成时间?
  • 有几种方法可以将字串转为时间?

Day2-Day7 主要介绍的内容为基本Ruby使用

Day8-10 会介绍区块Block

参考资料


<<:  Day7 资料储存 - object storage优缺点及场景

>>:  Array筛选特定值

繁体转换简体

最近想记录一下繁体转换简体,是因为网站或是在写blog的时候,未来会加入简体版,这样内容就可以迅速转...

Rails入门:疑难杂症~~ 无情dubug!! 上传Heroku先从资料库开始 PostgreSQL

大家好,我是Sean,一位Rails初心者,第一次发文请大大们手下留情XD 受一位朋友RS启发,决定...

Day 4 仓库 Repository

若你有用过 github 的话,对於仓库 Repository 的概念想必是不陌生。它就是一个存放各...

铁人赛 Day10 -- 一定要知道的 CSS (七) -- background:linear-gradient渐层背景

前言 昨天知道一些背景的属性後,是不是觉得有点单调呀,所以我们今天就来谈谈渐层 基本语法 backg...

2.MYSQL 安装

先去MYSQL官网下载:https://dev.mysql.com/downloads/ 然後按 这...