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 +4 -4
- data/.claude/settings.json +23 -0
- data/.github/workflows/ci.yml +16 -0
- data/.gitignore +4 -1
- data/.mise.toml +2 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +44 -0
- data/CLAUDE.md +39 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +36 -5
- data/README.md +108 -138
- data/Rakefile +2 -0
- data/bin/console +3 -9
- data/date_breakup.gemspec +6 -4
- data/lib/date_breakup.rb +49 -32
- metadata +17 -24
- data/.travis.yml +0 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b0b112a6229d63cd8d490c5309d553c488533a819b09874b8f8019bf1c60f867
|
|
4
|
+
data.tar.gz: 57286315af767547d74f7efa1d346fbfb4785593a3d3eb47e7a35ebc2f7ccd8d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
data/.mise.toml
ADDED
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
data/Gemfile.lock
CHANGED
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
date_breakup (3.0
|
|
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.
|
|
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
|
|
74
|
+
2.7.2
|
data/README.md
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
# DateBreakup
|
|
2
|
-
VERSION 3 is Live Now.
|
|
3
2
|
|
|
4
|
-
|
|
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
|
-
|
|
13
|
+
Then run:
|
|
18
14
|
|
|
19
|
-
|
|
15
|
+
```sh
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
20
18
|
|
|
21
|
-
Or install
|
|
19
|
+
Or install directly:
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
```sh
|
|
22
|
+
gem install date_breakup
|
|
23
|
+
```
|
|
24
24
|
|
|
25
25
|
## Usage
|
|
26
26
|
|
|
27
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
66
|
+
|
|
67
|
+
### `in_months` — greedy breakdown from months down
|
|
68
|
+
|
|
68
69
|
```ruby
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
###
|
|
105
|
+
### `in_days` — every day individually
|
|
131
106
|
|
|
132
107
|
```ruby
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
121
|
+
## Development
|
|
170
122
|
|
|
171
|
-
|
|
123
|
+
This project uses [mise](https://mise.jdx.dev) to manage the Ruby version.
|
|
172
124
|
|
|
173
|
-
|
|
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
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
|
-
|
|
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 = ['
|
|
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.
|
|
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 '
|
|
4
|
-
require 'time_difference'
|
|
3
|
+
require 'date'
|
|
5
4
|
|
|
6
5
|
class DateBreakup
|
|
7
|
-
VERSION = '3.0
|
|
6
|
+
VERSION = '3.1.0'
|
|
8
7
|
|
|
9
8
|
def self.between(start_date, end_date)
|
|
10
|
-
new(
|
|
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
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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.
|
|
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.
|
|
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:
|
|
76
|
+
name: rubocop
|
|
83
77
|
requirement: !ruby/object:Gem::Requirement
|
|
84
78
|
requirements:
|
|
85
79
|
- - "~>"
|
|
86
80
|
- !ruby/object:Gem::Version
|
|
87
|
-
version: '
|
|
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: '
|
|
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
|
-
-
|
|
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
|
-
- ".
|
|
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: '
|
|
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.
|
|
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
|