date_range_covers 0.0.1

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/.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
+