                          time_zone: Calendar.time_zone, zone_abbr: Calendar.zone_abbr,
                          utc_offset: Calendar.utc_offset, std_offset: Calendar.std_offset}
 
-  @seconds_per_day 86400
-  @seconds_per_hour 3600
-  @seconds_per_minute 60
-  @days_per_year 365
-  @days_per_leap_year 365
+  @unix_epoch :calendar.datetime_to_gregorian_seconds {{1970, 1, 1}, {0, 0, 0}}
 
   @doc """
   Converts the given unix time to DateTime.

  def to_unix(%DateTime{std_offset: 0, utc_offset: 0, time_zone: "Etc/UTC",
                        hour: hour, minute: minute, second: second, microsecond: microsecond,
                        year: year, month: month, day: day}, precision \\ :seconds) when year >= 1970 do
    seconds = time_to_seconds(hour, minute, second) + date_to_seconds(year - 1970, month, day)
    System.convert_time_unit(seconds * 1_000_000 + microsecond, :microseconds, precision)
  end

  # Converts the given time to seconds
  defp time_to_seconds(hour, minute, second) do
    (hour * 60 + minute) * 60 + second
  end

  # Converts the given date to seconds
  defp date_to_seconds(year, month, day) do
    (days_in_previous_years(year) +
      days_in_previous_months(days_per_month(year), month, 0) +
      day) * @seconds_per_day
  end

  # Receives the day of year and returns the month plus day of month.
  defp year_day_to_month_day([current | _], month, days) when days <= current,
    do: {month, days}
  defp year_day_to_month_day([current | rest], month, days),
    do: year_day_to_month_day(rest, month + 1, days - current)

  # Returns the number of days per month.
  defp days_per_month(year) do
    feb = if Calendar.ISO.leap_year?(year), do: 29, else: 28
    [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  end

  # Receives epoch days and return year plus day of the year.
  defp days_to_year_day(days) do
    year = div(days, @days_per_year)
    {year, diff_days} = days_to_year_day_diff(year, days, days_in_previous_years(year))
    {year, days - diff_days}
  end

  defp days_to_year_day_diff(year, d1, d2) when d1 < d2,
    do: days_to_year_day_diff(year - 1, d1, days_in_previous_years(year - 1))
  defp days_to_year_day_diff(year, _d, d2),
    do: {year, d2}

  # Gets the days in previous months
  defp days_in_previous_months([_ | _], 1, acc),
    do: acc
  defp days_in_previous_months([days | rest], month, acc),
    do: days_in_previous_months(rest, month - 1, acc + days)

  # Gets the days in previous years
  defp days_in_previous_years(year) when year <= 0, do: 0
  defp days_in_previous_years(year) do
    rest = year - 1
    div(rest, 4) - div(rest, 100) + div(rest, 400) + rest * @days_per_year + @days_per_leap_year
  end

  @doc """

  def from_unix(integer, precision \\ :seconds) when is_integer(integer) and integer >= 0 do
    unit = System.convert_time_unit(integer, precision, :microseconds)
    microsecond = rem(unit, 1_000_000)
    {{year, month, day}, {hour, minute, second}} =
      :calendar.gregorian_seconds_to_datetime(@unix_epoch + div(unit, 1_000_000))

    %DateTime{year: year, month: month, day: day,
              hour: hour, minute: minute, second: second, microsecond: microsecond,
