date_range_covers 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
+ # development environment upon cd'ing into the directory
5
+
6
+ # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
+ # Only full ruby name is supported here, for short names use:
8
+ # echo "rvm use 1.8.7" > .rvmrc
9
+ environment_id="ruby-1.8.7-p370@date_range_covers"
10
+
11
+ # Uncomment the following lines if you want to verify rvm version per project
12
+ # rvmrc_rvm_version="1.18.20 (latest)" # 1.10.1 seams as a safe start
13
+ # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
+ # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
+ # return 1
16
+ # }
17
+
18
+ # First we attempt to load the desired environment directly from the environment
19
+ # file. This is very fast and efficient compared to running through the entire
20
+ # CLI and selector. If you want feedback on which environment was used then
21
+ # insert the word 'use' after --create as this triggers verbose mode.
22
+ if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
+ && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
+ then
25
+ \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
+ [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
27
+ \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
28
+ if [[ $- == *i* ]] # check for interactive shells
29
+ then echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
30
+ else echo "Using: $GEM_HOME" # don't use colors in non-interactive shells
31
+ fi
32
+ else
33
+ # If the environment file has not yet been created, use the RVM CLI to select.
34
+ rvm --create use "$environment_id" || {
35
+ echo "Failed to create RVM environment '${environment_id}'."
36
+ return 1
37
+ }
38
+ fi
39
+
40
+ # If you use bundler, this might be useful to you:
41
+ # if [[ -s Gemfile ]] && {
42
+ # ! builtin command -v bundle >/dev/null ||
43
+ # builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
44
+ # }
45
+ # then
46
+ # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
47
+ # gem install bundler
48
+ # fi
49
+ # if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
50
+ # then
51
+ # bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
52
+ # fi
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in date_range_covers.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Antall Fernandes
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # DateRangeCovers
2
+
3
+ Library to return months/weeks/days covered by a date range
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'date_range_covers'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install date_range_covers
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'date_range_covers/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "date_range_covers"
8
+ spec.version = DateRangeCovers::VERSION
9
+ spec.authors = ["Antall Fernandes"]
10
+ spec.email = ["antallfernandes@gmail.com"]
11
+ spec.description = %q{The gem helps find the years, months, weeks and days covered by a date range}
12
+ spec.summary = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "rails", "~> 2.3"
25
+ end
data/lib/a_date.rb ADDED
@@ -0,0 +1,26 @@
1
+ module ADate
2
+
3
+ @@DAYS_OF_WEEK = [
4
+ :sunday,
5
+ :monday,
6
+ :tuesday,
7
+ :wednesday,
8
+ :thursday,
9
+ :friday,
10
+ :saturday
11
+ ]
12
+
13
+ def self.beginning_of_week(date, beginning_of_week_day = :sunday)
14
+ beginning_of_week_adj = @@DAYS_OF_WEEK.index beginning_of_week_day
15
+
16
+ current_day_number = 0
17
+ current_day_number = date.wday if date.wday != 0
18
+ current_day_number = 7 if date.wday == 0 and beginning_of_week_adj == 1
19
+
20
+ date = date - current_day_number + beginning_of_week_adj
21
+ end
22
+
23
+ def self.end_of_week(date, beginning_of_week_day = :sunday)
24
+ beginning_of_week(date, beginning_of_week_day) + 6
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module DateRangeCovers
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,274 @@
1
+ require "date_range_covers/version"
2
+ require 'date'
3
+ require 'active_support'
4
+ require 'a_date'
5
+
6
+ module DateRangeCovers
7
+ class DateRange
8
+ attr_reader :start_date, :end_date
9
+ attr_reader :beginning_of_week_day
10
+ attr_reader :covs
11
+
12
+ def initialize(start_date, end_date, bow_day = :monday)
13
+ @start_date = start_date
14
+ @end_date = end_date
15
+ @beginning_of_week_day = bow_day
16
+ @covs = {}
17
+ end
18
+
19
+ # Is the date range created, a single date range
20
+ #
21
+ # @example
22
+ # boolean = DateRangeCovers::DateRange.is_single_date_range?
23
+ #
24
+ # @return boolean
25
+ # true: when start date and end date are the same
26
+ # false: when start date and end date are different or either date is nil
27
+ #
28
+ # @api public
29
+ def is_single_date_range?
30
+ (start_date and end_date and start_date == end_date) ? true : false
31
+ end
32
+
33
+ # Is the date range created an actual date range
34
+ #
35
+ # @example
36
+ # boolean = DateRangeCovers::DateRange.is_date_range?
37
+ #
38
+ # @return boolean
39
+ # true: when start date and end date are different
40
+ # false: when start date and end date are the same or either date is nil
41
+ #
42
+ # @api public
43
+ def is_date_range?
44
+ (start_date and end_date and !is_single_date_range?) ? true : false
45
+ end
46
+
47
+ # months_covered
48
+ #
49
+ # @example
50
+ # months = DateRangeCovers::DateRange.months_covered(options)
51
+ #
52
+ # @options
53
+ # :include
54
+ # => :start_month - include the month containing the start date
55
+ # => :end_month - include the month containing the end date
56
+ # => :both - include months containing start date and end date respectively
57
+ #
58
+ # @return [starting date of months]
59
+ #
60
+ # @api public
61
+ def months_covered(options={})
62
+ s_date = start_date.dup
63
+ e_date = end_date.dup
64
+
65
+ months = []
66
+
67
+ unless options.empty?
68
+ case options[:include]
69
+ when :both
70
+ months << s_date.beginning_of_month
71
+ months << e_date.beginning_of_month
72
+ when :start_month
73
+ months << s_date.beginning_of_month
74
+ when :end_month
75
+ months << e_date.beginning_of_month
76
+ end
77
+ end
78
+
79
+ #We need to iterate through every month and
80
+ #see if the month is wholly contained in the
81
+ #date range
82
+ bottom_of_month = s_date.beginning_of_month
83
+ top_of_month = s_date.end_of_month
84
+
85
+ while bottom_of_month < e_date do
86
+ if (s_date..e_date).include?(bottom_of_month..top_of_month)
87
+ months << bottom_of_month
88
+ end
89
+ bottom_of_month = bottom_of_month.next_month
90
+ top_of_month = bottom_of_month.end_of_month
91
+ end
92
+
93
+ covs[:months] = months.compact.uniq.sort
94
+ end
95
+
96
+ # Checks to see if part of a date range lies in a selected month
97
+ #
98
+ # @example
99
+ # boolean = DateRangeCovers::DateRange.date_range_flows_into_selected_months(start_date_of_range, end_date_of_range)
100
+ #
101
+ # @return boolean
102
+ #
103
+ # @api private
104
+ def date_range_flows_into_selected_months?(s_date, e_date)
105
+ if covs[:months]
106
+ covs[:months].each do |bottom_of_month|
107
+ top_of_month = bottom_of_month.end_of_month
108
+
109
+ #first condition :the month contains the start date and not the end date
110
+ #second condition :the month contains the end date and not the start date
111
+ if ((bottom_of_month..top_of_month).include?(s_date) and !(bottom_of_month..top_of_month).include?(e_date)) \
112
+ or ((bottom_of_month..top_of_month).include?(e_date) and !(bottom_of_month..top_of_month).include?(s_date))
113
+ return true #because the range spilled over the month return true
114
+ end
115
+ end
116
+ end
117
+ false
118
+ end
119
+
120
+ # weeks_covered
121
+ #
122
+ # @example
123
+ # weeks = DateRangeCovers::DateRange.weeks_covered(options)
124
+ #
125
+ # @options
126
+ # :include
127
+ # => :start_week - include the week containing the start date
128
+ # => :end_week - include the week containing the end date
129
+ # => :both - include weeks containing the start date and end date respectively
130
+ #
131
+ # @return [starting date of weeks]
132
+ #
133
+ # @api public
134
+ def weeks_covered(options={})
135
+ s_date = start_date.dup
136
+ e_date = end_date.dup
137
+
138
+ weeks = []
139
+
140
+ unless options.empty?
141
+ case options[:include]
142
+ when :both
143
+ s_date = ADate.beginning_of_week(s_date, beginning_of_week_day)
144
+ e_date = ADate.end_of_week(e_date, beginning_of_week_day)
145
+
146
+ start_of_e_week = ADate.beginning_of_week(e_date, beginning_of_week_day)
147
+
148
+ weeks << s_date unless is_covered?(s_date)
149
+ weeks << start_of_e_week unless is_covered?(start_of_e_week)
150
+
151
+ when :start_week
152
+ s_date = ADate.beginning_of_week(s_date, beginning_of_week_day)
153
+ weeks << s_date unless is_covered?(s_date)
154
+
155
+ when :end_week
156
+ e_date = ADate.end_of_week(e_date, beginning_of_week_day)
157
+ start_of_e_week = ADate.beginning_of_week(e_date, beginning_of_week_day)
158
+
159
+ weeks << start_of_e_week unless is_covered?(start_of_e_week)
160
+ end
161
+ end
162
+
163
+ #We need to iterate through every week and
164
+ #see if the week is wholly contained in the
165
+ #date range
166
+ bottom_of_week = ADate.beginning_of_week(s_date, beginning_of_week_day)
167
+ top_of_week = bottom_of_week + 6
168
+
169
+ while bottom_of_week < e_date do
170
+ if (s_date..e_date).include?(bottom_of_week..top_of_week)\
171
+ and !date_range_flows_into_selected_months?(bottom_of_week, top_of_week) # if part of a week already belongs to a covered month, we should not add that week to the list
172
+ weeks << bottom_of_week unless is_covered?(bottom_of_week)
173
+ end
174
+ bottom_of_week += 7
175
+ top_of_week = bottom_of_week + 6
176
+ end
177
+
178
+ covs[:weeks] = weeks.compact.uniq.sort
179
+ end
180
+
181
+ # dates_covered
182
+ #
183
+ # @example
184
+ # dates = DateRangeCovers::DateRange.dates_covered(options)
185
+ #
186
+ # @options
187
+ # :include
188
+ # => :start_date - include the start date
189
+ # => :end_date - include the end date
190
+ # => :both - include the start date and end date respectively
191
+ #
192
+ # @return [dates]
193
+ #
194
+ # @api public
195
+ def dates_covered(options={})
196
+ s_date = start_date.dup
197
+ e_date = end_date.dup
198
+
199
+ if is_single_date_range?
200
+ dates = handle_single_date_range(s_date, e_date, options)
201
+ else
202
+ dates = handle_date_range(s_date, e_date, options)
203
+ end
204
+
205
+ covs[:dates] = dates.compact.sort
206
+ end
207
+
208
+ def handle_single_date_range(s_date, e_date, options)
209
+ if options.empty?
210
+ s_date += 1
211
+ dates = (s_date...e_date).collect { |date| date unless is_covered?(date) }
212
+ else
213
+ dates = (s_date..e_date).collect { |date| date unless is_covered?(date) }
214
+ end
215
+
216
+ return dates
217
+ end
218
+
219
+ def handle_date_range(s_date, e_date, options)
220
+ case options[:include]
221
+ when :both
222
+ # do nothing
223
+ when :end_date
224
+ s_date += 1
225
+ when :start_date
226
+ e_date -= 1
227
+ else
228
+ s_date += 1
229
+ e_date -= 1
230
+ end
231
+
232
+ return (s_date..e_date).collect { |date| date unless is_covered?(date) }
233
+ end
234
+
235
+ def covers(cover_options = [:all])
236
+ if cover_options.include?(:all)
237
+ months_covered
238
+ weeks_covered
239
+ dates_covered(:include => :both)
240
+ else
241
+ months_covered if cover_options.include?(:months)
242
+ weeks_covered if cover_options.include?(:weeks)
243
+ dates_covered(:include => :both)
244
+ end
245
+
246
+ return covs
247
+ end
248
+
249
+ def is_covered?(dates)
250
+ is_covered = false
251
+
252
+ if !is_covered and covs[:months] and !covs[:months].empty?
253
+ start_month = covs[:months].first
254
+ end_month = covs[:months].last.end_of_month
255
+ is_covered = (start_month..end_month).include?(dates) ? true : false
256
+ end
257
+
258
+ if !is_covered and covs[:weeks] and !covs[:weeks].empty?
259
+ covs[:weeks].each do |week_date|
260
+ start_week = ADate.beginning_of_week(week_date, beginning_of_week_day)
261
+ end_week = ADate.end_of_week(week_date, beginning_of_week_day)
262
+ is_covered = (start_week..end_week).include?(dates) ? true : false
263
+ break if is_covered
264
+ end
265
+ end
266
+
267
+ is_covered
268
+ end
269
+
270
+ private :is_covered?, :date_range_flows_into_selected_months?
271
+ private :handle_single_date_range, :handle_date_range
272
+
273
+ end
274
+ end
@@ -0,0 +1,86 @@
1
+ require 'lib/a_date'
2
+ require 'date'
3
+
4
+ describe Date do
5
+ describe '#beginning_of_week and #end_of_week' do
6
+
7
+ context 'when beginning_of_week day is sunday' do
8
+ context 'when date is a sunday' do
9
+ let(:date) { Date.parse('2013-03-03') }
10
+
11
+ it 'should return 2013-03-03' do
12
+ ADate.beginning_of_week(date).should == date
13
+ end
14
+
15
+ it 'should return 2013-03-09' do
16
+ ADate.end_of_week(date).should == Date.parse('2013-03-09')
17
+ end
18
+ end
19
+
20
+ context 'when date is a monday' do
21
+ let(:date) { Date.parse('2013-03-04') }
22
+
23
+ it 'should return 2013-03-03' do
24
+ ADate.beginning_of_week(date).should == Date.parse('2013-03-03')
25
+ end
26
+
27
+ it 'should return 2013-03-09' do
28
+ ADate.end_of_week(date).should == Date.parse('2013-03-09')
29
+ end
30
+ end
31
+
32
+ context 'when date is a saturday' do
33
+ let(:date) { Date.parse('2013-03-09') }
34
+
35
+ it 'should return 2013-03-03' do
36
+ ADate.beginning_of_week(date).should == Date.parse('2013-03-03')
37
+ end
38
+
39
+ it 'should return 2013-03-09' do
40
+ ADate.end_of_week(date).should == Date.parse('2013-03-09')
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'when beginning_of_week day is monday' do
46
+ context 'when date is a sunday' do
47
+ let(:date) { Date.parse('2013-03-03') }
48
+
49
+ it 'should return 2013-02-25' do
50
+ ADate.beginning_of_week(date, :monday).should == Date.parse('2013-02-25')
51
+ end
52
+
53
+ it 'should return 2013-03-03' do
54
+ ADate.end_of_week(date, :monday).should == Date.parse('2013-03-03')
55
+ end
56
+ end
57
+
58
+ context 'when date is a monday' do
59
+ let(:date) { Date.parse('2013-03-04') }
60
+
61
+ it 'should return 2013-03-04' do
62
+ ADate.beginning_of_week(date, :monday).should == Date.parse('2013-03-04')
63
+ end
64
+
65
+ it 'should return 2013-03-10' do
66
+ ADate.end_of_week(date, :monday).should == Date.parse('2013-03-10')
67
+ end
68
+ end
69
+
70
+ context 'when date is a saturday' do
71
+ let(:date) { Date.parse('2013-03-09') }
72
+
73
+ it 'should return 2013-03-04' do
74
+ ADate.beginning_of_week(date, :monday).should == Date.parse('2013-03-04')
75
+ end
76
+
77
+ it 'should return 2013-03-10' do
78
+ ADate.end_of_week(date, :monday).should == Date.parse('2013-03-10')
79
+ end
80
+ end
81
+ end
82
+
83
+ end
84
+ end
85
+
86
+