Packages
xandra
0.9.2
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.18.0-rc.9
0.18.0-rc.8
0.18.0-rc.7
0.18.0-rc.6
0.18.0-rc.5
0.18.0-rc.4
0.18.0-rc.3
0.18.0-rc.2
0.18.0-rc.1
0.17.0
0.16.0
0.15.0
0.15.0-rc.1
0.14.0
0.13.1
0.13.0
0.12.0
0.11.0
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.0
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
Fast, simple, and robust Cassandra driver for Elixir.
Current section
Files
Jump to
Current section
Files
lib/xandra/calendar.ex
# Copyright 2012 Plataformatec
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Modifications copyright 2017 Aleksei Magusev
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
defmodule Xandra.Calendar do
# NOTE: Time.add/3 was introduced in Elixir v1.6.
if Code.ensure_loaded?(Time) and function_exported?(Time, :add, 3) do
def time_from_nanoseconds(nanoseconds) do
Time.add(~T[00:00:00], nanoseconds, :nanosecond)
end
else
def time_from_nanoseconds(nanoseconds) do
nanoseconds
|> DateTime.from_unix!(:nanoseconds)
|> DateTime.to_time()
end
end
# NOTE: Time.diff/3, Date.add/2, and Date.diff/2 were introduced in Elixir v1.5.
if Code.ensure_loaded?(Time) and
function_exported?(Time, :diff, 3) and
Code.ensure_loaded?(Date) and
function_exported?(Date, :add, 2) and
function_exported?(Date, :diff, 2) do
def time_to_nanoseconds(time) do
Time.diff(time, ~T[00:00:00.000000], :nanosecond)
end
def date_from_unix_days(days) do
Date.add(~D[1970-01-01], days)
end
def date_to_unix_days(date) do
Date.diff(date, ~D[1970-01-01])
end
else
@unix_epoch_days 719528
@seconds_per_minute 60
@seconds_per_hour 60 * 60
# Note that this does _not_ handle leap seconds.
@seconds_per_day 24 * 60 * 60
@microseconds_per_second 1_000_000
@parts_per_day @seconds_per_day * @microseconds_per_second
@days_per_nonleap_year 365
@days_per_leap_year 366
def time_to_nanoseconds(time) do
%{hour: hour, minute: minute, second: second, microsecond: microsecond} = time
{parts, ppd} = time_to_day_fraction(hour, minute, second, microsecond)
microseconds = div(parts * @parts_per_day, ppd)
System.convert_time_unit(microseconds, :microseconds, :nanoseconds)
end
def date_from_unix_days(days) do
{year, month, day} = date_from_iso_days(days + @unix_epoch_days)
%Date{year: year, month: month, day: day, calendar: Calendar.ISO}
end
def date_to_unix_days(%{calendar: Calendar.ISO} = date) do
%{year: year, month: month, day: day} = date
date_to_iso_days(year, month, day) - @unix_epoch_days
end
defp time_to_day_fraction(hour, minute, second, {microsecond, _}) do
combined_seconds = hour * @seconds_per_hour + minute * @seconds_per_minute + second
{combined_seconds * @microseconds_per_second + microsecond, @parts_per_day}
end
defp date_to_iso_days(year, month, day) when year in 0..9999 do
true = day <= days_in_month(year, month)
days_in_previous_years(year) + days_before_month(month) + leap_day_offset(year, month) + day - 1
end
defp days_in_month(year, 2) do
if Calendar.ISO.leap_year?(year), do: 29, else: 28
end
defp days_in_month(_, month) when month in [4, 6, 9, 11], do: 30
defp days_in_month(_, month) when month in 1..12, do: 31
defp days_before_month(1), do: 0
defp days_before_month(2), do: 31
defp days_before_month(3), do: 59
defp days_before_month(4), do: 90
defp days_before_month(5), do: 120
defp days_before_month(6), do: 151
defp days_before_month(7), do: 181
defp days_before_month(8), do: 212
defp days_before_month(9), do: 243
defp days_before_month(10), do: 273
defp days_before_month(11), do: 304
defp days_before_month(12), do: 334
defp leap_day_offset(_year, month) when month < 3, do: 0
defp leap_day_offset(year, _month) do
if Calendar.ISO.leap_year?(year), do: 1, else: 0
end
defp date_from_iso_days(days) when days in 0..3_652_424 do
{year, day_of_year} = days_to_year(days)
extra_day = if Calendar.ISO.leap_year?(year), do: 1, else: 0
{month, day_in_month} = year_day_to_year_date(extra_day, day_of_year)
{year, month, day_in_month + 1}
end
defp days_to_year(days) do
year = floor_div(days, @days_per_nonleap_year)
{year, days_before_year} = days_to_year(year, days, days_in_previous_years(year))
{year, days - days_before_year}
end
defp days_to_year(year, days1, days2) when days1 < days2 do
days_to_year(year - 1, days1, days_in_previous_years(year - 1))
end
defp days_to_year(year, _days1, days2) do
{year, days2}
end
defp days_in_previous_years(0), do: 0
defp days_in_previous_years(year) do
previous_year = year - 1
floor_div(previous_year, 4) -
floor_div(previous_year, 100) +
floor_div(previous_year, 400) +
previous_year * @days_per_nonleap_year + @days_per_leap_year
end
# Note that 0 is the first day of the month.
defp year_day_to_year_date(_extra_day, day_of_year) when day_of_year < 31 do
{1, day_of_year}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (59 + extra_day) do
{2, day_of_year - 31}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (90 + extra_day) do
{3, day_of_year - (59 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (120 + extra_day) do
{4, day_of_year - (90 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (151 + extra_day) do
{5, day_of_year - (120 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (181 + extra_day) do
{6, day_of_year - (151 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (212 + extra_day) do
{7, day_of_year - (181 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (243 + extra_day) do
{8, day_of_year - (212 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (273 + extra_day) do
{9, day_of_year - (243 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (304 + extra_day) do
{10, day_of_year - (273 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) when day_of_year < (334 + extra_day) do
{11, day_of_year - (304 + extra_day)}
end
defp year_day_to_year_date(extra_day, day_of_year) do
{12, day_of_year - (334 + extra_day)}
end
defp floor_div(dividend, divisor) do
if dividend * divisor < 0 and rem(dividend, divisor) != 0 do
div(dividend, divisor) - 1
else
div(dividend, divisor)
end
end
end
end