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