ruby-units 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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