fiscal_date 0.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.
- data/.gemspec +21 -0
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +18 -0
- data/README.md +0 -0
- data/Rakefile +9 -0
- data/lib/fiscal_date/arithmetic_ext.rb +34 -0
- data/lib/fiscal_date/comparable_ext.rb +18 -0
- data/lib/fiscal_date/date_ext.rb +37 -0
- data/lib/fiscal_date.rb +29 -0
- data/test/fiscal_date/arithmetic_ext_test.rb +39 -0
- data/test/fiscal_date/comparable_ext_test.rb +25 -0
- data/test/fiscal_date/date_ext_test.rb +37 -0
- data/test/fiscal_date_test.rb +35 -0
- data/test/test_helper.rb +3 -0
- metadata +97 -0
data/.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fiscal_date"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fiscal_date"
|
7
|
+
s.version = "0.1.0"
|
8
|
+
s.authors = ["Brian Smith"]
|
9
|
+
s.email = ["bsmith@swig505.com"]
|
10
|
+
s.homepage = "https://github.com/Estimize/fiscal_date"
|
11
|
+
s.summary = %q{Ruby gem for working with fiscal dates}
|
12
|
+
s.description = %q{Ruby gem for working with fiscal dates}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_development_dependency('rake')
|
20
|
+
s.add_development_dependency('minitest', [">= 2.6.2"])
|
21
|
+
end
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.md
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
class FiscalDate
|
2
|
+
module ArithmeticExt
|
3
|
+
|
4
|
+
def -(fd_or_integer)
|
5
|
+
case fd_or_integer
|
6
|
+
when Integer
|
7
|
+
self.+(-fd_or_integer)
|
8
|
+
when FiscalDate
|
9
|
+
year_diff = year - fd_or_integer.year
|
10
|
+
quarter_diff = quarter - fd_or_integer.quarter
|
11
|
+
year_diff * 4 + quarter_diff
|
12
|
+
else
|
13
|
+
raise(ArgumentError, "must pass an integer or another FiscalDate")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def +(quarters)
|
18
|
+
return self if quarters.zero?
|
19
|
+
|
20
|
+
years = quarters / 4
|
21
|
+
quarters = (quarters % 4) + self.quarter
|
22
|
+
|
23
|
+
# We can use modulo but we'd have to map the quarters 1-4 to 0-3,
|
24
|
+
# this seemed easier.
|
25
|
+
if quarters > 4
|
26
|
+
years += 1
|
27
|
+
quarters -= 4
|
28
|
+
end
|
29
|
+
|
30
|
+
self.class.new(self.year + years, quarters)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class FiscalDate
|
2
|
+
module ComparableExt
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.send(:include, Comparable)
|
6
|
+
end
|
7
|
+
|
8
|
+
def <=>(fd)
|
9
|
+
if year < fd.year || (year == fd.year && quarter < fd.quarter)
|
10
|
+
-1
|
11
|
+
elsif year > fd.year || (year == fd.year && quarter > fd.quarter)
|
12
|
+
1
|
13
|
+
else
|
14
|
+
0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class FiscalDate
|
2
|
+
class InconsistentCalendar < StandardError; end
|
3
|
+
|
4
|
+
module DateExt
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:extend, ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def by_quarter_date(quarter_end_date, year_end_month = 12)
|
12
|
+
quarter_end_month = quarter_end_date.month - 1
|
13
|
+
year_end_month = year_end_month - 1
|
14
|
+
|
15
|
+
fiscal_year = quarter_end_date.year
|
16
|
+
fiscal_quarter = nil
|
17
|
+
|
18
|
+
if year_end_month == quarter_end_month
|
19
|
+
fiscal_quarter = 4
|
20
|
+
elsif (year_end_month + 3) % 12 == quarter_end_month
|
21
|
+
fiscal_quarter = 1
|
22
|
+
elsif (year_end_month + 6) % 12 == quarter_end_month
|
23
|
+
fiscal_quarter = 2
|
24
|
+
elsif (year_end_month + 9) % 12 == quarter_end_month
|
25
|
+
fiscal_quarter = 3
|
26
|
+
else
|
27
|
+
raise(InconsistentCalendar, "quarter ends month #{quarter_end_month} and year ends month #{year_end_month}")
|
28
|
+
end
|
29
|
+
|
30
|
+
fiscal_year += 1 if quarter_end_month > year_end_month
|
31
|
+
|
32
|
+
return new(fiscal_year, fiscal_quarter)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/fiscal_date.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'fiscal_date/comparable_ext'
|
3
|
+
require 'fiscal_date/arithmetic_ext'
|
4
|
+
require 'fiscal_date/date_ext'
|
5
|
+
|
6
|
+
class FiscalDate
|
7
|
+
include ComparableExt
|
8
|
+
include ArithmeticExt
|
9
|
+
include DateExt
|
10
|
+
|
11
|
+
class InvalidQuarter < StandardError; end
|
12
|
+
class InvalidYear < StandardError; end
|
13
|
+
|
14
|
+
attr_reader :year, :quarter
|
15
|
+
|
16
|
+
VALID_QUARTERS = (1..4)
|
17
|
+
VALID_YEARS = { min: 1, max: 9999 }
|
18
|
+
|
19
|
+
def initialize(year, quarter)
|
20
|
+
@year, @quarter = year.to_i, quarter.to_i
|
21
|
+
raise(InvalidQuarter, "`#{quarter}` is not a valid quarter") unless VALID_QUARTERS.include?(@quarter)
|
22
|
+
raise(InvalidYear, "`#{year}` is not a valid year") unless VALID_YEARS[:min] < @year && VALID_YEARS[:max] > @year
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"Q#{quarter.to_s} #{year.to_s}"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
describe FiscalDate do
|
4
|
+
describe "#-" do
|
5
|
+
describe "when argument is FiscalDate" do
|
6
|
+
it "should return the difference in quarters for same year" do
|
7
|
+
(FiscalDate.new(2012, 4) - FiscalDate.new(2012, 2)).must_equal 2
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return the difference in quarters for different years" do
|
11
|
+
(FiscalDate.new(2012, 4) - FiscalDate.new(2010, 3)).must_equal 9
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return negative difference when appropriate" do
|
15
|
+
(FiscalDate.new(2012, 2) - FiscalDate.new(2012, 4)).must_equal -2
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "when argument is integer" do
|
20
|
+
it "should subtract quarters and return FiscalDate" do
|
21
|
+
(FiscalDate.new(2012,2) - 1).must_equal FiscalDate.new(2012,1)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should roll back to previous year when necessary" do
|
25
|
+
(FiscalDate.new(2012,2) - 2).must_equal FiscalDate.new(2011,4)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#+" do
|
31
|
+
it "should add quarters" do
|
32
|
+
(FiscalDate.new(2012,2) + 1).must_equal FiscalDate.new(2012,3)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should roll over to next year when necessary" do
|
36
|
+
(FiscalDate.new(2012,2) + 3).must_equal FiscalDate.new(2013,1)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
describe FiscalDate do
|
4
|
+
describe "#<=>" do
|
5
|
+
it "should return -1 when less than (same years)" do
|
6
|
+
(FiscalDate.new(2012,2) <=> FiscalDate.new(2012,3)).must_equal -1
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return -1 when less than (different years)" do
|
10
|
+
(FiscalDate.new(2011,4) <=> FiscalDate.new(2012,3)).must_equal -1
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return 0 when equal" do
|
14
|
+
(FiscalDate.new(2012,3) <=> FiscalDate.new(2012,3)).must_equal 0
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should return 1 when greater than (same years)" do
|
18
|
+
(FiscalDate.new(2012,4) <=> FiscalDate.new(2012,3)).must_equal 1
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should return 1 when greater than (different years)" do
|
22
|
+
(FiscalDate.new(2013,1) <=> FiscalDate.new(2012,3)).must_equal 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
describe FiscalDate do
|
4
|
+
describe ".from_quarter_date" do
|
5
|
+
describe "with default end of fiscal year (December)" do
|
6
|
+
it "should return Q4 for same year" do
|
7
|
+
FiscalDate.by_quarter_date(Date.new(2012,12,31)).must_equal FiscalDate.new(2012, 4)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should return Q1 for same year" do
|
11
|
+
FiscalDate.by_quarter_date(Date.new(2012,3,31)).must_equal FiscalDate.new(2012, 1)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "with specified end of fiscal year" do
|
16
|
+
it "should return Q4 for same year" do
|
17
|
+
FiscalDate.by_quarter_date(Date.new(2012,9,30), 9).must_equal FiscalDate.new(2012, 4)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should return Q1 for same year" do
|
21
|
+
FiscalDate.by_quarter_date(Date.new(2011,12,31), 9).must_equal FiscalDate.new(2012, 1)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return Q1 for following year" do
|
25
|
+
FiscalDate.by_quarter_date(Date.new(2012,12,31), 9).must_equal FiscalDate.new(2013, 1)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "with bogus calendar" do
|
30
|
+
it "should raise InconsistentCalendar" do
|
31
|
+
-> {
|
32
|
+
FiscalDate.by_quarter_date(Date.new(2012,11,30), 12)
|
33
|
+
}.must_raise(FiscalDate::InconsistentCalendar)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
describe FiscalDate do
|
4
|
+
let(:fiscal_date) { FiscalDate.new(2012, 1) }
|
5
|
+
|
6
|
+
it "should have year" do
|
7
|
+
fiscal_date.year.must_equal 2012
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have quarter" do
|
11
|
+
fiscal_date.quarter.must_equal 1
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should not allow quarters other than 1-4" do
|
15
|
+
["a",5,0].each do |q|
|
16
|
+
-> {
|
17
|
+
FiscalDate.new(2012, q)
|
18
|
+
}.must_raise(FiscalDate::InvalidQuarter)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should not allow years outside of 1-9999" do
|
23
|
+
["a",0,10000].each do |y|
|
24
|
+
-> {
|
25
|
+
FiscalDate.new(y, 1)
|
26
|
+
}.must_raise(FiscalDate::InvalidYear)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#to_s" do
|
31
|
+
it "should return Q<quarter> YYYY format" do
|
32
|
+
fiscal_date.to_s.must_equal "Q1 2012"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fiscal_date
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brian Smith
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.6.2
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.6.2
|
46
|
+
description: Ruby gem for working with fiscal dates
|
47
|
+
email:
|
48
|
+
- bsmith@swig505.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gemspec
|
54
|
+
- .gitignore
|
55
|
+
- Gemfile
|
56
|
+
- Gemfile.lock
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- lib/fiscal_date.rb
|
60
|
+
- lib/fiscal_date/arithmetic_ext.rb
|
61
|
+
- lib/fiscal_date/comparable_ext.rb
|
62
|
+
- lib/fiscal_date/date_ext.rb
|
63
|
+
- test/fiscal_date/arithmetic_ext_test.rb
|
64
|
+
- test/fiscal_date/comparable_ext_test.rb
|
65
|
+
- test/fiscal_date/date_ext_test.rb
|
66
|
+
- test/fiscal_date_test.rb
|
67
|
+
- test/test_helper.rb
|
68
|
+
homepage: https://github.com/Estimize/fiscal_date
|
69
|
+
licenses: []
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.8.23
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Ruby gem for working with fiscal dates
|
92
|
+
test_files:
|
93
|
+
- test/fiscal_date/arithmetic_ext_test.rb
|
94
|
+
- test/fiscal_date/comparable_ext_test.rb
|
95
|
+
- test/fiscal_date/date_ext_test.rb
|
96
|
+
- test/fiscal_date_test.rb
|
97
|
+
- test/test_helper.rb
|