Hokkaido 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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