Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from seamusabshere/fancyintervals
parse most forms of ISO 8601 intervals, including shorthand
- Loading branch information
Showing
6 changed files
with
171 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,5 +5,4 @@ gemspec | |
# development dependencies | ||
gem 'yard' | ||
gem 'minitest' | ||
gem 'home_run' | ||
gem 'rake' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
class Timeframe | ||
module Iso8601 | ||
# Internal use. | ||
# | ||
# Parses a duration like 'P1Y2M4DT3H4M2S' | ||
class Duration < ::Struct.new(:date_part, :time_part) | ||
def seconds | ||
(y*31_556_926 + m*2_629_743.83 + d*86_400 + h*3_600 + minutes*60 + s).ceil | ||
end | ||
private | ||
def y; @y ||= parse(date_part, :Y); end | ||
def m; @m ||= parse(date_part, :M); end | ||
def d; @d ||= parse(date_part, :D); end | ||
def h; @h ||= parse(time_part, :H); end | ||
def minutes; @minutes ||= parse(time_part, :M); end | ||
def s; @s ||= parse(time_part, :S); end | ||
def parse(part, indicator) | ||
if part =~ /(\d+)#{indicator.to_s}/ | ||
$1.to_f | ||
else | ||
0 | ||
end | ||
end | ||
end | ||
|
||
# Internal use. | ||
class Side | ||
# We add one day because so that it can be excluded per timeframe's conventions. | ||
EXCLUDED_LAST_DAY = 86_400 | ||
attr_reader :date_part, :time_part | ||
def to_time(counterpart) | ||
if date_part.start_with?('P') | ||
counterpart.resolve_time(self) + resolve_offset + EXCLUDED_LAST_DAY | ||
else | ||
resolve_time counterpart | ||
end | ||
end | ||
end | ||
|
||
# Internal use. | ||
# | ||
# The "A" side of "A/B" | ||
class A < Side | ||
def initialize(raw) | ||
raw = raw.upcase | ||
@date_part, @time_part = raw.split('T') | ||
@time_part ||= '' | ||
end | ||
def resolve_time(*) | ||
Time.parse [date_part, time_part].join('T') | ||
end | ||
# When A is a period, it counts as a negative offset to B. | ||
def resolve_offset | ||
0.0 - Duration.new(date_part, time_part).seconds | ||
end | ||
end | ||
|
||
# Internal use. | ||
# | ||
# The "B" side of "A/B" | ||
class B < Side | ||
def initialize(raw) | ||
raw = raw.upcase | ||
if raw.include?(':') and not raw.include?('T') | ||
# it's just a shorthand for time | ||
@date_part = '' | ||
@time_part = raw | ||
else | ||
@date_part, @time_part = raw.split('T') | ||
@time_part ||= '' | ||
end | ||
end | ||
# When shorthand is used, we need to peek at our counterpart (A) in order to steal letters | ||
# Shorthand can only be used on the B side, and only in <start>/<end> format. | ||
def resolve_time(counterpart) | ||
filled_in_date_part = unless date_part.count('-') == 2 | ||
counterpart.date_part[0..(0-date_part.length-1)] + date_part | ||
else | ||
date_part | ||
end | ||
filled_in_time_part = if time_part.count(':') < 2 | ||
counterpart.time_part[0..(0-time_part.length-1)] + time_part | ||
else | ||
time_part | ||
end | ||
Time.parse [filled_in_date_part, filled_in_time_part].join('T') | ||
end | ||
def resolve_offset | ||
Duration.new(date_part, time_part).seconds | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters