호두나무 공방/Exercism in Elixir

Meetup - Exercism in Elixir

2022. 9. 5. 22:32

문제 보기

'yyyy년 dd월 n번째 x요일'과 같은 식으로 날짜가 주어졌을 때 정확한 날짜를 계산하는 문제였다. 'n번째'로 정확히 지정되는 경우도 있지만, '마지막 주'나 'teen번째'(이 문제에서만 쓰이는 용어로, 날짜를 영어로 썼을 때 teen으로 끝나는 13~19일 중 하루를 가리킨다)도 있어 아주 약간 더 복잡해졌다.

기본적으로 리스트를 대상으로 호출하는 Enum.at/2은 금기로 생각하고 있지만, 이 문제의 경우 리스트의 항목 수가 많아 봐야 대여섯 개 정도여서 부담을 조금 내려놓고 마음껏 호출했다.

defmodule Meetup do
  @moduledoc """
  Calculate meetup dates.
  """

  @type weekday ::
          :monday
          | :tuesday
          | :wednesday
          | :thursday
          | :friday
          | :saturday
          | :sunday

  @type schedule :: :first | :second | :third | :fourth | :last | :teenth

  @weekday_mapping %{
    monday: 1,
    tuesday: 2,
    wednesday: 3,
    thursday: 4,
    friday: 5,
    saturday: 6,
    sunday: 7
  }

  @doc """
  Calculate a meetup date.

  The schedule is in which week (1..4, last or "teenth") the meetup date should
  fall.
  """
  @spec meetup(pos_integer, pos_integer, weekday, schedule) :: Date.t()
  def meetup(year, month, weekday, schedule) do
    first_day = Date.new!(year, month, 1)
    last_day = Date.end_of_month(first_day)
    weekday_code = @weekday_mapping[weekday]

    Date.range(first_day, last_day)
    |> Enum.filter(fn d ->
      Date.day_of_week(d) == weekday_code
    end)
    |> then(fn dates ->
      case schedule do
        :first -> hd(dates)
        :second -> Enum.at(dates, 1)
        :third -> Enum.at(dates, 2)
        :fourth -> Enum.at(dates, 3)
        :last -> List.last(dates)
        :teenth -> Enum.find(dates, fn d -> d.day in 13..19 end)
      end
    end)
  end
end