My solution produces output like the following:

Mon:
    9 AM:  Brian
   10 AM:  Brian
   11 AM:  Brian
   12 PM:  Brian
    1 PM:  Brian
    2 PM:  Brian
    3 PM:  Brian
    4 PM:  Brian
    5 PM:  Brian
    6 PM:  Brian
Tue:
    9 AM:  Brian
   10 AM:  Brian
   11 AM:  Brian
   12 PM:  Brian
    1 PM:  Brian
    2 PM:  Brian
    3 PM:  Brian
    4 PM:  Brian
    5 PM:  Brian
    6 PM:  James
...
Sun:
    9 AM:  James
   10 AM:  James
   11 AM:  James
   12 PM:  James
    1 PM:  James
    2 PM:  James
    3 PM:  James
    4 PM:  James
    5 PM:  James
    6 PM:  Brian
    7 PM:  Brian
    8 PM:  Brian
    9 PM:  Brian
   10 PM:  Brian

This is a legal schedule, with everyone having shifts only when they  
are available.  On top of that, it tries to correct for preferred  
schedules as much as possible.

That's probably a bit of a problem, because you get schedules like  
Tuesday above, with one hour shifts.  To correct this, you probably  
need to set something like a minimum shift length and assign only in  
terms of those.  Exploration of such possibilities is left as an  
exercise to the reader...  :)

Here's the code:

#!/usr/local/bin/ruby -w

require "yaml"

# A representation of a single hour.  Usable in Ranges.
class Hour
     def initialize( text )
         @hour = case text
         when "12 AM"
             0
         when "12 PM"
             12
         when /(\d+) PM/
             $1.to_i + 12
         else
             text[/\d+/].to_i
         end
     end

     include Comparable

     def <=>( other )
         @hour <=> other.instance_eval { @hour }
     end

     def succ
         next_hour = Hour.new("12 AM")

         next_time = (@hour + 1) % 24
         next_hour.instance_eval { @hour = next_time }

         next_hour
     end

     def to_s
         str = case @hour
         when 0
             "12 AM"
         when 12
             "12 PM"
         when 13..23
             "#{@hour - 12} PM"
         else
             "#{@hour} AM"
         end
         "%5s" % str
     end
end

# An object for tracking a worker's availability and preferences.
class Worker
     def initialize( name )
         @name  = name

         @avail   = Hash.new
         @prefers = Hash.new
     end

     attr_reader :name

     def can_work( day, times )
         @avail[day] = parse_times(times)

         @prefers[day] = if times =~ /\((?:prefers )?([^)]+)\s*\)/
             parse_times($1)
         else
             Hour.new("12 AM")..Hour.new("11 PM")
         end
     end

     def available?( day, hour )
         if @avail[day].nil?
             false
         else
             @avail[day].include?(hour)
         end
     end

     def prefers?( day, hour )
         return false unless available? day, hour

         if @prefers[day].nil?
             false
         else
             @prefers[day].include?(hour)
         end
     end

     def ==( other )
         @name == other.name
     end

     def to_s
         @name.to_s
     end

     private

     def parse_times( times )
         case times
         when /^\s*any\b/i
             Hour.new("12 AM")..Hour.new("11 PM")
         when /^\s*before (\d+ [AP]M)\b/i
             Hour.new("12 AM")..Hour.new($1)
         when /^\s*after (\d+ [AP]M)\b/i
             Hour.new($1)..Hour.new("11 PM")
         when /^\s*(\d+ [AP]M) to (\d+ [AP]M)\b/i
             Hour.new($1)..Hour.new($2)
         when /^\s*not available\b/i
             nil
         else
             raise "Unexpected availability format."
         end
     end
end

if __FILE__ == $0
     unless ARGV.size == 1 and File.exists?(ARGV.first)
         puts "Usage:  #{File.basename($0)} SCHEDULE_FILE"
         exit
     end

     # load the data
     data = File.open(ARGV.shift) { |file| YAML.load(file) }

     # build worker list
     workers = Array.new
     data["Workers"].each do |name, avail|
         worker = Worker.new(name)
         avail.each { |day, times| worker.can_work(day, times) }
         workers << worker
     end

     # create a legal schedule, respecting availability
     schedule = Hash.new
     data["Schedule"].each do |day, times|
         schedule[day] = Array.new
         if times =~ /^\s*(\d+ [AP]M) to (\d+ [AP]M)\b/i
             start, finish = Hour.new($1), Hour.new($2)
         else
             raise "Unexpected schedule format."
         end

         (start..finish).each do |hour|
             started_with = workers.first
             loop do
                 if workers.first.available? day, hour
                     schedule[day] << [hour, workers.first]
                     break
                 else
                     workers << workers.shift
                     if workers.first == started_with
                         schedule[day] << [hour, "No workers  
available!"]
                         break
                     end
                 end
             end
         end
         workers << workers.shift
     end

     # make schedule swaps for preferred times
     schedule.each do |day, hours|
         hours.each_with_index do |(hour, worker), index|
             next unless worker.is_a?(Worker)
             unless worker.prefers?(day, hour)
                 alternate = workers.find { |w| w.prefers?(day, hour) }
                 hours[index][-1] = alternate unless alternate.nil?
             end
         end
     end

     # print schedule
     %w{Mon Tue Wed Thu Fri Sat Sun}.each do |day|
         puts "#{day}:"
         schedule[day].each do |hour, worker|
             puts "  #{hour}:  #{worker}"
         end
     end
end

__END__

James Edward Gray II