* 定义快速按时间查询的便利方法 ** 问题 对于按照时间范围查询的需求,一般需要构造一个表示时间范围的 Range 对象,例如: #+BEGIN_SRC ruby Student.where(created_at: Time.now.beginning_of_day..Time.now.end_of_day) #+END_SRC 或者 #+BEGIN_SRC ruby Student.where(created_at: Time.new(2016, 1, 1)..Time.new(2016, 12, 31)) #+END_SRC ** 定义一个便利方法 为了简化构造时间 Range 对象的过程,我们可以定义一个便利方法,例如: 创建一个文件 =~/.arql.d/date_range.rb= ,内容如下: #+BEGIN_SRC ruby module Kernel # == Example: # Range # 1. -3..0 # 2. '27'..'29' # 3. '04-01'..'04-10' # 4. '2021-04-01'..'2021-04-10' # Array # 1. [nil, nil] or [] => 1970-01-01 ~ 1000 days later from now # 2. [:yday, :today] or [:yesterday, :today] # 3. [-10.months, :today] # Others # 1. Time.now or Date.today # 2. :now or :today # 3. :yesterday or :yday # 4. -2 # 5. -2.months def dates_range(dates) dates = :yesterday if dates == :yday if dates.is_a?(Range) s = if dates.begin.is_a?(Integer) (Time.now+dates.begin.days).beginning_of_day elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{4}-\d{2}-\d{2}$/ Time.parse(dates.begin).beginning_of_day elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{2}-\d{2}$/ Time.strptime(dates.begin, '%m-%d').beginning_of_day elsif dates.begin.is_a?(String) && dates.begin =~ /^\d{2}$/ Time.strptime(dates.begin, '%d').beginning_of_day else dates.begin end e = if dates.end.is_a?(Integer) (Time.now+dates.end.days).end_of_day elsif dates.end.is_a?(String) && dates.end =~ /^\d{4}-\d{2}-\d{2}$/ Time.parse(dates.end).end_of_day elsif dates.end.is_a?(String) && dates.end =~ /^\d{2}-\d{2}$/ Time.strptime(dates.end, '%m-%d').end_of_day elsif dates.end.is_a?(String) && dates.end =~ /^\d{2}$/ Time.strptime(dates.end, '%d').end_of_day else dates.end end s..e elsif dates.is_a?(Array) if dates.size == 2 s = dates.first || Date.new(1970, 1, 1) s = if s.is_a?(Time) || s.is_a?(Date) s.beginning_of_day elsif s == :now Time.now.beginning_of_day elsif s == :today Date.today.beginning_of_day elsif s == :yesterday || s == :yday Date.yesterday.beginning_of_day elsif s.in?(Time.instance_methods) Time.now.send(s).beginning_of_day elsif s.is_a?(ActiveSupport::Duration) (Time.now+s).beginning_of_day elsif s.is_a?(Integer) (Time.now+s.days).beginning_of_day else raise "Not supported s: #{s}" end e = dates.last || Date.today + 1000.days e = if e.is_a?(Time) || e.is_a?(Date) e.end_of_day elsif e == :now Time.now.end_of_day elsif e == :today Date.today.end_of_day elsif e == :yesterday || e == :yday Date.yesterday.end_of_day elsif e.in?(Time.instance_methods) Time.now.send(e).end_of_day elsif e.is_a?(ActiveSupport::Duration) (Time.now+e).end_of_day elsif e.is_a?(Integer) (Time.now+e.days).beginning_of_day else raise "Not supported e: #{e}" end return s..e else times = dates.map do |date| if date.nil? nil elsif date.is_a?(Time) date elsif date == :now Time.now elsif date.in?(Time.instance_methods) Time.now.send(date) elsif date.is_a?(ActiveSupport::Duration) Time.now+date elsif date.is_a?(Integer) (Time.now+date.days).beginning_of_day else raise "Not supported date: #{date}" end end s = times.first || Time.new(1970, 1, 1, 0, 0, 0) e = times.last || Time.now + 1000.days return s..e end else if dates.is_a?(Time) or dates.is_a?(Date) dates = dates.beginning_of_day..dates.end_of_day elsif dates == :now dates = Time.now.beginning_of_day..Time.now.end_of_day elsif dates == :today dates = Date.today.beginning_of_day..Date.today.end_of_day elsif dates == :yday || dates == :yesterday dates = Date.yesterday.beginning_of_day..Date.yesterday.end_of_day elsif dates.in?(Time.instance_methods) dates = Time.now.send(dates).beginning_of_day..Time.now.send(dates).end_of_day elsif dates.is_a?(ActiveSupport::Duration) dates = (Time.now+dates).beginning_of_day..(Time.now+dates).end_of_day elsif dates.is_a?(Integer) (Time.now+dates.days).beginning_of_day..(Time.now+dates.days).end_of_day else raise "Not supported dates: #{dates}" end end end alias_method :dates, :dates_range end class ::ArqlModel class << self def ts_attribute_for_create (timestamp_attributes_for_create||[]).find { |e| e.in?(column_names) } end def ts_attribute_for_update (timestamp_attributes_for_update||[]).find { |e| e.in?(column_names) } end def created_on(dates) attr = ts_attribute_for_create raise 'No attrtibute for create defined' unless attr where(attr => dates_range(dates)) end alias on created_on def today created_on(0) end def modified_on(dates) attr = ts_attribute_for_update raise 'No attrtibute for update defined' unless attr where(attr: dates_range(dates)) end end end #+END_SRC 然后在 =~/.arql.d/init.rb= 中引入这个文件: #+BEGIN_SRC ruby load(File.absolute_path(File.dirname(__FILE__) + "/date_range.rb")) #+END_SRC ** 使用方法 然后就可以使用这个方法了: #+BEGIN_SRC ruby Student.where(created_at: dates(0)). # 查询今天创建的 Student.where(created_at: dates(:today)). # 查询今天创建的 Student.where(created_at: dates('2016-01-01'..'2016-01-31')) # 查询 2016 年 1 月创建的 Student.where(created_at: dates('01'..'10')) # 查询当月 1-10 号创建的 Student.where(created_at: dates('03-01'..'04-10')) # 查询当年 3 月 1 日到 4 月 10 日创建的 Student.where(created_at: dates(-20..-1)) # 查询 20 天前到昨天创建的 #+END_SRC 如果是对 created_at 字段 (在 =init.yaml= 中通过 =created_at= 配置,默认值是 =created_at=) 进行查询, 可以直接使用: #+BEGIN_SRC ruby Student.on(0). # 查询今天创建的 Student.on(:today). # 查询今天创建的 Student.on('2016-01-01'..'2016-01-31') # 查询 2016 年 1 月创建的 Student.on('01'..'10') # 查询当月 1-10 号创建的 Student.on('03-01'..'04-10') # 查询当年 3 月 1 日到 4 月 10 日创建的 Student.on(-20..-1) # 查询 20 天前到昨天创建的 #+END_SRC