Нахождение даты в диапазоне двух выходных между другой датой - PullRequest
0 голосов
/ 23 февраля 2019

Я делаю приложение, которое дает скидки для пользователей, а одна из скидок дается в день его рождения.Поскольку действительно трудно использовать скидку только в день вашего рождения, мы решили предоставить скидку всю неделю, включая оба выходных.

Некоторые правила

  • Учитывая дату рождения,скидка начнется в предыдущую пятницу дня рождения;
  • Скидка заканчивается через 10 дней после старта, в воскресенье;
  • Если день рождения в пятницу, все равно получите последнюю пятницу перед этим;

Хорошо, поэтому нам нужен метод, который получает birthdayDate и todayDateи верните значение true или false, сообщая, находится ли дата в диапазоне дня рождения.

Проблема

Все выглядит хорошо, пока вы не начнете смотреть на даты, близкие к смене года.
Если ваш день рождения 31 декабря 2018 года, вы можете использовать скидку 6 января 2019 года, точно так же, если ваш день рождения 1 января 2019 года, 28 декабря 2018 года вы уже можете использовать скидку.
Так что взглянуть только на день рождения текущего года недостаточно, и, поскольку дни недели меняются каждый год, последняя пятница вашего дня рождения в этом году не будет тем же днем ​​следующего.

Существует элегантный способ легко найти этот диапазон и создать метод, возвращающий true или false?

Реальная проблема в том, чтобы найти, какой день рождения является релевантным днем ​​рождения, так как вы можете получить скидку на корметвой последний день рождения и до твоего следующего дня рождения.

Исходный код написан на PHP, но так как это проблема алгоритма, я могу принимать ответы на любом языке

Контрольные примеры

| Today             | Birth             | Output |
|-------------------|-------------------|:------:|
| February 15, 2019 | February 22, 2000 | true   |
| February 24, 2019 | February 22, 2000 | true   |
| February 25, 2019 | February 22, 2000 | false  |
| December 28, 2018 | January 03, 2000  | true   |
| December 27, 2018 | January 03, 2000  | false  |
| December 27, 2019 | January 03, 2000  | true   |
| January 01, 2019  | January 03, 2000  | true   |
| January 01, 2019  | December 31, 2000 | true   |
| January 01, 2019  | December 28, 2000 | false  |

Ответы [ 3 ]

0 голосов
/ 24 февраля 2019

предложение решения (с использованием JS и временных меток)

// Helper constants
const day = 1000 * 60 * 60 * 24
const friday = 5
const $input = document.getElementById('datepicker')
const $result = document.getElementById('result')
const $tests = document.getElementById('tests')

// Generate the period based on birthday and now (optional)
function getPeriod(birthday, today) {
  const now = new Date(today || new Date())
  // reset to start at 00:00
  now.setHours(0)
  now.setMinutes(0)
  now.setMilliseconds(0)

  // Available beggining date
  const begin = new Date(now - (10 * day))
  birthday = new Date(birthday)
  // fix birthday year
  birthday.setFullYear(begin.getFullYear())
  
  // if it already passed, jump to next year
  if (birthday < begin)
    birthday.setFullYear(birthday.getFullYear() + 1)

  // Start date
  const start = new Date(birthday)
  
  // if the birthday is already on friday, jump to previous friday (3th condition) 
  if(start.getDay() === friday)
    start.setTime(start.getTime() - (day * 7))

  // go to the last friday
  while (start.getDay() !== friday) 
    start.setTime(start.getTime() - day)
  // return found date + 10 days
  return [start, new Date(start.getTime() + (10 * day)-1)]
}


function calculatePeriod() {
  const birthday = $input.value
  const [begin, end] = getPeriod(birthday)
  $result.innerHTML = begin.toString() + '<br>' + end.toString()
}


const testCases = [
  ['February 15, 2019', 'February 22, 2000'],
  ['February 24, 2019', 'February 22, 2000'],
  ['February 25, 2019', 'February 22, 2000'],
  ['December 28, 2018', 'January 03, 2000'],
  ['December 27, 2018', 'January 03, 2000'],
  ['December 27, 2019', 'January 03, 2000'],
  ['January 01, 2019 ', 'January 03, 2000'],
  ['January 01, 2019 ', 'December 31, 2000'],
  ['January 01, 2019 ', 'December 28, 2000'],
]

testCases.map(([now, birthday]) => {
  const [begin, end] = getPeriod(birthday, now)
  $tests.innerHTML += `BIRTH: ${birthday}<br>NOW:   ${now}<br>BEGIN: ${begin}<br>END  : ${end}<br><br>`
})
Select an date



Result: ...

Тесты

0 голосов
/ 09 апреля 2019

Здесь то же решение от Jon Skeet, но в PHP с использованием Carbon.

use Carbon\Carbon;

class UserBirthdayTest extends TestCase
{
    /**
     * Setup this test
     */
    public function setUp()
    {
        parent::setUp();
    }

    /**
     * Test create methods on basic CRUDs controllers
     */
    public function testCreate()
    {
        $this->validate("2019-02-15", "2000-02-22", true);
        $this->validate("2019-02-24", "2000-02-22", true);
        $this->validate("2019-02-25", "2000-02-22", false);
        $this->validate("2018-12-28", "2000-01-03", true);
        $this->validate("2019-01-01", "2000-01-03", true);
        $this->validate("2018-12-27", "2000-01-03", false);
        $this->validate("2019-12-27", "2000-01-03", true);
    }

    /**
     * @param \PHPUnit\Framework\TestResult $today
     * @param $birthday
     * @param $expectedResult
     * @return \PHPUnit\Framework\TestResult|void
     */
    public function validate($today, $birthday, $expectedResult)
    {
        $today = new Carbon($today);
        $birthday = new Carbon($birthday);

        // closest discount period
        $threeMonthsAgo = (new Carbon($today))->subMonth(3);

        $ageThreeMonthsAgo = $birthday->diffInYears($threeMonthsAgo);

        // Note: this will use Feb 28th for a Feb 29th birthday in a non leap year.
        $relevantBirthday = $birthday->addYears($ageThreeMonthsAgo + 1);

        // Find the strictly-previous Friday to start the discount interval
        $discountStart = Carbon::createFromTimestamp(strtotime('last friday', $relevantBirthday->timestamp));
        $discountEndInclusive = (new Carbon($discountStart))->addDays(9);

        $actualResult = $today->greaterThanOrEqualTo($discountStart) && $today->lessThanOrEqualTo($discountEndInclusive); // between($discountStart, $discountEndInclusive, false);

        $this->assertEquals($expectedResult, $actualResult);
    }
}
0 голосов
/ 23 февраля 2019

Вот некоторый код C # с использованием моей библиотеки Noda Time , которая проходит все указанные вами тесты.

Основная идея проста:

  • Goпо прошествии трех месяцев с сегодняшнего дня
  • Найти день рождения после этой даты
  • Построить период скидки вокруг этого дня рождения
  • Проверить, находится ли сегодняшняя дата в этом периоде

Выбор трех месяцев несколько произвольный;Он просто предназначен для того, чтобы обойти случай, когда у просто был день рождения.(Вполне возможно, что все, что больше 10 дней и менее 355 дней, будет в порядке. Я просто считаю, что 3 месяца легче рассуждать.)

using System;
using NodaTime;
using NodaTime.Text;

class Test
{
    static void Main()
    {
        RunTest("2019-02-15", "2000-02-22", true);
        RunTest("2019-02-24", "2000-02-22", true);
        RunTest("2019-02-25", "2000-02-22", false);
        RunTest("2018-12-28", "2000-01-03", true);
        RunTest("2019-01-01", "2000-01-03", true);
        RunTest("2018-12-27", "2000-01-03", false);
        RunTest("2019-12-27", "2000-01-03", true);
    }

    static void RunTest(string todayText, string birthdayText, bool expectedResult)
    {
        var pattern = LocalDatePattern.Iso;
        RunTest(pattern.Parse(todayText).Value,
                pattern.Parse(birthdayText).Value,
                expectedResult);               
    }

    static void RunTest(LocalDate today, LocalDate birthday, bool expectedResult)
    {
        // Work out "the birthday that comes after 3 months ago".
        // That can be:
        // - A recent birthday before the closest birthday discount period,
        //   in which case the *next* birthday will be after the current closest
        //   discount period
        // - A birthday *in* the current closest birthday discount period
        // - A birthday *after* the current closest birthday discount period,
        //   in which case the *previous* birthday was before the current
        //   closest discount period
        LocalDate threeMonthsAgo = today.PlusMonths(-3);
        int ageThreeMonthsAgo = Period.Between(birthday, threeMonthsAgo).Years;

        // Note: this will use Feb 28th for a Feb 29th birthday in a non leap year.
        LocalDate relevantBirthday = birthday.PlusYears(ageThreeMonthsAgo + 1);

        // Find the strictly-previous Friday to start the discount interval
        LocalDate discountStart = relevantBirthday.With(DateAdjusters.Previous(IsoDayOfWeek.Friday));        
        LocalDate discountEndInclusive = discountStart.PlusDays(9);
        DateInterval discountInterval = new DateInterval(discountStart, discountEndInclusive);

        bool actualResult = discountInterval.Contains(today);
        Console.WriteLine($"{today} / {birthday} / {(actualResult == expectedResult ? "PASS" : "FAIL")}");
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...