iT邦幫忙

2021 iThome 鐵人賽

DAY 7
0
Modern Web

初階 Rails 工程師的養成系列 第 7

Day7. 活用Ruby的Time,人人都可以成為時間魔術師

  • 分享至 

  • xImage
  •  

時間永遠是人生的一大問題,但在`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

參考資料


上一篇
Day6. Array & Hash 之間的組合應用
下一篇
Day8. functional programming in Ruby - Block Part1
系列文
初階 Rails 工程師的養成34
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言