Date and Time

With no concept of time, our lives would be a mess. Without software programs to constantly manage and record this bizarre aspect of our universe…well, we might actually be better off. But why take the risk?

Some programs manage real-world time on behalf of the people who'd otherwise have to do it themselves: calendars, schedules, and data gatherers for scientific experiments. Other programs use the human concept of time for their own purposes: they may run experiments of their own, making decisions based on microsecond variations. Objects that have nothing to do with time are sometimes given timestamps recording when they were created or last modified. Of the basic data types, a time is the only one that directly corresponds to something in the real world.

Ruby supports the date and time interfaces you might be used to from other programming languages, but on top of them are Ruby-specific idioms that make programming easier. In this chapter, we'll show you how to use those interfaces and idioms, and how to fill in the gaps left by the language as it comes out of the box.

Ruby actually has two different time implementations. There's a set of time libraries written in C that have been around for decades. Like most modern programming languages, Ruby provides a native interface to these C libraries. The libraries are powerful, useful, and reliable, but they also have some significant shortcomings, so Ruby compensates with a second time library written in pure Ruby. The pure Ruby library isn't used for everything because it's slower than the C interface, and it lacks some of the features buried deep in the C library, such as the management of Daylight Saving Time.

The Time class contains Ruby's interface to the C libraries, and it's all you need for most applications. The Time class has a lot of Ruby idiom attached to it, but most of its methods have strange unRuby-like names like strftime and strptime. This is for the benefit of people who are already used to the C library, or one of its other interfaces (like Perl or Python's).

The internal representation of a Time object is a number of seconds before or since "time zero." Time zero for Ruby is the Unix epoch: the first second GMT of January 1, 1970. You can get the current local time with Time.now, or create a Time object from seconds-since-epoch with Time.at.

	Time.now # => Sat Mar 18 14:49:30 EST 2006
	Time.at(0) # => Wed Dec 31 19:00:00 EST 1969

This numeric internal representation of the time isn't very useful as a human-readable representation. You can get a string representation of a Time, as seen above, or call accessor methods to split up an instant of time according to how humans reckon time:

	t = Time.at(0)
	t.sec # => 0
	t.min # => 0
	t.hour # => 19
	t.day # => 31
	t.month # => 12
	t.year # => 1969
	t.wday # => 3 # Numeric day of week; Sunday
	is 0
	t.yday # => 365 # Numeric day of year
	t.isdst # => false # Is Daylight Saving Time in
	 # effect?
	t.zone # => "EST" # Time zone

See Recipe 3.3 for more human-readable ways of slicing and dicing Time objects.

Apart from the awkward method and member names, the biggest shortcoming of the Time class is that on a 32-bit system, its underlying implementation can't handle dates before December 1901 or after January 2037.[1]

[1] A system with a 64-bit time_t can represent a much wider range of times (about half a trillion years):

	Time.local(1865,4,9) # => Sun Apr 09 00:00:00 EWT 1865
	Time.local(2100,1,1) # => Fri Jan 01 00:00:00 EST 2100

You'll still get into trouble with older times, though, because Time doesn't handle calendrical reform. It'll also give time zones to times that predate the creation of time zones (EWT stands for Eastern War Time, an American timezone used during World War II).

	Time.local(1865, 4, 9)
	# ArgumentError: time out of range
	Time.local(2100, 1, 1)
	# ArgumentError: time out of range

To represent those times, you'll need to turn to Ruby's other time implementation: the Date and DateTime classes. You can probably use DateTime for everything, and not use Date at all:

	require 'date'
	DateTime.new(1865, 4, 9).to_s # => "1865-04-09T00:00:00Z"
	DateTime.new(2100, 1, 1).to_s # => "2100-01-01T00:00:00Z"

Recall that a Time object is stored as a fractional number of seconds since a "time zero" in 1970. The internal representation of a Date or DateTime object is a astronomical Julian date: a fractional number of days since a "time zero" in 4712 BCE, over 6,000 years ago.

	# Time zero for the date library:
	 
DateTime.new.to_s # => "-4712-01-01T00:00:00Z"

	# The current date and time:
	DateTime::now.to_s # => "2006-03-18T14:53:18-0500"

A DateTime object can precisely represent a time further in the past than the universe is old, or further in the future than the predicted lifetime of the universe. When DateTime handles historical dates, it needs to take into account the calendar reform movements that swept the Western world throughout the last 500 years. See Recipe 3.1 for more information on creating Date and DateTime objects.

Clearly DateTime is superior to Time for astronomical and historical applications, but you can use Time for most everyday programs. This table should give you a picture of the relative advantages of Time objects and DateTime objects.

Table 3-1.

 

Time

DateTime

Date range

19012037 on 32-bit systems

Effectively infinite

Handles Daylight Saving Time

Yes

No

Handles calendar reform

No

Yes

Time zone conversion

Easy with the tz gem

Difficult unless you only work with time zone offsets

Common time formats like RFC822

Built-in

Write them yourself

Speed

Faster

Slower

Both Time and DateTime objects support niceties like iteration and date arithmetic: you can basically treat them like numbers, because they're stored as numbers internally. But recall that a Time object is stored as a number of seconds, while a DateTime object is stored as a number of days, so the same operations will operate on different time scales on Time and DateTime objects. See Recipes 3.4 and 3.5 for more on this.

So far, we've talked about writing code to manage specific moments in time: a moment in the past or future, or right now. The other use of time is duration, the relationship between two times: "start" and "end," "before" and "after." You can measure duration by subtracting one DateTime object from another, or one Time object from another: you'll get a result measured in days or seconds (see Recipe 3.5). If you want your program to actually experience duration (the difference between now and a time in the future), you can put a thread to sleep for a certain amount of time: see Recipes 3.12 and 3.13.

You'll need duration most often, perhaps, during development. Benchmarking and profiling can measure how long your program took to run, and which parts of it took the longest. These topics are covered in Chapter 17: see Recipes 17.12 and 17.13.


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