timecop 0.5.2 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 681f52d846b808b06ff833f3fbfc21e3c7c4c1bf426baa03f101c0a2c3b79ce8
4
+ data.tar.gz: b95b6d4a3c357e7f11f046d82dad3badd81d4903c96ef2b8e77762f241eeb66e
5
+ SHA512:
6
+ metadata.gz: e62e48939fb924a78d59199222688ae7692a2f6649c16daac296375ac1e537fa1c85e6a34d1a5cbd64b209e643ffa08d180ce3d933905f91284ce4c819873c1e
7
+ data.tar.gz: f6f5ef32ee1bfadfd43a5a8899bb521873d61eafd2f242942df6e80a3ed11f80a170d074ada23a61eb3b92009eeaf3a1046b126c802dc9a1c014c77d32acb3ab
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2012 — Travis Jeffery, John Trupiano
3
+ Copyright (c) 2019 — Travis Jeffery, John Trupiano
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
data/README.markdown CHANGED
@@ -1,7 +1,7 @@
1
1
  # timecop
2
2
 
3
- - Source[http://github.com/jtrupiano/timecop]
4
- - Documentation[http://johntrupiano.rubyforge.org/timecop]
3
+ [![Gem Version](https://badge.fury.io/rb/timecop.svg)](https://rubygems.org/gems/timecop)
4
+ [![Build Status](https://github.com/travisjeffery/timecop/workflows/CI/badge.svg)](https://github.com/travisjeffery/timecop/actions?query=workflow%3ACI)
5
5
 
6
6
  ## DESCRIPTION
7
7
 
@@ -9,7 +9,7 @@ A gem providing "time travel" and "time freezing" capabilities, making it dead s
9
9
 
10
10
  ## INSTALL
11
11
 
12
- gem install timecop
12
+ `gem install timecop`
13
13
 
14
14
  ## FEATURES
15
15
 
@@ -40,11 +40,28 @@ Timecop.freeze(Date.today + 30) do
40
40
  end
41
41
  ```
42
42
 
43
+ You can mock the time for a set of tests easily via setup/teardown methods
44
+
45
+ ```ruby
46
+ describe "some set of tests to mock" do
47
+ before do
48
+ Timecop.freeze(Time.local(1990))
49
+ end
50
+
51
+ after do
52
+ Timecop.return
53
+ end
54
+
55
+ it "should do blah blah blah" do
56
+ end
57
+ end
58
+ ```
59
+
43
60
  Set the time for the test environment of a rails app -- this is particularly
44
61
  helpful if your whole application is time-sensitive. It allows you to build
45
62
  your test data at a single point in time, and to move in/out of that time as
46
63
  appropriate (within your tests)
47
-
64
+
48
65
  in config/environments/test.rb
49
66
 
50
67
  ```ruby
@@ -85,25 +102,43 @@ being able to simulate activity via subsequent calls to your application.
85
102
 
86
103
  ```ruby
87
104
  # seconds will now seem like hours
88
- Timecop.lens(3600)
105
+ Timecop.scale(3600)
89
106
  Time.now
90
107
  # => 2012-09-20 21:23:25 -0500
91
- # seconds later, hours have past it's gone from 9pm at night to 6am in the morning
108
+ # seconds later, hours have passed and it's gone from 9pm at night to 6am in the morning
92
109
  Time.now
93
110
  # => 2012-09-21 06:22:59 -0500
94
111
  ```
95
112
 
96
- ## REFERENCES
113
+ See [#42](https://github.com/travisjeffery/timecop/pull/42) for more information, thanks to Ken Mayer, David Holcomb, and Pivotal Labs.
114
+
115
+ ### Timecop.safe_mode
116
+
117
+ Safe mode forces you to use Timecop with the block syntax since it always puts time back the way it was. If you are running in safe mode and use Timecop without the block syntax `Timecop::SafeModeException` will be raised to tell the user they are not being safe.
118
+
119
+ ``` ruby
120
+ # turn on safe mode
121
+ Timecop.safe_mode = true
122
+
123
+ # check if you are in safe mode
124
+ Timecop.safe_mode?
125
+ # => true
126
+
127
+ # using method without block
128
+ Timecop.freeze
129
+ # => Timecop::SafeModeException: Safe mode is enabled, only calls passing a block are allowed.
130
+ ```
131
+
132
+ ### Rails v Ruby Date/Time libraries
133
+
134
+ Sometimes [Rails Date/Time methods don't play nicely with Ruby Date/Time methods.](https://rails.lighthouseapp.com/projects/8994/tickets/6410-dateyesterday-datetoday)
97
135
 
98
- * {0.3.4 release}[http://blog.smartlogicsolutions.com/2009/12/07/timecop-0-3-4-released/]
99
- * {0.3.0 release}[http://blog.smartlogicsolutions.com/2009/09/20/timecop-0-3-0-released/]
100
- * {0.2.0 release}[http://blog.smartlogicsolutions.com/2008/12/24/timecop-2-released-freeze-and-rebase-time-ruby/]
101
- * {0.1.0 release}[http://blog.smartlogicsolutions.com/2008/11/19/timecop-freeze-time-in-ruby-for-better-testing/]
136
+ Be careful mixing Ruby `Date.today` with Rails `Date.tomorrow` / `Date.yesterday` as things might break.
102
137
 
103
138
  ## Contribute
104
139
 
105
- timecop is maintained by [travisjeffery](http://github.com/travisjeffery), who
106
- you can also hit up on [Twitter](http://twitter.com/travisjeffery).
140
+ timecop is maintained by [travisjeffery](http://github.com/travisjeffery), and
141
+ was created by [jtrupiano](https://github.com/jtrupiano).
107
142
 
108
143
  Here's the most direct way to get your work merged into the project.
109
144
 
data/Rakefile CHANGED
@@ -3,8 +3,6 @@ require 'bundler/gem_tasks'
3
3
  require 'rake/testtask'
4
4
  require 'rdoc/task'
5
5
 
6
- $LOAD_PATH.unshift("lib")
7
-
8
6
  Rake::RDocTask.new do |rdoc|
9
7
  if File.exist?('VERSION')
10
8
  version = File.read('VERSION')
@@ -20,8 +18,16 @@ Rake::RDocTask.new do |rdoc|
20
18
  rdoc.rdoc_files.include('lib/**/*.rb')
21
19
  end
22
20
 
23
- task :test do
24
- system "cd test && ./run_tests.sh"
21
+ task :test do
22
+ failed = Dir["test/*_test.rb"].map do |test|
23
+ command = "ruby #{test}"
24
+ puts
25
+ puts command
26
+ command unless system(command)
27
+ end.compact
28
+ if failed.any?
29
+ abort "#{failed.count} Tests failed\n#{failed.join("\n")}"
30
+ end
25
31
  end
26
32
 
27
33
  desc 'Default: run tests'
@@ -1,85 +1,159 @@
1
+ require 'time'
2
+ require 'date'
1
3
 
2
4
  class Time #:nodoc:
3
5
  class << self
4
- # Time we are behaving as
5
6
  def mock_time
6
7
  mocked_time_stack_item = Timecop.top_stack_item
7
8
  mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.time(self)
8
9
  end
9
-
10
- # Alias the original now
10
+
11
11
  alias_method :now_without_mock_time, :now
12
12
 
13
- # Define now_with_mock_time
14
13
  def now_with_mock_time
15
14
  mock_time || now_without_mock_time
16
15
  end
17
-
18
- # Alias now to now_with_mock_time
16
+
19
17
  alias_method :now, :now_with_mock_time
20
18
 
21
19
  alias_method :new_without_mock_time, :new
22
20
 
23
- def new_with_mock_time *args
24
- begin
25
- raise ArgumentError.new if args.size <= 0
26
- new_without_mock_time *args
27
- rescue ArgumentError
28
- now
29
- end
21
+ def new_with_mock_time(*args)
22
+ args.size <= 0 ? now : new_without_mock_time(*args)
30
23
  end
31
24
 
25
+ ruby2_keywords :new_with_mock_time if Module.private_method_defined?(:ruby2_keywords)
26
+
32
27
  alias_method :new, :new_with_mock_time
33
28
  end
34
- end
35
-
36
- if Object.const_defined?(:Date) && Date.respond_to?(:today)
37
- class Date #:nodoc:
38
- class << self
39
- # Date we are behaving as
40
- def mock_date
41
- mocked_time_stack_item = Timecop.top_stack_item
42
- mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.date(self)
29
+ end
30
+
31
+ class Date #:nodoc:
32
+ class << self
33
+ def mock_date
34
+ mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.date(self)
35
+ end
36
+
37
+ alias_method :today_without_mock_date, :today
38
+
39
+ def today_with_mock_date
40
+ mock_date || today_without_mock_date
41
+ end
42
+
43
+ alias_method :today, :today_with_mock_date
44
+
45
+ alias_method :strptime_without_mock_date, :strptime
46
+
47
+ def strptime_with_mock_date(str = '-4712-01-01', fmt = '%F', start = Date::ITALY)
48
+ unless start == Date::ITALY
49
+ raise ArgumentError, "Timecop's #{self}::#{__method__} only " +
50
+ "supports Date::ITALY for the start argument."
43
51
  end
44
-
45
- # Alias the original today
46
- alias_method :today_without_mock_date, :today
47
-
48
- # Define today_with_mock_date
49
- def today_with_mock_date
50
- mock_date || today_without_mock_date
52
+
53
+ #If date is not valid the following line raises
54
+ Date.strptime_without_mock_date(str, fmt)
55
+
56
+ d = Date._strptime(str, fmt)
57
+ now = Time.now.to_date
58
+ year = d[:year] || now.year
59
+ mon = d[:mon] || now.mon
60
+ if d.keys == [:year]
61
+ Date.new(year)
62
+ elsif d[:mday]
63
+ Date.new(year, mon, d[:mday])
64
+ elsif d[:wday]
65
+ Date.new(year, mon, now.mday) + (d[:wday] - now.wday)
66
+ elsif d[:yday]
67
+ Date.new(year).next_day(d[:yday] - 1)
68
+ elsif d[:cwyear] && d[:cweek]
69
+ if d[:cwday]
70
+ Date.commercial(d[:cwyear], d[:cweek], d[:cwday])
71
+ else
72
+ Date.commercial(d[:cwyear], d[:cweek])
73
+ end
74
+ elsif d[:seconds]
75
+ Time.at(d[:seconds]).to_date
76
+ else
77
+ Date.new(year, mon)
51
78
  end
52
-
53
- # Alias today to today_with_mock_date
54
- alias_method :today, :today_with_mock_date
79
+ end
80
+
81
+ alias_method :strptime, :strptime_with_mock_date
82
+
83
+ def parse_with_mock_date(*args)
84
+ parsed_date = parse_without_mock_date(*args)
85
+ return parsed_date unless mocked_time_stack_item
86
+ date_hash = Date._parse(*args)
87
+
88
+ case
89
+ when date_hash[:year] && date_hash[:mon]
90
+ parsed_date
91
+ when date_hash[:mon] && date_hash[:mday]
92
+ Date.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
93
+ when date_hash[:mday]
94
+ Date.new(mocked_time_stack_item.year, mocked_time_stack_item.month, date_hash[:mday])
95
+ when date_hash[:wday]
96
+ closest_wday(date_hash[:wday])
97
+ else
98
+ parsed_date + mocked_time_stack_item.travel_offset_days
99
+ end
100
+ end
101
+
102
+ alias_method :parse_without_mock_date, :parse
103
+ alias_method :parse, :parse_with_mock_date
104
+
105
+ def mocked_time_stack_item
106
+ Timecop.top_stack_item
107
+ end
108
+
109
+ def closest_wday(wday)
110
+ today = Date.today
111
+ result = today - today.wday
112
+ result += 1 until wday == result.wday
113
+ result
55
114
  end
56
115
  end
57
116
  end
58
117
 
59
- if Object.const_defined?(:DateTime) && DateTime.respond_to?(:now)
60
- class DateTime #:nodoc:
61
- class << self
62
- # Time we are behaving as
63
- def mock_time
64
- mocked_time_stack_item = Timecop.top_stack_item
65
- mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.datetime(self)
66
- end
67
-
68
- # Fake alias :now_without_mock_time :now
69
- # It appears that the DateTime library itself references Time.now
70
- # for it's interpretation of now which caused
71
- # DateTime.now_without_mock_time to incorrectly return the frozen time.
72
- def now_without_mock_time
73
- Time.now_without_mock_time.send :to_datetime
74
- end
75
-
76
- # Define now_with_mock_time
77
- def now_with_mock_time
78
- mock_time || now_without_mock_time
118
+ class DateTime #:nodoc:
119
+ class << self
120
+ def mock_time
121
+ mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.datetime(self)
122
+ end
123
+
124
+ def now_with_mock_time
125
+ mock_time || now_without_mock_time
126
+ end
127
+
128
+ alias_method :now_without_mock_time, :now
129
+
130
+ alias_method :now, :now_with_mock_time
131
+
132
+ def parse_with_mock_date(*args)
133
+ date_hash = Date._parse(*args)
134
+ parsed_date = parse_without_mock_date(*args)
135
+ return parsed_date unless mocked_time_stack_item
136
+ date_hash = DateTime._parse(*args)
137
+
138
+ case
139
+ when date_hash[:year] && date_hash[:mon]
140
+ parsed_date
141
+ when date_hash[:mon] && date_hash[:mday]
142
+ DateTime.new(mocked_time_stack_item.year, date_hash[:mon], date_hash[:mday])
143
+ when date_hash[:mday]
144
+ DateTime.new(mocked_time_stack_item.year, mocked_time_stack_item.month, date_hash[:mday])
145
+ when date_hash[:wday]
146
+ Date.closest_wday(date_hash[:wday]).to_datetime
147
+ else
148
+ parsed_date + mocked_time_stack_item.travel_offset_days
79
149
  end
150
+ end
151
+
152
+ alias_method :parse_without_mock_date, :parse
153
+ alias_method :parse, :parse_with_mock_date
80
154
 
81
- # Alias now to now_with_mock_time
82
- alias_method :now, :now_with_mock_time
155
+ def mocked_time_stack_item
156
+ Timecop.top_stack_item
83
157
  end
84
158
  end
85
159
  end
@@ -1,145 +1,140 @@
1
-
2
1
  class Timecop
3
2
  # A data class for carrying around "time movement" objects. Makes it easy to keep track of the time
4
3
  # movements on a simple stack.
5
4
  class TimeStackItem #:nodoc:
6
-
7
5
  attr_reader :mock_type
6
+
8
7
  def initialize(mock_type, *args)
9
8
  raise "Unknown mock_type #{mock_type}" unless [:freeze, :travel, :scale].include?(mock_type)
9
+ @travel_offset = @scaling_factor = nil
10
10
  @scaling_factor = args.shift if mock_type == :scale
11
11
  @mock_type = mock_type
12
12
  @time = parse_time(*args)
13
13
  @time_was = Time.now_without_mock_time
14
14
  @travel_offset = compute_travel_offset
15
- @dst_adjustment = compute_dst_adjustment(@time)
16
15
  end
17
-
16
+
18
17
  def year
19
18
  time.year
20
19
  end
21
-
20
+
22
21
  def month
23
22
  time.month
24
23
  end
25
-
24
+
26
25
  def day
27
26
  time.day
28
27
  end
29
-
28
+
30
29
  def hour
31
30
  time.hour
32
31
  end
33
-
32
+
34
33
  def min
35
34
  time.min
36
35
  end
37
-
36
+
38
37
  def sec
39
38
  time.sec
40
39
  end
41
-
40
+
42
41
  def utc_offset
43
42
  time.utc_offset
44
43
  end
45
-
44
+
46
45
  def travel_offset
47
- @travel_offset
46
+ @travel_offset unless mock_type == :freeze
47
+ end
48
+
49
+ def travel_offset_days
50
+ (@travel_offset / 60 / 60 / 24).round
48
51
  end
49
52
 
50
53
  def scaling_factor
51
54
  @scaling_factor
52
55
  end
53
-
54
- def time(time_klass=Time) #:nodoc:
56
+
57
+ def time(time_klass = Time) #:nodoc:
58
+ if @time.respond_to?(:in_time_zone)
59
+ time = time_klass.at(@time.dup.localtime)
60
+ else
61
+ time = time_klass.at(@time)
62
+ end
63
+
55
64
  if travel_offset.nil?
56
- time_klass.at( @time.to_f )
65
+ time
57
66
  elsif scaling_factor.nil?
58
- time_klass.at( ( Time.now_without_mock_time + travel_offset ).to_f )
67
+ time_klass.at(Time.now_without_mock_time + travel_offset)
59
68
  else
60
- time_klass.at( scaled_time )
69
+ time_klass.at(scaled_time)
61
70
  end
62
71
  end
63
72
 
64
73
  def scaled_time
65
74
  (@time + (Time.now_without_mock_time - @time_was) * scaling_factor).to_f
66
75
  end
67
-
68
- def date(date_klass=Date)
76
+
77
+ def date(date_klass = Date)
69
78
  date_klass.jd(time.__send__(:to_date).jd)
70
79
  end
71
-
72
- def datetime(datetime_klass=DateTime)
73
- # DateTime doesn't know about DST, so let's remove its presence
74
- our_offset = utc_offset + dst_adjustment
80
+
81
+ def datetime(datetime_klass = DateTime)
75
82
  if Float.method_defined?(:to_r)
76
83
  fractions_of_a_second = time.to_f % 1
77
- datetime_klass.new(year, month, day, hour, min, sec + fractions_of_a_second,
78
- utc_offset_to_rational(our_offset))
84
+ datetime_klass.new(year, month, day, hour, min, (fractions_of_a_second + sec), utc_offset_to_rational(utc_offset))
79
85
  else
80
- our_offset = utc_offset + dst_adjustment
81
- datetime_klass.new(year, month, day, hour, min, sec, utc_offset_to_rational(our_offset))
86
+ datetime_klass.new(year, month, day, hour, min, sec, utc_offset_to_rational(utc_offset))
82
87
  end
83
88
  end
84
-
85
- def dst_adjustment
86
- @dst_adjustment
87
- end
88
-
89
+
89
90
  private
90
- def rational_to_utc_offset(rational)
91
- ((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
92
- end
93
-
94
- def utc_offset_to_rational(utc_offset)
95
- Rational(utc_offset, 24 * 60 * 60)
96
- end
97
-
98
- def parse_time(*args)
99
- time_klass = Time.respond_to?(:zone) && Time.zone ? Time.zone : Time
100
- arg = args.shift
101
- if arg.is_a?(Time)
102
- if Timecop.active_support != false && arg.respond_to?(:in_time_zone)
103
- arg.in_time_zone
104
- else
105
- arg.getlocal
106
- end
107
- elsif Object.const_defined?(:DateTime) && arg.is_a?(DateTime)
108
- expected_time = time_klass.local(arg.year, arg.month, arg.day, arg.hour, arg.min, arg.sec)
109
- expected_time += expected_time.utc_offset - rational_to_utc_offset(arg.offset)
110
- expected_time + compute_dst_adjustment(expected_time)
111
- elsif Object.const_defined?(:Date) && arg.is_a?(Date)
112
- time_klass.local(arg.year, arg.month, arg.day, 0, 0, 0)
113
- elsif args.empty? && arg.kind_of?(Integer)
114
- Time.now + arg
115
- elsif arg.nil?
116
- Time.now
91
+
92
+ def rational_to_utc_offset(rational)
93
+ ((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
94
+ end
95
+
96
+ def utc_offset_to_rational(utc_offset)
97
+ Rational(utc_offset, 24 * 60 * 60)
98
+ end
99
+
100
+ def parse_time(*args)
101
+ arg = args.shift
102
+ if arg.is_a?(Time)
103
+ arg
104
+ elsif Object.const_defined?(:DateTime) && arg.is_a?(DateTime)
105
+ time_klass.at(arg.to_time.to_f).getlocal
106
+ elsif Object.const_defined?(:Date) && arg.is_a?(Date)
107
+ time_klass.local(arg.year, arg.month, arg.day, 0, 0, 0)
108
+ elsif args.empty? && (arg.kind_of?(Integer) || arg.kind_of?(Float))
109
+ time_klass.now + arg
110
+ elsif arg.nil?
111
+ time_klass.now
112
+ else
113
+ if arg.is_a?(String) && Time.respond_to?(:parse)
114
+ time_klass.parse(arg)
117
115
  else
118
- if arg.is_a?(String) && Timecop.active_support != false && Time.respond_to?(:parse)
119
- Time.parse(arg)
120
- else
121
- # we'll just assume it's a list of y/m/d/h/m/s
122
- year = arg || 2000
123
- month = args.shift || 1
124
- day = args.shift || 1
125
- hour = args.shift || 0
126
- minute = args.shift || 0
127
- second = args.shift || 0
128
- time_klass.local(year, month, day, hour, minute, second)
129
- end
116
+ # we'll just assume it's a list of y/m/d/h/m/s
117
+ year = arg || 2000
118
+ month = args.shift || 1
119
+ day = args.shift || 1
120
+ hour = args.shift || 0
121
+ minute = args.shift || 0
122
+ second = args.shift || 0
123
+ time_klass.local(year, month, day, hour, minute, second)
130
124
  end
131
125
  end
132
-
133
- def compute_dst_adjustment(time)
134
- return 0 if !(time.dst? ^ Time.now.dst?)
135
- return -1 * 60 * 60 if time.dst?
136
- return 60 * 60
137
- end
138
-
139
- def compute_travel_offset
140
- return nil if mock_type == :freeze
141
- time - Time.now_without_mock_time
142
- end
143
-
126
+ end
127
+
128
+ def compute_travel_offset
129
+ time - Time.now_without_mock_time
130
+ end
131
+
132
+ def times_are_equal_within_epsilon t1, t2, epsilon_in_seconds
133
+ (t1 - t2).abs < epsilon_in_seconds
134
+ end
135
+
136
+ def time_klass
137
+ Time.respond_to?(:zone) && Time.zone ? Time.zone : Time
138
+ end
144
139
  end
145
140
  end