Ensure intuitive behavior when querying across holidays (#71)

This commit is contained in:
Hakan Ensari 2024-11-20 14:13:17 +01:00
parent e5815737c1
commit 7c99f214e6
No known key found for this signature in database
4 changed files with 60 additions and 16 deletions

File diff suppressed because one or more lines are too long

View File

@ -3,13 +3,24 @@
class Day < Sequel::Model
dataset_module do
def latest(date = Date.today)
where(date: select(:date).where(Sequel.lit("date <= ?", date))
where(date: select(:date).where(Sequel[:date] <= date)
.order(Sequel.desc(:date))
.limit(1))
end
# Returns rates for a given date interval
#
# If the start date falls on a holiday/weekend, rates start from the closest preceding business day.
def between(interval)
where(date: interval)
return where(false) if interval.begin > Date.today
previous_date = select(:date)
.where(Sequel[:date] <= interval.begin)
.order(Sequel.desc(:date))
.limit(1)
where(Sequel.expr(:date) >= Sequel.function(:coalesce, previous_date, interval.begin))
.where(Sequel.expr(:date) <= interval.end)
end
def currencies

View File

@ -5,29 +5,54 @@ require "day"
describe Day do
describe ".latest" do
it "returns latest rates before given date" do
it "returns latest available rates on given date" do
date = Date.parse("2010-01-04")
data = Day.latest(date)
_(data.to_a.sample.date).must_equal(date)
date = Date.parse("2010-01-01")
data = Day.latest(date)
_(data.first.date).must_be(:<=, date)
_(data.to_a.sample.date).must_equal(Date.parse("2009-12-31"))
end
it "returns nothing if there are no rates before given date" do
_(Day.latest(Date.parse("1998-01-01"))).must_be_empty
it "returns nothing if date predates dataset" do
_(Day.latest(Date.parse("1901-01-01"))).must_be_empty
end
end
describe ".between" do
it "returns rates between given dates" do
start_date = Date.parse("2010-01-01")
end_date = Date.parse("2010-01-31")
it "returns rates between given working dates" do
start_date = Date.parse("2010-01-04")
end_date = Date.parse("2010-01-29")
dates = Day.between((start_date..end_date)).map(:date).sort
_(dates.first).must_be(:>=, start_date)
_(dates.last).must_be(:<=, end_date)
_(dates.first).must_equal(start_date)
_(dates.last).must_equal(end_date)
end
it "returns nothing if there are no rates between given dates" do
interval = (Date.parse("1998-01-01")..Date.parse("1998-01-31"))
it "starts on preceding business day if start date is a holiday" do
start_date = Date.parse("2024-11-03")
end_date = Date.parse("2024-11-04")
dates = Day.between((start_date..end_date)).map(:date)
_(dates).must_include(Date.parse("2024-11-01"))
end
it "returns nothing if end date predates dataset" do
interval = (Date.parse("1901-01-01")..Date.parse("1901-01-31"))
_(Day.between(interval)).must_be_empty
end
it "allows start date to predate dataset" do
start_date = Date.parse("1901-01-01")
end_date = Date.parse("2024-01-01")
dates = Day.between((start_date..end_date)).map(:date)
_(dates).wont_be_empty
end
it "returns nothing if queried for the future" do
start_date = Date.today + 1
end_date = start_date + 1
dates = Day.between((start_date..end_date)).map(:date)
_(dates).must_be_empty
end
end
end

View File

@ -22,8 +22,16 @@ module Quote
end
it "quotes given date interval" do
_(Date.parse(quote.formatted[:start_date])).must_be(:>=, dates.first)
_(Date.parse(quote.formatted[:end_date])).must_be(:<=, dates.last)
returned_start = Date.parse(quote.formatted[:start_date])
returned_end = Date.parse(quote.formatted[:end_date])
# The returned start date should be the closest working day (before or on) the requested start
_(returned_start).must_be(:<=, dates.first)
# But it shouldn't be too far back (maybe 10 business days)
_(returned_start).must_be(:>, dates.first - 10)
# End date should still be within the requested range
_(returned_end).must_be(:<=, dates.last)
end
it "quotes against the Euro" do