From 5a4f7b600065cee855cfeb100c88fd50de8f09ed Mon Sep 17 00:00:00 2001 From: Aram Visser Date: Sun, 19 Feb 2023 16:23:29 +0700 Subject: [PATCH] Add Faker::Date.day_of_week_between --- doc/default/date.md | 7 +++++ lib/faker/default/date.rb | 45 +++++++++++++++++++++++++++ test/faker/default/test_faker_date.rb | 38 ++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/doc/default/date.md b/doc/default/date.md index 79c7a7ebe1..8addda6a25 100644 --- a/doc/default/date.md +++ b/doc/default/date.md @@ -13,6 +13,13 @@ Faker::Date.between_except(from: '2014-09-23', to: '2015-09-25', excepted: '2015 # If used with Rails (the Active Support gem), additional options are available: Faker::Date.between_except(from: 1.year.ago, to: 1.year.from_now, excepted: Date.today) #=> # +# Random date at given day(s) of week between dates +# Keyword arguments: day, from, to +Faker::Date.on_day_of_week_between(day: :tuesday, from: '2023-01-01', to: '2023-02-01') #=> "Tue, 10 Jan 2023" +Faker::Date.on_day_of_week_between(day: [:saturday, :sunday], from: '2023-01-01', to: '2023-02-01') #=> "Sun, 22 Jan 2023" +# If used with Rails (the Active Support gem), additional options are available: +Faker::Date.on_day_of_week_between(day: [:monday, :wednesday, :friday], from: 1.year.ago, to: 1.year.from_now) #=> "Mon, 20 Feb 2023" + # Random date in the future (up to maximum of N days) # Keyword arguments: days Faker::Date.forward(days: 23) # => "Fri, 03 Oct 2014" diff --git a/lib/faker/default/date.rb b/lib/faker/default/date.rb index 6d1997c1af..d6c86b53d2 100644 --- a/lib/faker/default/date.rb +++ b/lib/faker/default/date.rb @@ -2,6 +2,8 @@ module Faker class Date < Base + DAYS_OF_WEEK = %i[sunday monday tuesday wednesday thursday friday saturday].freeze + class << self ## # Produce a random date between two dates. @@ -128,6 +130,49 @@ def in_date_period(month: nil, year: ::Date.today.year) between(from: from, to: to).to_date end + ## + # Produce a random date at given day(s) of the week between two dates. + # + # @param day [Symbol, Array] # The day(s) of the week. See {DAYS_OF_WEEK}. + # @param from [Date, String] The start of the usable date range. + # @param to [Date, String] The end of the usable date range. + # @return [Date] + # + # @example if used with or without Rails (Active Support) + # Faker::Date.on_day_of_week_between(day: :tuesday, from: '2023-01-01', to: '2023-02-01') #=> # + # + # @example if used with Rails (Active Support) + # Faker::Date.on_day_of_week_between(day: [:saturday, :sunday], from: 1.month.ago, to: Date.today) #=> # + # + # @faker.version next + def on_day_of_week_between(day:, from:, to:) + days = [day].flatten + raise ArgumentError, 'Day of week cannot be empty' if days.empty? + + # Convert given days of the week to numbers used by `Date#wday` method + numeric_weekdays = days.map do |d| + DAYS_OF_WEEK.index(d.to_sym.downcase) || raise(ArgumentError, "#{d} is not a valid day of the week") + end + + from = get_date_object(from) + to = get_date_object(to) + date = Faker::Base.rand_in_range(from, to) + + # If the date is not on one of the given days of the week... + unless numeric_weekdays.include? date.wday + # ...offset date towards a random wanted day of the week + date += sample(numeric_weekdays) - date.wday + + # Move date 1 week earlier or later if the adjusted date is now outside the date range + date += 7 if date < from + date -= 7 if date > to + + raise ArgumentError, "There is no #{DAYS_OF_WEEK[date.wday].capitalize} between #{from} and #{to}" if date > to || date < from + end + + date + end + private def birthday_date(date, age) diff --git a/test/faker/default/test_faker_date.rb b/test/faker/default/test_faker_date.rb index d509bd85dc..46b5b2f3e9 100644 --- a/test/faker/default/test_faker_date.rb +++ b/test/faker/default/test_faker_date.rb @@ -191,4 +191,42 @@ def test_in_date_period_date assert_equal date.year, year end end + + def test_on_day_of_week_between + days = %i[tuesday saturday] + from = Date.parse('2012-01-01') + to = Date.parse('2012-02-01') + + 100.times do + random_date = @tester.on_day_of_week_between(day: days, from: from, to: to) + + assert random_date >= from, "Expected >= \"#{from}\", but got #{random_date}" + assert random_date <= to, "Expected <= \"#{to}\", but got #{random_date}" + assert random_date.tuesday? || random_date.saturday?, "Expected #{random_date} to be Tuesday or Saturday, but was #{Faker::Date::DAYS_OF_WEEK[random_date.wday].capitalize}" + end + end + + def test_unknown_day_of_week + error = assert_raise ArgumentError do + @tester.on_day_of_week_between(day: :unknown, from: '2012-01-01', to: '2013-01-01') + end + + assert_equal 'unknown is not a valid day of the week', error.message + end + + def test_empty_day_of_week + error = assert_raise ArgumentError do + @tester.on_day_of_week_between(day: [], from: '2012-01-01', to: '2013-01-01') + end + + assert_equal 'Day of week cannot be empty', error.message + end + + def test_day_of_week_outside_date_range + error = assert_raise ArgumentError do + @tester.on_day_of_week_between(day: :friday, from: '2012-01-01', to: '2012-01-03') + end + + assert_equal 'There is no Friday between 2012-01-01 and 2012-01-03', error.message + end end