Iterating Over Dates

Problem

Given a point in time, you want to get somewhere else.

Solution

All of Ruby's time objects can be used in ranges as though they were numbers. Date and DateTime objects iterate in increments of one day, and Time objects iterate in increments of one second:

	require 'date'
	(Date.new(1776, 7, 2)..Date.new(1776, 7, 4)).each { |x| puts x }
	# 1776-07-02
	# 1776-07-03
	# 1776-07-04

	span = DateTime.new(1776, 7, 2, 1, 30, 15)..DateTime.new(1776, 7, 4, 7, 0, 0)
	span.each { |x| puts x }
	# 1776-07-02T01:30:15Z
	# 1776-07-03T01:30:15Z
	# 1776-07-04T01:30:15Z

	(Time.at(100)..Time.at(102)).each { |x| puts x }
	# Wed Dec 31 19:01:40 EST 1969
	# Wed Dec 31 19:01:41 EST 1969
	# Wed Dec 31 19:01:42 EST 1969

Ruby's Date class defines step and upto, the same convenient iterator methods used by numbers:

	the_first = Date.new(2004, 1, 1)
	the_fifth = Date.new(2004, 1, 5)

	the_first.upto(the_fifth) { |x| puts x }
	# 2004-01-01
	# 2004-01-02
	# 2004-01-03
	# 2004-01-04
	# 2004-01-05

 

Discussion

Ruby date objects are stored internally as numbers, and a range of those objects is treated like a range of numbers. For Date and DateTime objects, the internal representation is the Julian day: iterating over a range of those objects adds one day at a time. For Time objects, the internal representation is the number of seconds since the Unix epoch: iterating over a range of Time objects adds one second at a time.

Time doesn't define the step and upto method, but it's simple to add them:

	class Time
	 def step(other_time, increment)
	 raise ArgumentError, "step can't be 0" if increment == 0
	 increasing = self < other_time
	 if (increasing && increment < 0) || (!increasing && increment > 0)
	 yield self
	 return
	 end
	 d = self
	 begin
	 yield d
	 d += increment
	 end while (increasing ? d <= other_time : d >= other_time)
	 end

	 def upto(other_time)
	 step(other_time, 1) { |x| yield x }
	 end
	 end

	 the_first = Time.local(2004, 1, 1)
	 the_second = Time.local(2004, 1, 2)
	 the_first.step(the_second, 60 * 60 * 6) { |x| puts x }
	 # Thu Jan 01 00:00:00 EST 2004
	 # Thu Jan 01 06:00:00 EST 2004
	 # Thu Jan 01 12:00:00 EST 2004
	 # Thu Jan 01 18:00:00 EST 2004
	 # Fri Jan 02 00:00:00 EST 2004

	 the_first.upto(the_first) { |x| puts x }
	 # Thu Jan 01 00:00:00 EST 2004

 

See Also

  • Recipe 2.15, "Generating a Sequence of Numbers"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net