Hokkaido 0.0.3 → 0.0.4

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.
Files changed (84) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +2 -2
  3. data/ansiterm-color/.gitignore +3 -0
  4. data/ansiterm-color/CHANGES +28 -0
  5. data/ansiterm-color/COPYING +340 -0
  6. data/ansiterm-color/README +137 -0
  7. data/ansiterm-color/Rakefile +88 -0
  8. data/ansiterm-color/VERSION +1 -0
  9. data/ansiterm-color/bin/cdiff +19 -0
  10. data/ansiterm-color/bin/decolor +12 -0
  11. data/ansiterm-color/doc-main.txt +119 -0
  12. data/ansiterm-color/examples/example.rb +90 -0
  13. data/ansiterm-color/install.rb +15 -0
  14. data/ansiterm-color/lib/term/ansicolor/.keep +0 -0
  15. data/ansiterm-color/lib/term/ansicolor/version.rb +10 -0
  16. data/ansiterm-color/lib/term/ansicolor.rb +102 -0
  17. data/ansiterm-color/make_doc.rb +4 -0
  18. data/ansiterm-color/tests/ansicolor_test.rb +66 -0
  19. data/chronic/.gitignore +6 -0
  20. data/chronic/HISTORY.md +205 -0
  21. data/chronic/LICENSE +21 -0
  22. data/chronic/README.md +181 -0
  23. data/chronic/Rakefile +46 -0
  24. data/chronic/chronic.gemspec +20 -0
  25. data/chronic/lib/chronic/grabber.rb +33 -0
  26. data/chronic/lib/chronic/handler.rb +88 -0
  27. data/chronic/lib/chronic/handlers.rb +572 -0
  28. data/chronic/lib/chronic/mini_date.rb +38 -0
  29. data/chronic/lib/chronic/numerizer.rb +121 -0
  30. data/chronic/lib/chronic/ordinal.rb +47 -0
  31. data/chronic/lib/chronic/pointer.rb +32 -0
  32. data/chronic/lib/chronic/repeater.rb +145 -0
  33. data/chronic/lib/chronic/repeaters/repeater_day.rb +53 -0
  34. data/chronic/lib/chronic/repeaters/repeater_day_name.rb +52 -0
  35. data/chronic/lib/chronic/repeaters/repeater_day_portion.rb +108 -0
  36. data/chronic/lib/chronic/repeaters/repeater_fortnight.rb +71 -0
  37. data/chronic/lib/chronic/repeaters/repeater_hour.rb +58 -0
  38. data/chronic/lib/chronic/repeaters/repeater_minute.rb +58 -0
  39. data/chronic/lib/chronic/repeaters/repeater_month.rb +79 -0
  40. data/chronic/lib/chronic/repeaters/repeater_month_name.rb +94 -0
  41. data/chronic/lib/chronic/repeaters/repeater_season.rb +109 -0
  42. data/chronic/lib/chronic/repeaters/repeater_season_name.rb +43 -0
  43. data/chronic/lib/chronic/repeaters/repeater_second.rb +42 -0
  44. data/chronic/lib/chronic/repeaters/repeater_time.rb +128 -0
  45. data/chronic/lib/chronic/repeaters/repeater_week.rb +74 -0
  46. data/chronic/lib/chronic/repeaters/repeater_weekday.rb +85 -0
  47. data/chronic/lib/chronic/repeaters/repeater_weekend.rb +66 -0
  48. data/chronic/lib/chronic/repeaters/repeater_year.rb +77 -0
  49. data/chronic/lib/chronic/scalar.rb +116 -0
  50. data/chronic/lib/chronic/season.rb +26 -0
  51. data/chronic/lib/chronic/separator.rb +94 -0
  52. data/chronic/lib/chronic/span.rb +31 -0
  53. data/chronic/lib/chronic/tag.rb +36 -0
  54. data/chronic/lib/chronic/time_zone.rb +32 -0
  55. data/chronic/lib/chronic/token.rb +47 -0
  56. data/chronic/lib/chronic.rb +442 -0
  57. data/chronic/test/helper.rb +12 -0
  58. data/chronic/test/test_chronic.rb +150 -0
  59. data/chronic/test/test_daylight_savings.rb +118 -0
  60. data/chronic/test/test_handler.rb +104 -0
  61. data/chronic/test/test_mini_date.rb +32 -0
  62. data/chronic/test/test_numerizer.rb +72 -0
  63. data/chronic/test/test_parsing.rb +1061 -0
  64. data/chronic/test/test_repeater_day_name.rb +51 -0
  65. data/chronic/test/test_repeater_day_portion.rb +254 -0
  66. data/chronic/test/test_repeater_fortnight.rb +62 -0
  67. data/chronic/test/test_repeater_hour.rb +68 -0
  68. data/chronic/test/test_repeater_minute.rb +34 -0
  69. data/chronic/test/test_repeater_month.rb +50 -0
  70. data/chronic/test/test_repeater_month_name.rb +56 -0
  71. data/chronic/test/test_repeater_season.rb +40 -0
  72. data/chronic/test/test_repeater_time.rb +70 -0
  73. data/chronic/test/test_repeater_week.rb +62 -0
  74. data/chronic/test/test_repeater_weekday.rb +55 -0
  75. data/chronic/test/test_repeater_weekend.rb +74 -0
  76. data/chronic/test/test_repeater_year.rb +69 -0
  77. data/chronic/test/test_span.rb +23 -0
  78. data/chronic/test/test_token.rb +25 -0
  79. data/lib/Hokkaido/version.rb +1 -1
  80. data/lib/Hokkaido.rb +13 -9
  81. data/lib/gem_modifier.rb +23 -3
  82. data/lib/term/ansicolor.rb +4 -1
  83. data/spec/Hokkaido/port_spec.rb +15 -7
  84. metadata +78 -2
@@ -0,0 +1,121 @@
1
+ require 'strscan'
2
+
3
+ module Chronic
4
+ class Numerizer
5
+
6
+ DIRECT_NUMS = [
7
+ ['eleven', '11'],
8
+ ['twelve', '12'],
9
+ ['thirteen', '13'],
10
+ ['fourteen', '14'],
11
+ ['fifteen', '15'],
12
+ ['sixteen', '16'],
13
+ ['seventeen', '17'],
14
+ ['eighteen', '18'],
15
+ ['nineteen', '19'],
16
+ ['ninteen', '19'], # Common mis-spelling
17
+ ['zero', '0'],
18
+ ['one', '1'],
19
+ ['two', '2'],
20
+ ['three', '3'],
21
+ ['four(\W|$)', '4\1'], # The weird regex is so that it matches four but not fourty
22
+ ['five', '5'],
23
+ ['six(\W|$)', '6\1'],
24
+ ['seven(\W|$)', '7\1'],
25
+ ['eight(\W|$)', '8\1'],
26
+ ['nine(\W|$)', '9\1'],
27
+ ['ten', '10'],
28
+ ['\ba[\b^$]', '1'] # doesn't make sense for an 'a' at the end to be a 1
29
+ ]
30
+
31
+ ORDINALS = [
32
+ ['first', '1'],
33
+ ['third', '3'],
34
+ ['fourth', '4'],
35
+ ['fifth', '5'],
36
+ ['sixth', '6'],
37
+ ['seventh', '7'],
38
+ ['eighth', '8'],
39
+ ['ninth', '9'],
40
+ ['tenth', '10']
41
+ ]
42
+
43
+ TEN_PREFIXES = [
44
+ ['twenty', 20],
45
+ ['thirty', 30],
46
+ ['forty', 40],
47
+ ['fourty', 40], # Common mis-spelling
48
+ ['fifty', 50],
49
+ ['sixty', 60],
50
+ ['seventy', 70],
51
+ ['eighty', 80],
52
+ ['ninety', 90]
53
+ ]
54
+
55
+ BIG_PREFIXES = [
56
+ ['hundred', 100],
57
+ ['thousand', 1000],
58
+ ['million', 1_000_000],
59
+ ['billion', 1_000_000_000],
60
+ ['trillion', 1_000_000_000_000],
61
+ ]
62
+
63
+ def self.numerize(string)
64
+ string = string.dup
65
+
66
+ # preprocess
67
+ string.gsub!(/ +|([^\d])-([^\d])/, '\1 \2') # will mutilate hyphenated-words but shouldn't matter for date extraction
68
+ string.gsub!(/a half/, 'haAlf') # take the 'a' out so it doesn't turn into a 1, save the half for the end
69
+
70
+ # easy/direct replacements
71
+
72
+ DIRECT_NUMS.each do |dn|
73
+ string.gsub!(/#{dn[0]}/i, '<num>' + dn[1])
74
+ end
75
+
76
+ ORDINALS.each do |on|
77
+ string.gsub!(/#{on[0]}/i, '<num>' + on[1] + on[0][-2, 2])
78
+ end
79
+
80
+ # ten, twenty, etc.
81
+
82
+ TEN_PREFIXES.each do |tp|
83
+ string.gsub!(/(?:#{tp[0]}) *<num>(\d(?=[^\d]|$))*/i) { '<num>' + (tp[1] + $1.to_i).to_s }
84
+ end
85
+
86
+ TEN_PREFIXES.each do |tp|
87
+ string.gsub!(/#{tp[0]}/i) { '<num>' + tp[1].to_s }
88
+ end
89
+
90
+ # hundreds, thousands, millions, etc.
91
+
92
+ BIG_PREFIXES.each do |bp|
93
+ string.gsub!(/(?:<num>)?(\d*) *#{bp[0]}/i) { '<num>' + (bp[1] * $1.to_i).to_s}
94
+ andition(string)
95
+ end
96
+
97
+ # fractional addition
98
+ # I'm not combining this with the previous block as using float addition complicates the strings
99
+ # (with extraneous .0's and such )
100
+ string.gsub!(/(\d+)(?: | and |-)*haAlf/i) { ($1.to_f + 0.5).to_s }
101
+
102
+ string.gsub(/<num>/, '')
103
+ end
104
+
105
+ class << self
106
+ private
107
+
108
+ def andition(string)
109
+ sc = StringScanner.new(string)
110
+
111
+ while sc.scan_until(/<num>(\d+)( | and )<num>(\d+)(?=[^\w]|$)/i)
112
+ if sc[2] =~ /and/ || sc[1].size > sc[3].size
113
+ string[(sc.pos - sc.matched_size)..(sc.pos-1)] = '<num>' + (sc[1].to_i + sc[3].to_i).to_s
114
+ sc.reset
115
+ end
116
+ end
117
+ end
118
+
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,47 @@
1
+ module Chronic
2
+ class Ordinal < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Ordinal
5
+ # tags to each token.
6
+ #
7
+ # tokens - An Array of tokens to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of tokens.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ if t = scan_for_ordinals(token) then token.tag(t) end
14
+ if t = scan_for_days(token) then token.tag(t) end
15
+ end
16
+ end
17
+
18
+ # token - The Token object we want to scan.
19
+ #
20
+ # Returns a new Ordinal object.
21
+ def self.scan_for_ordinals(token)
22
+ Ordinal.new($1.to_i) if token.word =~ /^(\d*)(st|nd|rd|th)$/
23
+ end
24
+
25
+ # token - The Token object we want to scan.
26
+ #
27
+ # Returns a new Ordinal object.
28
+ def self.scan_for_days(token)
29
+ if token.word =~ /^(\d*)(st|nd|rd|th)$/
30
+ unless $1.to_i > 31 || $1.to_i < 1
31
+ OrdinalDay.new(token.word.to_i)
32
+ end
33
+ end
34
+ end
35
+
36
+ def to_s
37
+ 'ordinal'
38
+ end
39
+ end
40
+
41
+ class OrdinalDay < Ordinal #:nodoc:
42
+ def to_s
43
+ super << '-day-' << @type.to_s
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,32 @@
1
+ module Chronic
2
+ class Pointer < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Pointer
5
+ # tags to each token.
6
+ #
7
+ # tokens - An Array of tokens to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of tokens.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ if t = scan_for_all(token) then token.tag(t) end
14
+ end
15
+ end
16
+
17
+ # token - The Token object we want to scan.
18
+ #
19
+ # Returns a new Pointer object.
20
+ def self.scan_for_all(token)
21
+ scan_for token, self,
22
+ {
23
+ /\bpast\b/ => :past,
24
+ /\b(?:future|in)\b/ => :future,
25
+ }
26
+ end
27
+
28
+ def to_s
29
+ 'pointer-' << @type.to_s
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,145 @@
1
+ module Chronic
2
+ class Repeater < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Repeater
5
+ # tags to each token.
6
+ #
7
+ # tokens - An Array of tokens to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of tokens.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ if t = scan_for_season_names(token) then token.tag(t); next end
14
+ if t = scan_for_month_names(token) then token.tag(t); next end
15
+ if t = scan_for_day_names(token) then token.tag(t); next end
16
+ if t = scan_for_day_portions(token) then token.tag(t); next end
17
+ if t = scan_for_times(token) then token.tag(t); next end
18
+ if t = scan_for_units(token) then token.tag(t); next end
19
+ end
20
+ end
21
+
22
+ # token - The Token object we want to scan.
23
+ #
24
+ # Returns a new Repeater object.
25
+ def self.scan_for_season_names(token)
26
+ scan_for token, RepeaterSeasonName,
27
+ {
28
+ /^springs?$/ => :spring,
29
+ /^summers?$/ => :summer,
30
+ /^(autumn)|(fall)s?$/ => :autumn,
31
+ /^winters?$/ => :winter
32
+ }
33
+ end
34
+
35
+ # token - The Token object we want to scan.
36
+ #
37
+ # Returns a new Repeater object.
38
+ def self.scan_for_month_names(token)
39
+ scan_for token, RepeaterMonthName,
40
+ {
41
+ /^jan\.?(uary)?$/ => :january,
42
+ /^feb\.?(ruary)?$/ => :february,
43
+ /^mar\.?(ch)?$/ => :march,
44
+ /^apr\.?(il)?$/ => :april,
45
+ /^may$/ => :may,
46
+ /^jun\.?e?$/ => :june,
47
+ /^jul\.?y?$/ => :july,
48
+ /^aug\.?(ust)?$/ => :august,
49
+ /^sep\.?(t\.?|tember)?$/ => :september,
50
+ /^oct\.?(ober)?$/ => :october,
51
+ /^nov\.?(ember)?$/ => :november,
52
+ /^dec\.?(ember)?$/ => :december
53
+ }
54
+ end
55
+
56
+ # token - The Token object we want to scan.
57
+ #
58
+ # Returns a new Repeater object.
59
+ def self.scan_for_day_names(token)
60
+ scan_for token, RepeaterDayName,
61
+ {
62
+ /^m[ou]n(day)?$/ => :monday,
63
+ /^t(ue|eu|oo|u|)s?(day)?$/ => :tuesday,
64
+ /^we(d|dnes|nds|nns)(day)?$/ => :wednesday,
65
+ /^th(u|ur|urs|ers)(day)?$/ => :thursday,
66
+ /^fr[iy](day)?$/ => :friday,
67
+ /^sat(t?[ue]rday)?$/ => :saturday,
68
+ /^su[nm](day)?$/ => :sunday
69
+ }
70
+ end
71
+
72
+ # token - The Token object we want to scan.
73
+ #
74
+ # Returns a new Repeater object.
75
+ def self.scan_for_day_portions(token)
76
+ scan_for token, RepeaterDayPortion,
77
+ {
78
+ /^ams?$/ => :am,
79
+ /^pms?$/ => :pm,
80
+ /^mornings?$/ => :morning,
81
+ /^afternoons?$/ => :afternoon,
82
+ /^evenings?$/ => :evening,
83
+ /^(night|nite)s?$/ => :night
84
+ }
85
+ end
86
+
87
+ # token - The Token object we want to scan.
88
+ #
89
+ # Returns a new Repeater object.
90
+ def self.scan_for_times(token)
91
+ scan_for token, RepeaterTime, /^\d{1,2}(:?\d{1,2})?([\.:]?\d{1,2})?$/
92
+ end
93
+
94
+ # token - The Token object we want to scan.
95
+ #
96
+ # Returns a new Repeater object.
97
+ def self.scan_for_units(token)
98
+ {
99
+ /^years?$/ => :year,
100
+ /^seasons?$/ => :season,
101
+ /^months?$/ => :month,
102
+ /^fortnights?$/ => :fortnight,
103
+ /^weeks?$/ => :week,
104
+ /^weekends?$/ => :weekend,
105
+ /^(week|business)days?$/ => :weekday,
106
+ /^days?$/ => :day,
107
+ /^hrs?$/ => :hour,
108
+ /^hours?$/ => :hour,
109
+ /^mins?$/ => :minute,
110
+ /^minutes?$/ => :minute,
111
+ /^secs?$/ => :second,
112
+ /^seconds?$/ => :second
113
+ }.each do |item, symbol|
114
+ if item =~ token.word
115
+ klass_name = 'Repeater' + symbol.to_s.capitalize
116
+ klass = Chronic.const_get(klass_name)
117
+ return klass.new(symbol)
118
+ end
119
+ end
120
+ return nil
121
+ end
122
+
123
+ def <=>(other)
124
+ width <=> other.width
125
+ end
126
+
127
+ # returns the width (in seconds or months) of this repeatable.
128
+ def width
129
+ raise("Repeater#width must be overridden in subclasses")
130
+ end
131
+
132
+ # returns the next occurance of this repeatable.
133
+ def next(pointer)
134
+ raise("Start point must be set before calling #next") unless @now
135
+ end
136
+
137
+ def this(pointer)
138
+ raise("Start point must be set before calling #this") unless @now
139
+ end
140
+
141
+ def to_s
142
+ 'repeater'
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,53 @@
1
+ module Chronic
2
+ class RepeaterDay < Repeater #:nodoc:
3
+ DAY_SECONDS = 86_400 # (24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_day_start
13
+ @current_day_start = Chronic.time_class.local(@now.year, @now.month, @now.day)
14
+ end
15
+
16
+ direction = pointer == :future ? 1 : -1
17
+ @current_day_start += direction * DAY_SECONDS
18
+
19
+ Span.new(@current_day_start, @current_day_start + DAY_SECONDS)
20
+ end
21
+
22
+ def this(pointer = :future)
23
+ super
24
+
25
+ case pointer
26
+ when :future
27
+ day_begin = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
28
+ day_end = Chronic.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
29
+ when :past
30
+ day_begin = Chronic.construct(@now.year, @now.month, @now.day)
31
+ day_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
32
+ when :none
33
+ day_begin = Chronic.construct(@now.year, @now.month, @now.day)
34
+ day_end = Chronic.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
35
+ end
36
+
37
+ Span.new(day_begin, day_end)
38
+ end
39
+
40
+ def offset(span, amount, pointer)
41
+ direction = pointer == :future ? 1 : -1
42
+ span + direction * amount * DAY_SECONDS
43
+ end
44
+
45
+ def width
46
+ DAY_SECONDS
47
+ end
48
+
49
+ def to_s
50
+ super << '-day'
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,52 @@
1
+ module Chronic
2
+ class RepeaterDayName < Repeater #:nodoc:
3
+ DAY_SECONDS = 86400 # (24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ direction = pointer == :future ? 1 : -1
13
+
14
+ if !@current_date
15
+ @current_date = Date.new(@now.year, @now.month, @now.day)
16
+ @current_date += direction
17
+
18
+ day_num = symbol_to_number(@type)
19
+
20
+ while @current_date.wday != day_num
21
+ @current_date += direction
22
+ end
23
+ else
24
+ @current_date += direction * 7
25
+ end
26
+ next_date = @current_date.succ
27
+ Span.new(Chronic.construct(@current_date.year, @current_date.month, @current_date.day), Chronic.construct(next_date.year, next_date.month, next_date.day))
28
+ end
29
+
30
+ def this(pointer = :future)
31
+ super
32
+
33
+ pointer = :future if pointer == :none
34
+ self.next(pointer)
35
+ end
36
+
37
+ def width
38
+ DAY_SECONDS
39
+ end
40
+
41
+ def to_s
42
+ super << '-dayname-' << @type.to_s
43
+ end
44
+
45
+ private
46
+
47
+ def symbol_to_number(sym)
48
+ lookup = {:sunday => 0, :monday => 1, :tuesday => 2, :wednesday => 3, :thursday => 4, :friday => 5, :saturday => 6}
49
+ lookup[sym] || raise("Invalid symbol specified")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,108 @@
1
+ module Chronic
2
+ class RepeaterDayPortion < Repeater #:nodoc:
3
+ PORTIONS = {
4
+ :am => 0..(12 * 60 * 60 - 1),
5
+ :pm => (12 * 60 * 60)..(24 * 60 * 60 - 1),
6
+ :morning => (6 * 60 * 60)..(12 * 60 * 60), # 6am-12am,
7
+ :afternoon => (13 * 60 * 60)..(17 * 60 * 60), # 1pm-5pm,
8
+ :evening => (17 * 60 * 60)..(20 * 60 * 60), # 5pm-8pm,
9
+ :night => (20 * 60 * 60)..(24 * 60 * 60), # 8pm-12pm
10
+ }
11
+
12
+ def initialize(type)
13
+ super
14
+
15
+ if type.kind_of? Integer
16
+ @range = (@type * 60 * 60)..((@type + 12) * 60 * 60)
17
+ else
18
+ @range = PORTIONS[type]
19
+ @range || raise("Invalid type '#{type}' for RepeaterDayPortion")
20
+ end
21
+
22
+ @range || raise("Range should have been set by now")
23
+ end
24
+
25
+ def next(pointer)
26
+ super
27
+
28
+ if !@current_span
29
+ now_seconds = @now - Chronic.construct(@now.year, @now.month, @now.day)
30
+ if now_seconds < @range.begin
31
+ case pointer
32
+ when :future
33
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
34
+ when :past
35
+ range_start = Chronic.construct(@now.year, @now.month, @now.day - 1) + @range.begin
36
+ end
37
+ elsif now_seconds > @range.end
38
+ case pointer
39
+ when :future
40
+ range_start = Chronic.construct(@now.year, @now.month, @now.day + 1) + @range.begin
41
+ when :past
42
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
43
+ end
44
+ else
45
+ case pointer
46
+ when :future
47
+ range_start = Chronic.construct(@now.year, @now.month, @now.day + 1) + @range.begin
48
+ when :past
49
+ range_start = Chronic.construct(@now.year, @now.month, @now.day - 1) + @range.begin
50
+ end
51
+ end
52
+ offset = (@range.end - @range.begin)
53
+ range_end = construct_date_from_reference_and_offset(range_start, offset)
54
+ @current_span = Span.new(range_start, range_end)
55
+ else
56
+ days_to_shift_window =
57
+ case pointer
58
+ when :future
59
+ 1
60
+ when :past
61
+ -1
62
+ end
63
+
64
+ new_begin = Chronic.construct(@current_span.begin.year, @current_span.begin.month, @current_span.begin.day + days_to_shift_window, @current_span.begin.hour, @current_span.begin.min, @current_span.begin.sec)
65
+ new_end = Chronic.construct(@current_span.end.year, @current_span.end.month, @current_span.end.day + days_to_shift_window, @current_span.end.hour, @current_span.end.min, @current_span.end.sec)
66
+ @current_span = Span.new(new_begin, new_end)
67
+ end
68
+ end
69
+
70
+ def this(context = :future)
71
+ super
72
+
73
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
74
+ range_end = construct_date_from_reference_and_offset(range_start)
75
+ @current_span = Span.new(range_start, range_end)
76
+ end
77
+
78
+ def offset(span, amount, pointer)
79
+ @now = span.begin
80
+ portion_span = self.next(pointer)
81
+ direction = pointer == :future ? 1 : -1
82
+ portion_span + (direction * (amount - 1) * RepeaterDay::DAY_SECONDS)
83
+ end
84
+
85
+ def width
86
+ @range || raise("Range has not been set")
87
+ return @current_span.width if @current_span
88
+ if @type.kind_of? Integer
89
+ return (12 * 60 * 60)
90
+ else
91
+ @range.end - @range.begin
92
+ end
93
+ end
94
+
95
+ def to_s
96
+ super << '-dayportion-' << @type.to_s
97
+ end
98
+
99
+ private
100
+ def construct_date_from_reference_and_offset(reference, offset = nil)
101
+ elapsed_seconds_for_range = offset || (@range.end - @range.begin)
102
+ second_hand = ((elapsed_seconds_for_range - (12 * 60))) % 60
103
+ minute_hand = (elapsed_seconds_for_range - second_hand) / (60) % 60
104
+ hour_hand = (elapsed_seconds_for_range - minute_hand - second_hand) / (60 * 60) + reference.hour % 24
105
+ Chronic.construct(reference.year, reference.month, reference.day, hour_hand, minute_hand, second_hand)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,71 @@
1
+ module Chronic
2
+ class RepeaterFortnight < Repeater #:nodoc:
3
+ FORTNIGHT_SECONDS = 1_209_600 # (14 * 24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_fortnight_start
13
+ case pointer
14
+ when :future
15
+ sunday_repeater = RepeaterDayName.new(:sunday)
16
+ sunday_repeater.start = @now
17
+ next_sunday_span = sunday_repeater.next(:future)
18
+ @current_fortnight_start = next_sunday_span.begin
19
+ when :past
20
+ sunday_repeater = RepeaterDayName.new(:sunday)
21
+ sunday_repeater.start = (@now + RepeaterDay::DAY_SECONDS)
22
+ 2.times { sunday_repeater.next(:past) }
23
+ last_sunday_span = sunday_repeater.next(:past)
24
+ @current_fortnight_start = last_sunday_span.begin
25
+ end
26
+ else
27
+ direction = pointer == :future ? 1 : -1
28
+ @current_fortnight_start += direction * FORTNIGHT_SECONDS
29
+ end
30
+
31
+ Span.new(@current_fortnight_start, @current_fortnight_start + FORTNIGHT_SECONDS)
32
+ end
33
+
34
+ def this(pointer = :future)
35
+ super
36
+
37
+ pointer = :future if pointer == :none
38
+
39
+ case pointer
40
+ when :future
41
+ this_fortnight_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour) + RepeaterHour::HOUR_SECONDS
42
+ sunday_repeater = RepeaterDayName.new(:sunday)
43
+ sunday_repeater.start = @now
44
+ sunday_repeater.this(:future)
45
+ this_sunday_span = sunday_repeater.this(:future)
46
+ this_fortnight_end = this_sunday_span.begin
47
+ Span.new(this_fortnight_start, this_fortnight_end)
48
+ when :past
49
+ this_fortnight_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
50
+ sunday_repeater = RepeaterDayName.new(:sunday)
51
+ sunday_repeater.start = @now
52
+ last_sunday_span = sunday_repeater.next(:past)
53
+ this_fortnight_start = last_sunday_span.begin
54
+ Span.new(this_fortnight_start, this_fortnight_end)
55
+ end
56
+ end
57
+
58
+ def offset(span, amount, pointer)
59
+ direction = pointer == :future ? 1 : -1
60
+ span + direction * amount * FORTNIGHT_SECONDS
61
+ end
62
+
63
+ def width
64
+ FORTNIGHT_SECONDS
65
+ end
66
+
67
+ def to_s
68
+ super << '-fortnight'
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,58 @@
1
+ module Chronic
2
+ class RepeaterHour < Repeater #:nodoc:
3
+ HOUR_SECONDS = 3600 # 60 * 60
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_hour_start
13
+ case pointer
14
+ when :future
15
+ @current_hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour + 1)
16
+ when :past
17
+ @current_hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour - 1)
18
+ end
19
+ else
20
+ direction = pointer == :future ? 1 : -1
21
+ @current_hour_start += direction * HOUR_SECONDS
22
+ end
23
+
24
+ Span.new(@current_hour_start, @current_hour_start + HOUR_SECONDS)
25
+ end
26
+
27
+ def this(pointer = :future)
28
+ super
29
+
30
+ case pointer
31
+ when :future
32
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
33
+ hour_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour + 1)
34
+ when :past
35
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
36
+ hour_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
37
+ when :none
38
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
39
+ hour_end = hour_start + HOUR_SECONDS
40
+ end
41
+
42
+ Span.new(hour_start, hour_end)
43
+ end
44
+
45
+ def offset(span, amount, pointer)
46
+ direction = pointer == :future ? 1 : -1
47
+ span + direction * amount * HOUR_SECONDS
48
+ end
49
+
50
+ def width
51
+ HOUR_SECONDS
52
+ end
53
+
54
+ def to_s
55
+ super << '-hour'
56
+ end
57
+ end
58
+ end