diff --git a/lib/nori.rb b/lib/nori.rb index 7854a37..8abe667 100644 --- a/lib/nori.rb +++ b/lib/nori.rb @@ -1,6 +1,7 @@ require "nori/version" require "nori/core_ext" require "nori/xml_utility_node" +require "facets/date" class Nori @@ -19,11 +20,14 @@ def initialize(options = {}) :delete_namespace_attributes => false, :convert_tags_to => nil, :advanced_typecasting => true, - :parser => :nokogiri + :parser => :nokogiri, + :time_zone => nil } validate_options! defaults.keys, options.keys @options = defaults.merge(options) + + update_timezone(@options.delete(:time_zone)) end def find(hash, *path) @@ -46,6 +50,10 @@ def parse(xml) private + def update_timezone(tz) + ENV['TZ'] = tz if tz + end + def load_parser(parser) require "nori/parser/#{parser}" Parser.const_get PARSERS[parser] diff --git a/lib/nori/xml_utility_node.rb b/lib/nori/xml_utility_node.rb index 1b68946..cd6de47 100644 --- a/lib/nori/xml_utility_node.rb +++ b/lib/nori/xml_utility_node.rb @@ -72,9 +72,9 @@ def self.available_typecasts=(obj) self.typecasts = {} self.typecasts["integer"] = lambda { |v| v.nil? ? nil : v.to_i } self.typecasts["boolean"] = lambda { |v| v.nil? ? nil : (v.strip != "false") } - self.typecasts["datetime"] = lambda { |v| v.nil? ? nil : Time.parse(v).utc } + self.typecasts["datetime"] = lambda { |v| v.nil? ? nil : parse_time(v) } self.typecasts["date"] = lambda { |v| v.nil? ? nil : Date.parse(v) } - self.typecasts["dateTime"] = lambda { |v| v.nil? ? nil : Time.parse(v).utc } + self.typecasts["dateTime"] = lambda { |v| v.nil? ? nil : parse_time(v) } self.typecasts["decimal"] = lambda { |v| v.nil? ? nil : BigDecimal(v.to_s) } self.typecasts["double"] = lambda { |v| v.nil? ? nil : v.to_f } self.typecasts["float"] = lambda { |v| v.nil? ? nil : v.to_f } @@ -250,6 +250,14 @@ def to_html private + def self.parse_time(input) + if (input =~ /(((\+|\-)\d\d(:)?(\d\d)?)|Z)\Z/) + input.to_time + else + input.to_time(:local) + end + end + # TODO: replace REXML def unnormalize_xml_entities value REXML::Text.unnormalize(value) diff --git a/nori.gemspec b/nori.gemspec index 24d084b..a9c0a04 100644 --- a/nori.gemspec +++ b/nori.gemspec @@ -16,6 +16,8 @@ Gem::Specification.new do |s| s.add_development_dependency "rake", "~> 10.0" s.add_development_dependency "nokogiri", ">= 1.4.0" s.add_development_dependency "rspec", "~> 2.12" + s.add_development_dependency "pry" + s.add_dependency "facets" s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") diff --git a/spec/nori/nori_spec.rb b/spec/nori/nori_spec.rb index 7c84320..2e35515 100644 --- a/spec/nori/nori_spec.rb +++ b/spec/nori/nori_spec.rb @@ -7,6 +7,57 @@ let(:parser) { parser } + context "when parsing times" do + let(:time_in_utc) { Time.new(2013,06,01,11,00,01, '+00:00') } + + context "when timezone is given" do + it "it respects notation +01:00" do + xml = '' + parse(xml).should == { 'time' => time_in_utc } + end + + it "it respects notation +0100" do + xml = '' + parse(xml).should == { 'time' => time_in_utc } + end + + it "it respects notation +01" do + xml = '' + parse(xml).should == { 'time' => time_in_utc } + end + + it "it respects notation -01" do + xml = '' + parse(xml).should == { 'time' => time_in_utc } + end + + it "it respects notation Z" do + xml = '' + parse(xml).should == { 'time' => time_in_utc } + end + end + + context "when timezone is not given" do + before(:each) { ENV['TZ'] = 'Europe/Minsk' } # its +03:00, no DST :) + it "treats the input as local time" do + xml = '' + parse(xml).should == { 'time' => time_in_utc } + end + + it "accepts passing timezone via options" do + xml = '' + Nori.new(time_zone: 'Asia/Dushanbe') + ENV['TZ'].should == 'Asia/Dushanbe' + end + + it "treats unknown timezone as UTC" do + xml = '' + Nori.new(time_zone: 'Incorrect Timezone').parse(xml) + parse(xml).should == { 'time' => time_in_utc.utc } + end + end + end + it "should work with unnormalized characters" do xml = '&' parse(xml).should == { 'root' => "&" } diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dadf6cb..ab112a5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,2 +1,3 @@ require "bundler" +require 'pry' Bundler.require :default, :development