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 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
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ *.swp
3
+ fiscal_date-*.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,18 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ fiscal_date (0.1.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ minitest (4.4.0)
10
+ rake (10.0.3)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ fiscal_date!
17
+ minitest (>= 2.6.2)
18
+ rake
data/README.md ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :test
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs.push "lib"
7
+ t.pattern = "test/**/*_test.rb"
8
+ end
9
+
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative '../lib/fiscal_date'
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