ruby-units 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG CHANGED
@@ -100,4 +100,13 @@ Change Log for Ruby-units
100
100
  * added Time.in("5 min")
101
101
  * added Unit.to_unit to simplify some calls
102
102
 
103
+ 2006-09-22 0.2.3 * added support for date/time parsing with the Chronic gem
104
+ parsing will use Chronic if it is loaded
105
+ * allows Date / Time / DateTime conversions
106
+ * better test coverage
107
+ * The 'string'.to_time returns a Time object
108
+ * 'string'.to_datetime returns a DateTime object
109
+ * 'string'.time returns a Time object or a DateTime if the Time object fails
110
+ * 'string'.datetime returns a DateTime or a Time if the DateTime fails
111
+
103
112
 
data/README CHANGED
@@ -1,6 +1,6 @@
1
1
  =Ruby Units
2
2
 
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
 
5
5
  Kevin C. Olbrich, Ph.D.
6
6
 
data/lib/ruby-units.rb CHANGED
@@ -2,7 +2,7 @@ require 'mathn'
2
2
  require 'rational'
3
3
  require 'date'
4
4
  require 'parsedate'
5
- # = Ruby Units 0.2.2
5
+ # = Ruby Units 0.2.3
6
6
  #
7
7
  # Copyright 2006 by Kevin C. Olbrich, Ph.D.
8
8
  #
@@ -135,7 +135,10 @@ class Unit < Numeric
135
135
  return true if @signature == 400 && @numerator.size == 1 && @numerator[0] =~ /(celcius|kelvin|farenheit|rankine)/
136
136
  n = @numerator + @denominator
137
137
  n.compact.each do |x|
138
- return false unless x == '<1>' || (@@UNIT_VALUES[Regexp.escape(x)] && @@UNIT_VALUES[Regexp.escape(x)][:denominator].nil? && @@UNIT_VALUES[Regexp.escape(x)][:numerator].include?(Regexp.escape(x)))
138
+ return false unless x == '<1>' ||
139
+ (@@UNIT_VALUES[Regexp.escape(x)] &&
140
+ @@UNIT_VALUES[Regexp.escape(x)][:denominator].nil? &&
141
+ @@UNIT_VALUES[Regexp.escape(x)][:numerator].include?(Regexp.escape(x)))
139
142
  end
140
143
  return true
141
144
  end
@@ -564,14 +567,14 @@ class Unit < Numeric
564
567
 
565
568
  # '5 min'.unit.ago
566
569
  def ago
567
- Time.now - self
570
+ Time.now - self rescue DateTime.now - self
568
571
  end
569
572
 
570
573
  # '5 min'.before(time)
571
574
  def before(time_point = ::Time.now)
572
575
  raise ArgumentError, "Must specify a Time" unless time_point
573
576
  if String === time_point
574
- Time.local(*ParseDate.parsedate(time_point))-self
577
+ time_point.time - self
575
578
  else
576
579
  time_point - self
577
580
  end
@@ -583,9 +586,10 @@ class Unit < Numeric
583
586
  case time_point
584
587
  when Time: (Time.now - time_point).unit('s').to(self)
585
588
  when DateTime: (DateTime.now - time_point).unit('d').to(self)
586
- when String: (Time.now - Time.local(*ParseDate.parsedate(time_point))).unit('s').to(self)
589
+ when String:
590
+ (DateTime.now - time_point.time(:context=>:past)).unit('d').to(self)
587
591
  else
588
- raise ArgumentError, "Must specify a Time" unless time_point
592
+ raise ArgumentError, "Must specify a Time, DateTime, or String"
589
593
  end
590
594
  end
591
595
 
@@ -594,9 +598,11 @@ class Unit < Numeric
594
598
  case time_point
595
599
  when Time: (time_point - Time.now).unit('s').to(self)
596
600
  when DateTime: (time_point - DateTime.now).unit('d').to(self)
597
- when String: (Time.local(*ParseDate.parsedate(time_point)) - Time.now).unit('s').to(self)
601
+ when String:
602
+ r = (time_point.time(:context=>:future) - DateTime.now)
603
+ Time === time_point.time ? r.unit('s').to(self) : r.unit('d').to(self)
598
604
  else
599
- raise ArgumentError, "Must specify a Time" unless time_point
605
+ raise ArgumentError, "Must specify a Time, DateTime, or String"
600
606
  end
601
607
  end
602
608
 
@@ -604,7 +610,7 @@ class Unit < Numeric
604
610
  def from(time_point = ::Time.now)
605
611
  raise ArgumentError, "Must specify a Time" unless time_point
606
612
  if String === time_point
607
- Time.local(*ParseDate.parsedate(time_point))+self
613
+ time_point.time + self
608
614
  else
609
615
  time_point + self
610
616
  end
@@ -659,7 +665,6 @@ class Unit < Numeric
659
665
  def replace_temperature
660
666
  return self unless self.signature == 400 && self.units =~ /temp(R|K|F|C)/
661
667
  un = $1
662
- puts "self:#{self} #{un}"
663
668
  target = self.units
664
669
  @numerator = case un
665
670
  when 'R' : ['<rankine>']
@@ -822,25 +827,15 @@ class Unit < Numeric
822
827
  end
823
828
  end
824
829
 
825
- # Need the 'Uncertain' gem for this to do anything helpful
826
- if defined? Uncertain
827
- class Uncertain
828
- def to_unit(other=nil)
829
- other ? Unit.new(self).to(other) : Unit.new(self)
830
- end
831
- alias :unit :to_unit
832
- alias :u :to_unit
833
- end
834
- end
835
-
836
830
 
837
831
  # Allow date objects to do offsets by a time unit
838
832
  # Date.today + U"1 week" => gives today+1 week
839
833
  class Date
840
834
  alias :unit_date_add :+
841
835
  def +(unit)
842
- if Unit === unit
843
- unit_date_add(unit.to('day').scalar)
836
+ case unit
837
+ when Unit: unit_date_add(unit.to('day').scalar)
838
+ when Time: unit_date_add(unit.to_datetime)
844
839
  else
845
840
  unit_date_add(unit)
846
841
  end
@@ -848,12 +843,24 @@ class Date
848
843
 
849
844
  alias :unit_date_sub :-
850
845
  def -(unit)
851
- if Unit === unit
852
- unit_date_sub(unit.to('day').scalar)
846
+ case unit
847
+ when Unit: unit_date_sub(unit.to('day').scalar)
848
+ when Time: unit_date_sub(unit.to_datetime)
853
849
  else
854
850
  unit_date_sub(unit)
855
851
  end
856
852
  end
853
+
854
+ def to_time
855
+ Time.local(*ParseDate.parsedate(self.to_s))
856
+ end
857
+
858
+ alias :units_datetime_inspect :inspect
859
+ def inspect(raw = false)
860
+ return self.units_datetime_inspect if raw
861
+ self.to_s
862
+ end
863
+
857
864
  end
858
865
 
859
866
  class Object
@@ -894,8 +901,9 @@ class String
894
901
 
895
902
  # format unit output using formating codes '%0.2f' % '1 mm'.unit => '1.00 mm'
896
903
  def %(*args)
897
- if Unit === args[0]
898
- args[0].to_s(self)
904
+ case args[0]
905
+ when Unit: args[0].to_s(self)
906
+ when Complex: args[0].to_s
899
907
  else
900
908
  unit_format(*args)
901
909
  end
@@ -927,6 +935,36 @@ class String
927
935
  def to(other)
928
936
  self.unit.to(other)
929
937
  end
938
+
939
+ def time(options = {})
940
+ self.to_time(options) rescue self.to_datetime(options)
941
+ end
942
+
943
+ def to_time(options = {})
944
+ begin
945
+ #raises exception when Chronic not defined or when it returns a nil (i.e., can't parse the input)
946
+ r = Chronic.parse(self,options)
947
+ raise(ArgumentError, 'Invalid Time String') unless r
948
+ return r
949
+ rescue
950
+ Time.local(*ParseDate.parsedate(self))
951
+ end
952
+ end
953
+
954
+ def to_datetime(options = {})
955
+ begin
956
+ # raises an exception if Chronic.parse = nil or if Chronic not defined
957
+ r = Chronic.parse(self,options).to_datetime
958
+ rescue
959
+ r=DateTime.civil(*ParseDate.parsedate(self)[0..5].compact)
960
+ end
961
+ raise RuntimeError, "Invalid Time String" if r == DateTime.new
962
+ return r
963
+ end
964
+
965
+ def datetime(options = {})
966
+ self.to_datetime(options) rescue self.to_time(options)
967
+ end
930
968
  end
931
969
 
932
970
 
@@ -951,9 +989,15 @@ class Time
951
989
  alias :unit :to_unit
952
990
  alias :u :to_unit
953
991
  alias :unit_add :+
992
+
993
+ def to_datetime
994
+ DateTime.civil(1970,1,1)+(self.to_f+self.gmt_offset)/86400
995
+ end
996
+
954
997
  def +(other)
955
- if Unit === other
956
- unit_add(other.to('s').scalar)
998
+ case other
999
+ when Unit: unit_add(other.to('s').scalar)
1000
+ when DateTime: unit_add(other.to_time)
957
1001
  else
958
1002
  unit_add(other)
959
1003
  end
@@ -965,8 +1009,9 @@ class Time
965
1009
 
966
1010
  alias :unit_sub :-
967
1011
  def -(other)
968
- if Unit === other
969
- unit_sub(other.to('s').scalar)
1012
+ case other
1013
+ when Unit: unit_sub(other.to('s').scalar)
1014
+ when DateTime: unit_sub(other.to_time)
970
1015
  else
971
1016
  unit_sub(other)
972
1017
  end
data/lib/units.rb CHANGED
@@ -101,10 +101,10 @@ UNIT_DEFINITIONS = {
101
101
  #time
102
102
  '<second>'=> [%w{s sec second seconds}, 1.0, :time, %w{<second>}],
103
103
  '<minute>'=> [%w{min minute minutes}, 60.0, :time, %w{<second>}],
104
- '<hour>'=> [%w{h hour hours}, 3600.0, :time, %w{<second>}],
104
+ '<hour>'=> [%w{h hr hrs hour hours}, 3600.0, :time, %w{<second>}],
105
105
  '<day>'=> [%w{d day days}, 3600*24, :time, %w{<second>}],
106
106
  '<week>'=> [%w{wk week weeks}, 7*3600*24, :time, %w{<second>}],
107
- '<fortnight>'=> [%w{fortnight}, 1209600, :time, %W{<second>}],
107
+ '<fortnight>'=> [%w{fortnight fortnights}, 1209600, :time, %W{<second>}],
108
108
  '<year>'=> [%w{y yr year years annum}, 31556926, :time, %w{<second>}],
109
109
  '<decade>'=>[%w{decade decades}, 315569260, :time, %w{<second>}],
110
110
  '<century>'=>[%w{century centuries}, 3155692600, :time, %w{<second>}],
@@ -3,6 +3,7 @@ require 'rubygems'
3
3
  require 'ruby-units'
4
4
  require 'yaml'
5
5
  require 'uncertain'
6
+ require 'chronic'
6
7
 
7
8
  class Unit < Numeric
8
9
  @@USER_DEFINITIONS = {'<inchworm>' => [%w{inworm inchworm}, 0.0254, :length, %w{<meter>} ],
@@ -11,7 +12,51 @@ class Unit < Numeric
11
12
  Unit.setup
12
13
  end
13
14
 
15
+ class Time
16
+ @@forced_now = nil
17
+ class << self
18
+ alias :unforced_now :now
19
+ def forced_now
20
+ return @@forced_now ? @@forced_now : unforced_now
21
+ end
22
+ alias :now :forced_now
23
+
24
+ def forced_now=(now)
25
+ @@forced_now = now
26
+ end
27
+
28
+ end
29
+ end
30
+
31
+ class DateTime
32
+ @@forced_now = nil
33
+ class << self
34
+ alias :unforced_now :now
35
+ def forced_now
36
+ return @@forced_now ? @@forced_now : unforced_now
37
+ end
38
+ alias :now :forced_now
39
+
40
+ def forced_now=(now)
41
+ @@forced_now = now
42
+ end
43
+
44
+ end
45
+ end
46
+
14
47
  class TestRubyUnits < Test::Unit::TestCase
48
+
49
+ def setup
50
+ @april_fools = Time.at 1143910800
51
+ @april_fools_datetime = DateTime.parse('2006-4-1 12:00')
52
+ Time.forced_now = @april_fools
53
+ DateTime.forced_now = @april_fools_datetime
54
+ end
55
+
56
+ def teardown
57
+ Time.forced_now = nil
58
+ DateTime.forced_now = nil
59
+ end
15
60
 
16
61
  def test_to_yaml
17
62
  unit = "1 mm".u
@@ -29,9 +74,44 @@ class TestRubyUnits < Test::Unit::TestCase
29
74
  assert_in_delta "1 h".unit + Time.now, "1 h".from_now, 1
30
75
  assert_in_delta Time.now - 3600, "1 h".before_now, 1
31
76
  assert_in_delta (Time.now.unit - Time.now).unit.scalar, 0, 1
32
-
33
77
  assert_equal "60 min", "min".until(Time.now + 3600).to_s
34
78
  assert_equal "01:00", "min".since(Time.now - 3600).to_s("%H:%M")
79
+ assert_in_delta Time.now, "now".time, 1
80
+ end
81
+
82
+ def test_time_helpers
83
+ assert_equal @april_fools, Time.now
84
+ assert_equal "1 day".from_now, @april_fools + 86400
85
+ assert_equal "1 day".from("now"), @april_fools + 86400
86
+ assert_equal "1 day".ago, @april_fools - 86400
87
+ assert_equal "1 day".before_now, @april_fools - 86400
88
+ assert_equal '1 days'.before('now'), @april_fools - 86400
89
+ assert_equal 'days'.since(@april_fools - 86400), "1 day".unit
90
+ assert_equal 'days'.since('3/31/06 12:00'), "1 day".unit
91
+ assert_equal 'days'.since(DateTime.parse('2006-3-31 12:00')), "1 day".unit
92
+ assert_equal 'days'.until('4/2/2006'), '1 day'.unit
93
+ assert_equal 'now'.time, Time.now
94
+ assert_equal 'now'.datetime, DateTime.now
95
+ assert_raises(ArgumentError) { 'days'.until(1) }
96
+ assert_raises(ArgumentError) { 'days'.since(1) }
97
+ assert_equal Unit.new(Time.now).scalar, 1143910800
98
+ assert_equal @april_fools.unit.to_time, @april_fools
99
+ assert_equal Time.in('1 day'), @april_fools + 86400
100
+ assert_equal @april_fools_datetime.inspect, "2006-04-01T12:00:00Z"
101
+ end
102
+
103
+ def test_string_helpers
104
+ assert_equal '1 mm'.to('in'), Unit('1 mm').to('in')
105
+ end
106
+
107
+ def test_math
108
+ pi = Math::PI
109
+ assert_equal Math.sin(pi), Math.sin("180 deg".unit)
110
+ assert_equal Math.cos(pi), Math.cos("180 deg".unit)
111
+ assert_equal Math.tan(pi), Math.tan("180 deg".unit)
112
+ assert_equal Math.sinh(pi), Math.sinh("180 deg".unit)
113
+ assert_equal Math.cosh(pi), Math.cosh("180 deg".unit)
114
+ assert_equal Math.tanh(pi), Math.tanh("180 deg".unit)
35
115
  end
36
116
 
37
117
  def test_clone
@@ -68,6 +148,7 @@ class TestRubyUnits < Test::Unit::TestCase
68
148
 
69
149
  def test_to_unit
70
150
  unit1 = "1 mm".to_unit
151
+ assert_equal unit1, unit1.to_unit
71
152
  assert Unit === unit1
72
153
  unit2 = Unit("1 mm")
73
154
  assert Unit === unit1
@@ -431,6 +512,11 @@ class TestRubyUnits < Test::Unit::TestCase
431
512
  end
432
513
 
433
514
  def test_temperature_conversions
515
+ assert_in_delta '0 tempC'.unit.scalar, '273.15 degK'.unit.scalar, 0.01
516
+ assert_in_delta '0 tempF'.unit.scalar, '255.37 degK'.unit.scalar, 0.01
517
+ assert_in_delta '0 tempK'.unit.scalar, '0 degK'.unit.scalar, 0.01
518
+ assert_in_delta '0 tempR'.unit.scalar, '0 degK'.unit.scalar, 0.01
519
+
434
520
  unit1 = Unit.new("37 degC")
435
521
  unit2 = unit1 >> "tempF"
436
522
  assert_in_delta 98.6, unit2.scalar, 0.1
@@ -615,4 +701,19 @@ class TestRubyUnits < Test::Unit::TestCase
615
701
  assert_equal "cells", a.units
616
702
  end
617
703
 
704
+ def test_uncertain
705
+ if defined? Uncertain
706
+ a = Uncertain.new(1,1)
707
+ b = a.unit('mm')
708
+ assert_equal b.to_s, '1 +/- 1 mm'
709
+ else
710
+ fail "Can't test Uncertain Units unless 'Uncertain' gem is installed"
711
+ end
712
+ end
713
+
714
+ def test_format
715
+ assert_equal "%0.2f" % "1 mm".unit, "1.00 mm"
716
+
717
+ end
718
+
618
719
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: ruby-units
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.2
7
- date: 2006-09-19 00:00:00 -04:00
6
+ version: 0.2.3
7
+ date: 2006-09-22 00:00:00 -04:00
8
8
  summary: A model that performs unit conversions and unit math
9
9
  require_paths:
10
10
  - lib