date_breakup 3.0.1 → 3.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21887235144f6659466a3e0d709417dab4bd9d9dd6f5686e036e01f8890d8e3d
4
- data.tar.gz: 7842cf3953652600cd749bd8a1b212d4967476f0aade5899dba0100ddb642ed2
3
+ metadata.gz: b0b112a6229d63cd8d490c5309d553c488533a819b09874b8f8019bf1c60f867
4
+ data.tar.gz: 57286315af767547d74f7efa1d346fbfb4785593a3d3eb47e7a35ebc2f7ccd8d
5
5
  SHA512:
6
- metadata.gz: 1b964d8d15cb315b57fef5109c2c013fdfbfe0f740d09eea092152ae02b29bda6dffb852cd77138d462ad99b3b9105f23b7b3e759119078ae5014d5e03045a87
7
- data.tar.gz: 37aaf14964872d38432d51135915065913224db2f82d9e813946b5bccfafaf4b8fbbe29d720d641ebd23b0fc77b889bf9f36c9169d5d6483e21038d1d5715b55
6
+ metadata.gz: f9d67f0444647971676cbfb76d2bce7ac11d4fd1dea4516d330bcf415a7b4384a94db13beb83571321947979c62ff349f21a283610f4dbf22ff530a78abbf0f1
7
+ data.tar.gz: 2124f3c66ce6f2bbdbe8bf4cb27554f9c9af746e78403c3c14fa45b23533ee398d3fd762adff3bd3fa8aeff94c1058bfdea193719b48e748408fd6d97007a132
@@ -0,0 +1,23 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "matcher": "Edit|Write",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "cd \"$(git rev-parse --show-toplevel)\" && file=$(jq -r '.tool_input.file_path // \"\"') && if echo \"$file\" | grep -qE '\\.rb$'; then bundle exec rubocop \"$file\" --format simple --no-color 2>/dev/null; fi",
10
+ "timeout": 30,
11
+ "statusMessage": "Running RuboCop..."
12
+ },
13
+ {
14
+ "type": "command",
15
+ "command": "cd \"$(git rev-parse --show-toplevel)\" && bundle exec rspec --format progress --no-color 2>/dev/null",
16
+ "timeout": 60,
17
+ "statusMessage": "Running tests..."
18
+ }
19
+ ]
20
+ }
21
+ ]
22
+ }
23
+ }
@@ -0,0 +1,16 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ runs-on: ubuntu-latest
6
+ strategy:
7
+ matrix:
8
+ ruby: ['3.3']
9
+ steps:
10
+ - uses: actions/checkout@v4
11
+ - uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: ${{ matrix.ruby }}
14
+ bundler-cache: true
15
+ - run: bundle exec rspec
16
+ - run: bundle exec rubocop
data/.gitignore CHANGED
@@ -11,4 +11,7 @@
11
11
  .rspec_status
12
12
 
13
13
  *.gem
14
- *.DS_STORE
14
+ *.DS_STORE
15
+
16
+ # Claude Code personal overrides (machine-specific)
17
+ .claude/settings.local.json
data/.mise.toml ADDED
@@ -0,0 +1,2 @@
1
+ [tools]
2
+ ruby = "3.3"
data/.rubocop.yml ADDED
@@ -0,0 +1,29 @@
1
+ AllCops:
2
+ NewCops: disable
3
+ TargetRubyVersion: 3.3
4
+ SuggestExtensions: false
5
+
6
+ Style/Documentation:
7
+ Enabled: false
8
+
9
+ Metrics/MethodLength:
10
+ Max: 30
11
+
12
+ Metrics/AbcSize:
13
+ Max: 40
14
+
15
+ Metrics/CyclomaticComplexity:
16
+ Max: 12
17
+
18
+ Metrics/PerceivedComplexity:
19
+ Max: 13
20
+
21
+ Metrics/BlockLength:
22
+ Exclude:
23
+ - 'spec/**/*'
24
+ - '*.gemspec'
25
+
26
+ Layout/LineLength:
27
+ Max: 120
28
+ Exclude:
29
+ - 'spec/**/*'
data/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [3.1.0] - 2026-05-10
9
+
10
+ ### Added
11
+ - `.between` now accepts `Date` and `Time` objects in addition to date strings.
12
+ - `ArgumentError` raised when `start_date` is after `end_date` (previously returned silently empty results).
13
+ - GitHub Actions CI replacing Travis CI.
14
+ - `.mise.toml` to pin Ruby version for consistent local development.
15
+ - RuboCop added as a development dependency with a project `.rubocop.yml`.
16
+
17
+ ### Changed
18
+ - Replaced `define_method` metaprogramming loop with four explicit public methods (`in_years`, `in_months`, `in_weeks`, `in_days`) backed by a shared private `build_breakdown` method. No change to public API behaviour.
19
+ - All result arrays are now method-local variables — calling any `in_*` method twice on the same instance no longer returns duplicated data.
20
+ - `frozen_string_literal: true` added to all Ruby files.
21
+ - Bundler constraint relaxed from `~> 2.2` to `~> 2.0`.
22
+
23
+ ### Removed
24
+ - Runtime dependency on `time_difference` and implicit dependency on ActiveSupport — replaced with pure stdlib `Date` arithmetic. The gem now has zero runtime dependencies.
25
+
26
+ ### Fixed
27
+ - `bundle install` previously failed because `time_difference` was required in `lib/date_breakup.rb` but never declared as a dependency.
28
+
29
+ ## [3.0.1] - 2021-10-01
30
+
31
+ ### Changed
32
+ - Dependency bump: `date` gem updated to `3.2.1`.
33
+
34
+ ## [3.0.0] - 2021-09-01
35
+
36
+ ### Changed
37
+ - Class renamed from `DateBreakup::Range` to `DateBreakup`.
38
+ - Methods renamed: `get_years` → `in_years`, `get_months` → `in_months`, `get_weeks` → `in_weeks`, `get_days` → `in_days`.
39
+ - Internal code generation made dynamic via `define_method`.
40
+
41
+ ## [1.0.0] - 2019-01-01
42
+
43
+ ### Added
44
+ - Initial release. `DateBreakup::Range.between` with `get_years`, `get_months`, `get_weeks`, `get_days`.
data/CLAUDE.md ADDED
@@ -0,0 +1,39 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ bin/setup
10
+
11
+ # Run all tests
12
+ bundle exec rake spec
13
+ # or
14
+ bundle exec rspec
15
+
16
+ # Run a single spec file
17
+ bundle exec rspec spec/date_breakup_spec.rb
18
+
19
+ # Interactive console (loads the gem)
20
+ bin/console
21
+
22
+ # Install gem locally
23
+ bundle exec rake install
24
+
25
+ # Release gem (bumps version tag, pushes to RubyGems)
26
+ bundle exec rake release
27
+ ```
28
+
29
+ ## Architecture
30
+
31
+ This is a single-file Ruby gem (`lib/date_breakup.rb`). The entire logic lives in the `DateBreakup` class.
32
+
33
+ **Entry point:** `DateBreakup.between(start_date, end_date)` — accepts `Date`, `Time`, or date strings; returns a `DateBreakup` instance. Raises `ArgumentError` if start is after end.
34
+
35
+ **Core pattern:** The four public methods (`in_years`, `in_months`, `in_weeks`, `in_days`) are all generated dynamically via `define_method` in a single `%w[years months weeks days].each` loop. Each method walks day-by-day from `@start_date` to `@end_date`, greedily consuming the largest possible unit (year > month > week > day) at each step. The `unit` variable in the loop controls which units are eligible — `in_days` only ever appends to `days`; `in_weeks` can use weeks and days; `in_months` can use months, weeks, and days; `in_years` can use all four.
36
+
37
+ **Return shape:** All four methods return the same hash `{ years: [], months: [], weeks: [], days: [] }` — higher-granularity methods simply leave the coarser arrays empty.
38
+
39
+ **Dependencies:** Zero runtime dependencies — uses only stdlib `require 'date'`.
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
data/Gemfile.lock CHANGED
@@ -1,20 +1,30 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- date_breakup (3.0.1)
5
- date (~> 3.1, >= 3.1.1)
4
+ date_breakup (3.1.0)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
9
8
  specs:
9
+ ast (2.4.3)
10
10
  coderay (1.1.3)
11
- date (3.1.1)
12
11
  diff-lcs (1.4.4)
12
+ json (2.19.5)
13
+ language_server-protocol (3.17.0.5)
14
+ lint_roller (1.1.0)
13
15
  method_source (1.0.0)
16
+ parallel (2.1.0)
17
+ parser (3.3.11.1)
18
+ ast (~> 2.4.1)
19
+ racc
20
+ prism (1.9.0)
14
21
  pry (0.14.1)
15
22
  coderay (~> 1.1)
16
23
  method_source (~> 1.0)
24
+ racc (1.8.1)
25
+ rainbow (3.1.1)
17
26
  rake (13.0.3)
27
+ regexp_parser (2.12.0)
18
28
  rspec (3.10.0)
19
29
  rspec-core (~> 3.10.0)
20
30
  rspec-expectations (~> 3.10.0)
@@ -28,16 +38,37 @@ GEM
28
38
  diff-lcs (>= 1.2.0, < 2.0)
29
39
  rspec-support (~> 3.10.0)
30
40
  rspec-support (3.10.2)
41
+ rubocop (1.86.1)
42
+ json (~> 2.3)
43
+ language_server-protocol (~> 3.17.0.2)
44
+ lint_roller (~> 1.1.0)
45
+ parallel (>= 1.10)
46
+ parser (>= 3.3.0.2)
47
+ rainbow (>= 2.2.2, < 4.0)
48
+ regexp_parser (>= 2.9.3, < 3.0)
49
+ rubocop-ast (>= 1.49.0, < 2.0)
50
+ ruby-progressbar (~> 1.7)
51
+ unicode-display_width (>= 2.4.0, < 4.0)
52
+ rubocop-ast (1.49.1)
53
+ parser (>= 3.3.7.2)
54
+ prism (~> 1.7)
55
+ ruby-progressbar (1.13.0)
56
+ unicode-display_width (3.2.0)
57
+ unicode-emoji (~> 4.1)
58
+ unicode-emoji (4.2.0)
31
59
 
32
60
  PLATFORMS
61
+ arm64-darwin-23
33
62
  x86_64-darwin-20
63
+ x86_64-linux
34
64
 
35
65
  DEPENDENCIES
36
- bundler (~> 2.2, >= 2.2.20)
66
+ bundler (~> 2.0)
37
67
  date_breakup!
38
68
  pry (~> 0.14.1)
39
69
  rake (~> 13.0, >= 13.0.3)
40
70
  rspec (~> 3.10)
71
+ rubocop (~> 1.65)
41
72
 
42
73
  BUNDLED WITH
43
- 2.2.20
74
+ 2.7.2
data/README.md CHANGED
@@ -1,10 +1,6 @@
1
1
  # DateBreakup
2
- VERSION 3 is Live Now.
3
2
 
4
- What's New?
5
- - The code to generate data has been cleaned and made dynamic.
6
- - Dependencies has been updated.
7
- - The class name has changed and methods too. (Please refer the examples below)
3
+ Pass two dates and get a breakdown of the range grouped into years, months, weeks, and days. Accepts `Date`, `Time`, or date strings.
8
4
 
9
5
  ## Installation
10
6
 
@@ -14,163 +10,137 @@ Add this line to your application's Gemfile:
14
10
  gem 'date_breakup'
15
11
  ```
16
12
 
17
- And then execute:
13
+ Then run:
18
14
 
19
- $ bundle install
15
+ ```sh
16
+ bundle install
17
+ ```
20
18
 
21
- Or install it yourself as:
19
+ Or install directly:
22
20
 
23
- $ gem install date_breakup
21
+ ```sh
22
+ gem install date_breakup
23
+ ```
24
24
 
25
25
  ## Usage
26
26
 
27
- ### Pass in 2 Date(String) & get a breakup of dates in years, months, weeks & days
27
+ All four methods return the same hash shape `{ years:, months:, weeks:, days: }` — with coarser arrays left empty depending on the method used.
28
+
29
+ `.between` accepts `Date`, `Time`, or a date string:
30
+
31
+ ```ruby
32
+ DateBreakup.between(Date.new(2019, 1, 1), Date.new(2019, 12, 31))
33
+ DateBreakup.between(Time.new(2019, 1, 1), Time.new(2019, 12, 31))
34
+ DateBreakup.between('01/01/2019', '31/12/2019')
35
+ ```
36
+
37
+ Passing a start date after the end date raises `ArgumentError`.
38
+
39
+ ### `in_years` — greedy breakdown from years down
28
40
 
29
- #### FOR YEARS (BREAKUP in YEARS, MONTHS, WEEKS, DAYS)
30
41
  ```ruby
31
- # Was: DateBreakup::Range.between('01/01/2019', '31/12/2019').get_years
32
42
  DateBreakup.between('01/01/2019', '31/12/2019').in_years
33
- => {
34
- :years=>[
35
- {:year=>2019, :start_date=>Sat, 01 Jan 2019, :end_date=>Sat, 31 Dec 2019}
36
- ],
37
- :months=>[],
38
- :weeks=>[],
39
- :days=>[]
40
- }
43
+ # => {
44
+ # years: [{ year: 2019, start_date: Sat 01 Jan 2019, end_date: Sat 31 Dec 2019 }],
45
+ # months: [],
46
+ # weeks: [],
47
+ # days: []
48
+ # }
41
49
  ```
42
50
 
43
- #### FOR MONTHS (BREAKUP in MONTHS, WEEKS, DAYS)
51
+ For a range that doesn't align to full years, the remainder falls through to months, weeks, and days:
52
+
44
53
  ```ruby
45
- # Was: DateBreakup::Range.between('01/01/2019', '31/12/2019').get_months
46
- DateBreakup.between('01/01/2019', '31/12/2019').in_months
47
- => {
48
- :years=>[],
49
- :months=>[
50
- {:month=>1, :year=>2019, :start_date=>Tue, 01 Jan 2019, :end_date=>Thu, 31 Jan 2019},
51
- {:month=>2, :year=>2019, :start_date=>Fri, 01 Feb 2019, :end_date=>Thu, 28 Feb 2019},
52
- {:month=>3, :year=>2019, :start_date=>Fri, 01 Mar 2019, :end_date=>Sun, 31 Mar 2019},
53
- {:month=>4, :year=>2019, :start_date=>Mon, 01 Apr 2019, :end_date=>Tue, 30 Apr 2019},
54
- {:month=>5, :year=>2019, :start_date=>Wed, 01 May 2019, :end_date=>Fri, 31 May 2019},
55
- {:month=>6, :year=>2019, :start_date=>Sat, 01 Jun 2019, :end_date=>Sun, 30 Jun 2019},
56
- {:month=>7, :year=>2019, :start_date=>Mon, 01 Jul 2019, :end_date=>Wed, 31 Jul 2019},
57
- {:month=>8, :year=>2019, :start_date=>Thu, 01 Aug 2019, :end_date=>Sat, 31 Aug 2019},
58
- {:month=>9, :year=>2019, :start_date=>Sun, 01 Sep 2019, :end_date=>Mon, 30 Sep 2019},
59
- {:month=>10, :year=>2019, :start_date=>Tue, 01 Oct 2019, :end_date=>Thu, 31 Oct 2019},
60
- {:month=>11, :year=>2019, :start_date=>Fri, 01 Nov 2019, :end_date=>Sat, 30 Nov 2019},
61
- {:month=>12, :year=>2019, :start_date=>Sun, 01 Dec 2019, :end_date=>Tue, 31 Dec 2019}
62
- ],
63
- :weeks=>[],
64
- :days=>[]
65
- }
54
+ DateBreakup.between('10/10/2010', '12/12/2012').in_years
55
+ # => {
56
+ # years: [{ year: 2011, start_date: Sat 01 Jan 2011, end_date: Sat 31 Dec 2011 }],
57
+ # months: [
58
+ # { month: 11, year: 2010, start_date: Mon 01 Nov 2010, end_date: Tue 30 Nov 2010 },
59
+ # { month: 12, year: 2010, start_date: Wed 01 Dec 2010, end_date: Fri 31 Dec 2010 },
60
+ # ...
61
+ # ],
62
+ # weeks: [{ week: 41, month: 10, year: 2010, start_date: Mon 11 Oct 2010, end_date: Sun 17 Oct 2010 }, ...],
63
+ # days: [{ day: 283, month_day: 10, month: 10, year: 2010, start_date: Sun 10 Oct 2010, end_date: Sun 10 Oct 2010 }, ...]
64
+ # }
66
65
  ```
67
- #### FOR WEEKS (BREAKUP in WEEKS, DAYS)
66
+
67
+ ### `in_months` — greedy breakdown from months down
68
+
68
69
  ```ruby
69
- # Was: DateBreakup::Range.between('01/01/2019', '31/12/2019').get_weeks
70
- DateBreakup.between('01/01/2019', '31/12/2019').in_weeks
71
- => {
72
- :years=>[],
73
- :months=>[],
74
- :weeks=>[
75
- {:week=>2, :month=>1, :year=>2019, :start_date=>Mon, 07 Jan 2019, :end_date=>Sun, 13 Jan 2019},
76
- {:week=>3, :month=>1, :year=>2019, :start_date=>Mon, 14 Jan 2019, :end_date=>Sun, 20 Jan 2019},
77
- {:week=>4, :month=>1, :year=>2019, :start_date=>Mon, 21 Jan 2019, :end_date=>Sun, 27 Jan 2019},
78
- {:week=>5, :month=>1, :year=>2019, :start_date=>Mon, 28 Jan 2019, :end_date=>Sun, 03 Feb 2019},
79
- {:week=>6, :month=>2, :year=>2019, :start_date=>Mon, 04 Feb 2019, :end_date=>Sun, 10 Feb 2019},
80
- {:week=>7, :month=>2, :year=>2019, :start_date=>Mon, 11 Feb 2019, :end_date=>Sun, 17 Feb 2019},
81
- {:week=>8, :month=>2, :year=>2019, :start_date=>Mon, 18 Feb 2019, :end_date=>Sun, 24 Feb 2019},
82
- {:week=>9, :month=>2, :year=>2019, :start_date=>Mon, 25 Feb 2019, :end_date=>Sun, 03 Mar 2019},
83
- {:week=>10, :month=>3, :year=>2019, :start_date=>Mon, 04 Mar 2019, :end_date=>Sun, 10 Mar 2019},
84
- ...
85
- {:week=>43, :month=>10, :year=>2019, :start_date=>Mon, 21 Oct 2019, :end_date=>Sun, 27 Oct 2019},
86
- {:week=>44, :month=>10, :year=>2019, :start_date=>Mon, 28 Oct 2019, :end_date=>Sun, 03 Nov 2019},
87
- {:week=>45, :month=>11, :year=>2019, :start_date=>Mon, 04 Nov 2019, :end_date=>Sun, 10 Nov 2019},
88
- {:week=>46, :month=>11, :year=>2019, :start_date=>Mon, 11 Nov 2019, :end_date=>Sun, 17 Nov 2019},
89
- {:week=>47, :month=>11, :year=>2019, :start_date=>Mon, 18 Nov 2019, :end_date=>Sun, 24 Nov 2019},
90
- {:week=>48, :month=>11, :year=>2019, :start_date=>Mon, 25 Nov 2019, :end_date=>Sun, 01 Dec 2019},
91
- {:week=>49, :month=>12, :year=>2019, :start_date=>Mon, 02 Dec 2019, :end_date=>Sun, 08 Dec 2019},
92
- {:week=>50, :month=>12, :year=>2019, :start_date=>Mon, 09 Dec 2019, :end_date=>Sun, 15 Dec 2019},
93
- {:week=>51, :month=>12, :year=>2019, :start_date=>Mon, 16 Dec 2019, :end_date=>Sun, 22 Dec 2019},
94
- {:week=>52, :month=>12, :year=>2019, :start_date=>Mon, 23 Dec 2019, :end_date=>Sun, 29 Dec 2019}
95
- ],
96
- :days=>[
97
- {:day=>1, :month_day=>1, :month=>1, :year=>2019, :start_date=>Tue, 01 Jan 2019, :end_date=>Tue, 01 Jan 2019},
98
- {:day=>2, :month_day=>2, :month=>1, :year=>2019, :start_date=>Wed, 02 Jan 2019, :end_date=>Wed, 02 Jan 2019},
99
- {:day=>3, :month_day=>3, :month=>1, :year=>2019, :start_date=>Thu, 03 Jan 2019, :end_date=>Thu, 03 Jan 2019},
100
- {:day=>4, :month_day=>4, :month=>1, :year=>2019, :start_date=>Fri, 04 Jan 2019, :end_date=>Fri, 04 Jan 2019},
101
- {:day=>5, :month_day=>5, :month=>1, :year=>2019, :start_date=>Sat, 05 Jan 2019, :end_date=>Sat, 05 Jan 2019},
102
- {:day=>6, :month_day=>6, :month=>1, :year=>2019, :start_date=>Sun, 06 Jan 2019, :end_date=>Sun, 06 Jan 2019},
103
- {:day=>364, :month_day=>30, :month=>12, :year=>2019, :start_date=>Mon, 30 Dec 2019, :end_date=>Mon, 30 Dec 2019},
104
- {:day=>365, :month_day=>31, :month=>12, :year=>2019, :start_date=>Tue, 31 Dec 2019, :end_date=>Tue, 31 Dec 2019}
105
- ]
106
- }
70
+ DateBreakup.between('01/01/2019', '31/12/2019').in_months
71
+ # => {
72
+ # years: [],
73
+ # months: [
74
+ # { month: 1, year: 2019, start_date: Tue 01 Jan 2019, end_date: Thu 31 Jan 2019 },
75
+ # { month: 2, year: 2019, start_date: Fri 01 Feb 2019, end_date: Thu 28 Feb 2019 },
76
+ # ...
77
+ # { month: 12, year: 2019, start_date: Sun 01 Dec 2019, end_date: Tue 31 Dec 2019 }
78
+ # ],
79
+ # weeks: [],
80
+ # days: []
81
+ # }
107
82
  ```
108
- #### FOR DAYS (BREAKUP in DAYS)
83
+
84
+ ### `in_weeks` — greedy breakdown from weeks down
85
+
86
+ Partial weeks at the start or end of the range fall through to individual days.
87
+
109
88
  ```ruby
110
- # Was: DateBreakup::Range.between('01/01/2019', '31/12/2019').get_days
111
- DateBreakup.between('01/01/2019', '31/12/2019').in_days
112
- => {
113
- :years=>[],
114
- :months=>[],
115
- :weeks=>[],
116
- :days=>[
117
- {:day=>1, :month_day=>1, :month=>1, :year=>2019, :start_date=>Tue, 01 Jan 2019, :end_date=>Tue, 01 Jan 2019},
118
- {:day=>2, :month_day=>2, :month=>1, :year=>2019, :start_date=>Wed, 02 Jan 2019, :end_date=>Wed, 02 Jan 2019},
119
- {:day=>3, :month_day=>3, :month=>1, :year=>2019, :start_date=>Thu, 03 Jan 2019, :end_date=>Thu, 03 Jan 2019},
120
- {:day=>4, :month_day=>4, :month=>1, :year=>2019, :start_date=>Fri, 04 Jan 2019, :end_date=>Fri, 04 Jan 2019},
121
- {:day=>5, :month_day=>5, :month=>1, :year=>2019, :start_date=>Sat, 05 Jan 2019, :end_date=>Sat, 05 Jan 2019},
122
- {:day=>6, :month_day=>6, :month=>1, :year=>2019, :start_date=>Sun, 06 Jan 2019, :end_date=>Sun, 06 Jan 2019},
123
- ...
124
- {:day=>364, :month_day=>30, :month=>12, :year=>2019, :start_date=>Mon, 30 Dec 2019, :end_date=>Mon, 30 Dec 2019},
125
- {:day=>365, :month_day=>31, :month=>12, :year=>2019, :start_date=>Tue, 31 Dec 2019, :end_date=>Tue, 31 Dec 2019}
126
- ]
127
- }
89
+ DateBreakup.between('01/01/2019', '31/12/2019').in_weeks
90
+ # => {
91
+ # years: [],
92
+ # months: [],
93
+ # weeks: [
94
+ # { week: 2, month: 1, year: 2019, start_date: Mon 07 Jan 2019, end_date: Sun 13 Jan 2019 },
95
+ # ...
96
+ # { week: 52, month: 12, year: 2019, start_date: Mon 23 Dec 2019, end_date: Sun 29 Dec 2019 }
97
+ # ],
98
+ # days: [
99
+ # { day: 1, month_day: 1, month: 1, year: 2019, start_date: Tue 01 Jan 2019, end_date: Tue 01 Jan 2019 },
100
+ # ...
101
+ # ]
102
+ # }
128
103
  ```
129
104
 
130
- ### More Further Apart Dates
105
+ ### `in_days` every day individually
131
106
 
132
107
  ```ruby
133
- # Was: DateBreakup::Range.between('10/10/2010', '12/12/2012').get_years
134
- DateBreakup.between('10/10/2010', '12/12/2012').in_years
135
- => {
136
- :years=>[
137
- {:year=>2011, :start_date=>Sat, 01 Jan 2011, :end_date=>Sat, 31 Dec 2011}
138
- ],
139
- :months=>[
140
- {:month=>11, :year=>2010, :start_date=>Mon, 01 Nov 2010, :end_date=>Tue, 30 Nov 2010},
141
- {:month=>12, :year=>2010, :start_date=>Wed, 01 Dec 2010, :end_date=>Fri, 31 Dec 2010},
142
- {:month=>1, :year=>2012, :start_date=>Sun, 01 Jan 2012, :end_date=>Tue, 31 Jan 2012},
143
- {:month=>2, :year=>2012, :start_date=>Wed, 01 Feb 2012, :end_date=>Wed, 29 Feb 2012},
144
- {:month=>3, :year=>2012, :start_date=>Thu, 01 Mar 2012, :end_date=>Sat, 31 Mar 2012},
145
- {:month=>4, :year=>2012, :start_date=>Sun, 01 Apr 2012, :end_date=>Mon, 30 Apr 2012},
146
- {:month=>5, :year=>2012, :start_date=>Tue, 01 May 2012, :end_date=>Thu, 31 May 2012},
147
- {:month=>6, :year=>2012, :start_date=>Fri, 01 Jun 2012, :end_date=>Sat, 30 Jun 2012},
148
- {:month=>7, :year=>2012, :start_date=>Sun, 01 Jul 2012, :end_date=>Tue, 31 Jul 2012},
149
- {:month=>8, :year=>2012, :start_date=>Wed, 01 Aug 2012, :end_date=>Fri, 31 Aug 2012},
150
- {:month=>9, :year=>2012, :start_date=>Sat, 01 Sep 2012, :end_date=>Sun, 30 Sep 2012},
151
- {:month=>10, :year=>2012, :start_date=>Mon, 01 Oct 2012, :end_date=>Wed, 31 Oct 2012},
152
- {:month=>11, :year=>2012, :start_date=>Thu, 01 Nov 2012, :end_date=>Fri, 30 Nov 2012}],
153
- :weeks=>[
154
- {:week=>41, :month=>10, :year=>2010, :start_date=>Mon, 11 Oct 2010, :end_date=>Sun, 17 Oct 2010},
155
- {:week=>42, :month=>10, :year=>2010, :start_date=>Mon, 18 Oct 2010, :end_date=>Sun, 24 Oct 2010},
156
- {:week=>43, :month=>10, :year=>2010, :start_date=>Mon, 25 Oct 2010, :end_date=>Sun, 31 Oct 2010},
157
- {:week=>49, :month=>12, :year=>2012, :start_date=>Mon, 03 Dec 2012, :end_date=>Sun, 09 Dec 2012}],
158
- :days=>[
159
- {:day=>283, :month_day=>10, :month=>10, :year=>2010, :start_date=>Sun, 10 Oct 2010, :end_date=>Sun, 10 Oct 2010},
160
- {:day=>336, :month_day=>1, :month=>12, :year=>2012, :start_date=>Sat, 01 Dec 2012, :end_date=>Sat, 01 Dec 2012},
161
- {:day=>337, :month_day=>2, :month=>12, :year=>2012, :start_date=>Sun, 02 Dec 2012, :end_date=>Sun, 02 Dec 2012},
162
- {:day=>345, :month_day=>10, :month=>12, :year=>2012, :start_date=>Mon, 10 Dec 2012, :end_date=>Mon, 10 Dec 2012},
163
- {:day=>346, :month_day=>11, :month=>12, :year=>2012, :start_date=>Tue, 11 Dec 2012, :end_date=>Tue, 11 Dec 2012},
164
- {:day=>347, :month_day=>12, :month=>12, :year=>2012, :start_date=>Wed, 12 Dec 2012, :end_date=>Wed, 12 Dec 2012}
165
- ]
166
- }
108
+ DateBreakup.between('01/01/2019', '03/01/2019').in_days
109
+ # => {
110
+ # years: [],
111
+ # months: [],
112
+ # weeks: [],
113
+ # days: [
114
+ # { day: 1, month_day: 1, month: 1, year: 2019, start_date: Tue 01 Jan 2019, end_date: Tue 01 Jan 2019 },
115
+ # { day: 2, month_day: 2, month: 1, year: 2019, start_date: Wed 02 Jan 2019, end_date: Wed 02 Jan 2019 },
116
+ # { day: 3, month_day: 3, month: 1, year: 2019, start_date: Thu 03 Jan 2019, end_date: Thu 03 Jan 2019 }
117
+ # ]
118
+ # }
167
119
  ```
168
120
 
169
- <!-- ## Development
121
+ ## Development
170
122
 
171
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
123
+ This project uses [mise](https://mise.jdx.dev) to manage the Ruby version.
172
124
 
173
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). -->
125
+ ```sh
126
+ # Install the required Ruby version
127
+ mise install
128
+
129
+ # Install dependencies
130
+ bin/setup
131
+
132
+ # Run tests
133
+ bundle exec rspec
134
+
135
+ # Run a single spec file
136
+ bundle exec rspec spec/date_breakup_spec.rb
137
+
138
+ # Open an interactive console with the gem loaded
139
+ bin/console
140
+
141
+ # Install the gem locally
142
+ bundle exec rake install
143
+ ```
174
144
 
175
145
  ## Contributing
176
146
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
 
data/bin/console CHANGED
@@ -1,14 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'bundler/setup'
4
5
  require 'date_breakup'
6
+ require 'pry'
5
7
 
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require 'irb'
14
- IRB.start(__FILE__)
8
+ Pry.start
data/date_breakup.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path('lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'date_breakup'
@@ -6,7 +8,7 @@ Gem::Specification.new do |spec|
6
8
  spec.name = 'date_breakup'
7
9
  spec.version = DateBreakup::VERSION
8
10
  spec.authors = ['Huzaifa Saifuddin']
9
- spec.email = ['Huzaifa@Huzaifas-MacBook-Pro.local']
11
+ spec.email = ['huzaifasp18@gmail.com']
10
12
 
11
13
  spec.summary = 'Feed in 2 dates and get a breakup of dates grouped in years, months, weeks & days'
12
14
  spec.description = 'Feed in 2 dates and get a breakup of dates grouped in years, months, weeks & days'
@@ -30,11 +32,11 @@ Gem::Specification.new do |spec|
30
32
  spec.bindir = 'exe'
31
33
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
34
  spec.require_paths = ['lib']
35
+ spec.required_ruby_version = '>= 3.3'
33
36
 
34
- spec.add_development_dependency 'bundler', '~> 2.2', '>= 2.2.20'
37
+ spec.add_development_dependency 'bundler', '~> 2.0'
35
38
  spec.add_development_dependency 'pry', '~> 0.14.1'
36
39
  spec.add_development_dependency 'rake', '~> 13.0', '>= 13.0.3'
37
40
  spec.add_development_dependency 'rspec', '~> 3.10'
38
-
39
- spec.add_runtime_dependency 'date', '~> 3.1', '>= 3.1.1'
41
+ spec.add_development_dependency 'rubocop', '~> 1.65'
40
42
  end
data/lib/date_breakup.rb CHANGED
@@ -1,49 +1,66 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rubygems'
4
- require 'time_difference'
3
+ require 'date'
5
4
 
6
5
  class DateBreakup
7
- VERSION = '3.0.1'
6
+ VERSION = '3.1.0'
8
7
 
9
8
  def self.between(start_date, end_date)
10
- new(Date.parse(start_date), Date.parse(end_date))
9
+ new(coerce_to_date(start_date), coerce_to_date(end_date))
11
10
  end
12
11
 
13
12
  def initialize(start_date, end_date)
13
+ raise ArgumentError, 'start_date must be before or equal to end_date' if start_date > end_date
14
+
14
15
  @start_date = start_date
15
- @end_date = end_date
16
+ @end_date = end_date
17
+ end
18
+
19
+ def in_years = build_breakdown(years: true, months: true, weeks: true)
20
+ def in_months = build_breakdown(years: false, months: true, weeks: true)
21
+ def in_weeks = build_breakdown(years: false, months: false, weeks: true)
22
+ def in_days = build_breakdown(years: false, months: false, weeks: false)
16
23
 
17
- @years = []
18
- @months = []
19
- @weeks = []
20
- @days = []
24
+ private_class_method def self.coerce_to_date(value)
25
+ case value
26
+ when Date then value
27
+ when Time then value.to_date
28
+ when String then Date.parse(value)
29
+ else raise ArgumentError, "expected a Date, Time, or String, got #{value.class}"
30
+ end
21
31
  end
22
32
 
23
- # Methods
24
- # in_years, in_months, in_weeks, in_days
25
- %w[years months weeks days].each do |method|
26
- define_method "in_#{method}" do
27
- date = @start_date
28
- while date <= @end_date
29
- if ['years'].include?(method) && date.beginning_of_year == date && @end_date >= date.end_of_year
30
- @years << { year: date.year, start_date: date, end_date: date.end_of_year }
31
- date = date.end_of_year + 1.day
32
- elsif ['years', 'months'].include?(method) && date.beginning_of_month == date && @end_date >= date.end_of_month
33
- @months << { month: date.month, year: date.year, start_date: date, end_date: date.end_of_month }
34
- date = date.end_of_month + 1.day
35
- elsif ['years', 'months', 'weeks'].include?(method) && date.beginning_of_week == date && @end_date >= date.end_of_week
36
- @weeks << { week: date.cweek, month: date.month, year: date.year, start_date: date,
37
- end_date: date.end_of_week }
38
- date = date.end_of_week + 1.day
39
- else
40
- @days << { day: date.yday, month_day: date.mday, month: date.month, year: date.year, start_date: date,
41
- end_date: date }
42
- date += 1.day
43
- end
44
- end
33
+ private
45
34
 
46
- { years: @years, months: @months, weeks: @weeks, days: @days }
35
+ def build_breakdown(years: false, months: false, weeks: false)
36
+ y = []
37
+ m = []
38
+ w = []
39
+ d = []
40
+ date = @start_date
41
+
42
+ while date <= @end_date
43
+ year_end = Date.new(date.year, 12, 31)
44
+ month_end = Date.new(date.year, date.month, -1)
45
+ week_start = date - (date.cwday - 1)
46
+ week_end = date + (7 - date.cwday)
47
+
48
+ if years && date == Date.new(date.year, 1, 1) && @end_date >= year_end
49
+ y << { year: date.year, start_date: date, end_date: year_end }
50
+ date = year_end + 1
51
+ elsif months && date == Date.new(date.year, date.month, 1) && @end_date >= month_end
52
+ m << { month: date.month, year: date.year, start_date: date, end_date: month_end }
53
+ date = month_end + 1
54
+ elsif weeks && date == week_start && @end_date >= week_end
55
+ w << { week: date.cweek, month: date.month, year: date.year, start_date: date, end_date: week_end }
56
+ date = week_end + 1
57
+ else
58
+ d << { day: date.yday, month_day: date.mday, month: date.month, year: date.year,
59
+ start_date: date, end_date: date }
60
+ date += 1
61
+ end
47
62
  end
63
+
64
+ { years: y, months: m, weeks: w, days: d }
48
65
  end
49
66
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: date_breakup
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Huzaifa Saifuddin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-06-22 00:00:00.000000000 Z
11
+ date: 2026-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,20 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.2'
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 2.2.20
19
+ version: '2.0'
23
20
  type: :development
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
24
  - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: '2.2'
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: 2.2.20
26
+ version: '2.0'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: pry
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -79,36 +73,35 @@ dependencies:
79
73
  - !ruby/object:Gem::Version
80
74
  version: '3.10'
81
75
  - !ruby/object:Gem::Dependency
82
- name: date
76
+ name: rubocop
83
77
  requirement: !ruby/object:Gem::Requirement
84
78
  requirements:
85
79
  - - "~>"
86
80
  - !ruby/object:Gem::Version
87
- version: '3.1'
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- version: 3.1.1
91
- type: :runtime
81
+ version: '1.65'
82
+ type: :development
92
83
  prerelease: false
93
84
  version_requirements: !ruby/object:Gem::Requirement
94
85
  requirements:
95
86
  - - "~>"
96
87
  - !ruby/object:Gem::Version
97
- version: '3.1'
98
- - - ">="
99
- - !ruby/object:Gem::Version
100
- version: 3.1.1
88
+ version: '1.65'
101
89
  description: Feed in 2 dates and get a breakup of dates grouped in years, months,
102
90
  weeks & days
103
91
  email:
104
- - Huzaifa@Huzaifas-MacBook-Pro.local
92
+ - huzaifasp18@gmail.com
105
93
  executables: []
106
94
  extensions: []
107
95
  extra_rdoc_files: []
108
96
  files:
97
+ - ".claude/settings.json"
98
+ - ".github/workflows/ci.yml"
109
99
  - ".gitignore"
100
+ - ".mise.toml"
110
101
  - ".rspec"
111
- - ".travis.yml"
102
+ - ".rubocop.yml"
103
+ - CHANGELOG.md
104
+ - CLAUDE.md
112
105
  - Gemfile
113
106
  - Gemfile.lock
114
107
  - LICENSE.txt
@@ -131,14 +124,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
124
  requirements:
132
125
  - - ">="
133
126
  - !ruby/object:Gem::Version
134
- version: '0'
127
+ version: '3.3'
135
128
  required_rubygems_version: !ruby/object:Gem::Requirement
136
129
  requirements:
137
130
  - - ">="
138
131
  - !ruby/object:Gem::Version
139
132
  version: '0'
140
133
  requirements: []
141
- rubygems_version: 3.1.6
134
+ rubygems_version: 3.5.22
142
135
  signing_key:
143
136
  specification_version: 4
144
137
  summary: Feed in 2 dates and get a breakup of dates grouped in years, months, weeks
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.4.2
5
- before_install: gem install bundler -v 1.16.2