1defmodule Calendar.ParseUtil do
2  @moduledoc false
3
4  def month_number_for_month_name(string) do
5    string
6    |> String.downcase
7    |> cap_month_number_for_month_name
8  end
9  defp cap_month_number_for_month_name("jan"), do: 1
10  defp cap_month_number_for_month_name("feb"), do: 2
11  defp cap_month_number_for_month_name("mar"), do: 3
12  defp cap_month_number_for_month_name("apr"), do: 4
13  defp cap_month_number_for_month_name("may"), do: 5
14  defp cap_month_number_for_month_name("jun"), do: 6
15  defp cap_month_number_for_month_name("jul"), do: 7
16  defp cap_month_number_for_month_name("aug"), do: 8
17  defp cap_month_number_for_month_name("sep"), do: 9
18  defp cap_month_number_for_month_name("oct"), do: 10
19  defp cap_month_number_for_month_name("nov"), do: 11
20  defp cap_month_number_for_month_name("dec"), do: 12
21  # By returning 0 for invalid month names, we have a valid int to pass to
22  # DateTime.from_erl that will return a nice error. This way we avoid an
23  # exception when parsing an httpdate with an invalid month name.
24  defp cap_month_number_for_month_name(_), do: 0
25
26  def to_int(string) do
27    {int, _} = Integer.parse(string)
28    int
29  end
30
31  def capture_rfc2822_string(string) do
32    ~r/(?<day>[\d]{1,2})[\s]+(?<month>[^\d]{3})[\s]+(?<year>[\d]{4})[\s]+(?<hour>[\d]{2})[^\d]?(?<min>[\d]{2})[^\d]?(?<sec>[\d]{2})[^\d]?(((?<offset_sign>[+-])(?<offset_hours>[\d]{2})(?<offset_mins>[\d]{2})|(?<offset_letters>[A-Z]{1,3})))?/
33    |> Regex.named_captures(string)
34  end
35
36  # Takes strings of hours and mins and return secs
37  def hours_mins_to_secs!(hours, mins) do
38    hours_int = hours |> to_int
39    mins_int = mins |> to_int
40    hours_int*3600+mins_int*60
41  end
42
43  def two_to_four_digit_year(year, year_guessing_base) when year < 100 do
44    closest_year(year, year_guessing_base)
45  end
46  def two_to_four_digit_year(year, _), do: year
47
48  defp closest_year(two_digit_year, year_guessing_base) do
49    two_digit_year
50    |> possible_years(year_guessing_base)
51    |> Enum.map(fn year -> {year, abs(year_guessing_base-year)} end)
52    |> Enum.min_by(fn {_year, diff} -> diff end)
53    |> elem(0)
54  end
55  defp possible_years(two_digit_year, year_guessing_base) do
56    centuries_for_guessing_base(year_guessing_base)
57    |> Enum.map(&(&1+two_digit_year))
58  end
59  # The three centuries closest to the guessing base
60  # if you provide e.g. 2015 it should return [1900, 2000, 2100]
61  defp centuries_for_guessing_base(year_guessing_base) do
62    base_century = year_guessing_base-rem(year_guessing_base, 100)
63    [base_century-100, base_century, base_century+100]
64  end
65end
66