it
February 10, 2023

Задача: Текст в дату 

Нашел тестовую задачу:

Написать функцию, переводящую строку вида

'1991 years 1 month 8 days' в дату.
1) в единственном/множественном числе.

2) Тесты

3) не все компоненты обязательны, например '1975 year 14 days'

4) порядок компонентов может меняться, например '10 day 4 month 2000 year'

И тут Остапа понесло.

require 'byebug'
require 'date'
require 'rspec/autorun'


def text_to_date(text)
    return Date.new(0,1,1) unless text.is_a? String
    
    setup = {
        # 0 год существует
        year: {regex: /\s*(\s*(?'year'\d+)\s+(?i)(years?))\s*/, default: 0},
        # 0 месяц не существует
        month: {regex: /\s*(?'month'\b([1-9]|1[0-2])\b)\s+(?i)(months?)\s*/, default: 1},
        # 0 день не существует
        day: {regex: /\s*(?'day'\b[1-9]|[12][0-9]|3[01]\b)\s+(?i)days?\s*/, default: 1}
    }
    date_parts = {}
    find_date_parts = lambda { |group_name, regex, default| (regex.match(text)&.[](group_name) || default).to_i }
    setup.each { |key, value| date_parts[key] = find_date_parts.call(key, value[:regex], value[:default]) }

    return Date.new(date_parts[:year], date_parts[:month], date_parts[:day])
end

puts text_to_date('2020 years 1 month 4 days')

describe 'text_to_date' do
    it 'normal' do 
        expect(text_to_date('2020 years 1 month 4 days')).to eq(Date.new(2020,1,4))
    end

    it 'case sensitive' do 
        expect(text_to_date('2025 YeArS 1 MoNthS 15 DaYs')).to eq(Date.new(2025,1,15))
    end

    it 'only year' do
        expect(text_to_date('5 years')).to eq(Date.new(5,1,1))
    end

    it 'only month' do
        expect(text_to_date('5 month')).to eq(Date.new(0,5,1))
    end

    it 'only day' do
        expect(text_to_date('7 days')).to eq(Date.new(0,1,7))
    end

    it 'year and days' do
        expect(text_to_date('50 years 7 days')).to eq(Date.new(50,1,7))
    end

    it 'year and month' do
        expect(text_to_date('50 years 7 months')).to eq(Date.new(50,7,1))
    end

    it 'month and day' do
        expect(text_to_date('7 months 7 days')).to eq(Date.new(0,7,7))
    end

    it 'mix dates' do
        expect(text_to_date('7 months 7 years 5 days')).to eq(Date.new(7,7,5))
        expect(text_to_date('7 days 7 years 5 month')).to eq(Date.new(7,5,7))
    end

    it 'plural year' do
        expect(text_to_date('7 year')).to eq(Date.new(7,1,1))
        expect(text_to_date('7 years')).to eq(Date.new(7,1,1))
    end

    it 'plural month' do
        expect(text_to_date('7 month')).to eq(Date.new(0,7,1))
        expect(text_to_date('7 months')).to eq(Date.new(0,7,1))
    end

    it 'plural day' do
        expect(text_to_date('7 day')).to eq(Date.new(0,1,7))
        expect(text_to_date('7 days')).to eq(Date.new(0,1,7))
    end

    it 'more whitespaces' do
        expect(text_to_date('      2020     years       1    month     4     days   ')).to eq(Date.new(2020,1,4))
        expect(text_to_date('      2020     years   ')).to eq(Date.new(2020,1,1))
        expect(text_to_date('      5     month   ')).to eq(Date.new(0,5,1))
        expect(text_to_date('      1     day   ')).to eq(Date.new(0,1,1))
    end

    it '1-31 days' do 
        (1..31).each do |day|
            expect(text_to_date("2020 years 1 month #{day} days")).to eq(Date.new(2020,1,day))
        end
    end

    it '1-12 month' do 
        (1..12).each do |month|
            expect(text_to_date("2020 years #{month} months 2 days")).to eq(Date.new(2020,month,2))
        end
    end

    it '0-2023 years' do 
        (0..2050).each do |year|
            expect(text_to_date("#{year} years 2 months 2 days")).to eq(Date.new(year,2,2))
        end
    end

    it '32 days not exist' do 
        expect(text_to_date('2020 years 1 month 32 days')).to eq(Date.new(2020,1,1))
    end

    it '13 month not exist' do 
        expect(text_to_date('2020 years 13 month 4 days')).to eq(Date.new(2020,1,4))
    end

    it 'nothing found' do 
        expect(text_to_date('nothing')).to eq(Date.new(0,1,1))
        expect(text_to_date(nil)).to eq(Date.new(0,1,1))
    end

end