Created
October 31, 2018 12:07
-
-
Save dc0d/26782eae4d7a3f73960eff771475611d to your computer and use it in GitHub Desktop.
Persian Calendar Functions for Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule PersianCalendar do | |
@moduledoc """ | |
for converting persian calendar to/from gregorian calendar | |
""" | |
@breaks [ | |
-61, | |
9, | |
38, | |
199, | |
426, | |
686, | |
756, | |
818, | |
1111, | |
1181, | |
1210, | |
1635, | |
2060, | |
2097, | |
2192, | |
2262, | |
2324, | |
2394, | |
2456, | |
3178 | |
] | |
@doc """ | |
returns {pyear, pmonth, pday} | |
""" | |
def gregorian_to_persian(gyear, gmonth, gday) do | |
jd = g2jd(gyear, gmonth, gday, 0) | |
jd2p(jd) | |
end | |
@doc """ | |
returns {gyear, gmonth, gday} | |
""" | |
def persian_to_gregorian(pyear, pmonth, pday) do | |
jd = p2jd(pyear, pmonth, pday) | |
jd2g(jd, 0) | |
end | |
defp jd2p(jdn) do | |
{ll, _, _} = jd2g(jdn, 0) | |
jy = ll - 621 | |
{leap, _, march} = edge(jy) | |
jdn1f = g2jd(ll, 3, march, 0) | |
k = trunc(jdn - jdn1f) | |
cond do | |
k >= 0 && k <= 185 -> | |
jm = 1 + div(k, 31) | |
jd = rem(k, 31) + 1 | |
{jy, jm, jd} | |
k >= 0 -> | |
k = k - 186 | |
jm = 7 + div(k, 30) | |
jd = rem(k, 30) + 1 | |
{jy, jm, jd} | |
true -> | |
jy = jy - 1 | |
k = k + 179 | |
k = | |
if leap == 1 do | |
k + 1 | |
else | |
k | |
end | |
jm = 7 + div(k, 30) | |
jd = rem(k, 30) + 1 | |
{jy, jm, jd} | |
end | |
end | |
defp p2jd(jy, jm, jd) do | |
{_, igy, march} = edge(jy) | |
g2jd(igy, 3, march, 0) + (jm - 1) * 31 - div(jm, 7) * (jm - 7) + jd - 1 | |
end | |
defp edge(jy) do | |
gy = jy + 621 | |
leapj = -14 | |
jp = Enum.at(@breaks, 0) | |
if jy < jp || jy > Enum.at(@breaks, 19) do | |
# Exception Invalid Jalaali year number:',Jy,' (=',Gy,' Gregorian)' | |
end | |
{jump, jp, leapj} = find_jump(0, 0, jy, leapj, jp) | |
nn = jy - jp | |
leapj = leapj + div(nn, 33) * 8 + div(rem(nn, 33) + 3, 4) | |
leapj = | |
if rem(jump, 33) == 4 && jump - nn == 4 do | |
leapj + 1 | |
else | |
leapj | |
end | |
leapg = div(gy, 4) - div((div(gy, 100) + 1) * 3, 4) - 150 | |
march = 20 + leapj - leapg | |
nn = | |
if jump - nn < 6 do | |
nn - jump + div(jump + 4, 33) * 33 | |
else | |
nn | |
end | |
leap = rem(rem(nn + 1, 33) - 1, 4) | |
leap = | |
if leap == -1 do | |
4 | |
else | |
leap | |
end | |
{leap, gy, march} | |
end | |
defp find_jump(_jump, j, jy, leapj, jp) when j <= 19 do | |
jm = Enum.at(@breaks, j) | |
jump = jm - jp | |
if jy < jm do | |
leapj = trunc(leapj) | |
{jump, jp, leapj} | |
else | |
leapj = leapj + div(jump, 33) * 8 + div(rem(jump, 33), 4) | |
jp = jm | |
find_jump(jump, j + 1, jy, leapj, jp) | |
end | |
end | |
defp find_jump(jump, _j, _jy, leapj, jp) do | |
leapj = trunc(leapj) | |
{jump, jp, leapj} | |
end | |
defp g2jd(l, m, n, j1g0) do | |
res = | |
div((l + div(m - 8, 6) + 100_100) * 1461, 4) + div(153 * rem(m + 9, 12) + 2, 5) + n - | |
34_840_408 | |
case j1g0 do | |
0 -> | |
res - div(div(l + 100_100 + div(m - 8, 6), 100) * 3, 4) + 752 | |
end | |
end | |
defp jd2g(jd, j1g0) do | |
j = 4 * jd + 139_361_631 | |
j = | |
case j1g0 do | |
0 -> | |
j + div(div(4 * jd + 183_187_720, 146_097) * 3, 4) * 4 - 3908 | |
end | |
i = div(rem(j, 1461), 4) * 5 + 308 | |
n = div(rem(i, 153), 5) + 1 | |
m = rem(div(i, 153), 12) + 1 | |
l = div(j, 1461) - 100_100 + div(8 - m, 6) | |
{l, m, n} | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule PersianCalendarTest do | |
use ExUnit.Case | |
doctest PersianCalendar | |
@years [ | |
{{:leap, 1354}, {1975, 3, 21}}, | |
{{:normal, 1355}, {1976, 3, 21}}, | |
{{:normal, 1356}, {1977, 3, 21}}, | |
{{:normal, 1357}, {1978, 3, 21}}, | |
{{:leap, 1358}, {1979, 3, 21}}, | |
{{:normal, 1359}, {1980, 3, 21}}, | |
{{:normal, 1360}, {1981, 3, 21}}, | |
{{:normal, 1361}, {1982, 3, 21}}, | |
{{:leap, 1362}, {1983, 3, 21}}, | |
{{:normal, 1363}, {1984, 3, 21}}, | |
{{:normal, 1364}, {1985, 3, 21}}, | |
{{:normal, 1365}, {1986, 3, 21}}, | |
{{:leap, 1366}, {1987, 3, 21}}, | |
{{:normal, 1367}, {1988, 3, 21}}, | |
{{:normal, 1368}, {1989, 3, 21}}, | |
{{:normal, 1369}, {1990, 3, 21}}, | |
{{:leap, 1370}, {1991, 3, 21}}, | |
{{:normal, 1371}, {1992, 3, 21}}, | |
{{:normal, 1372}, {1993, 3, 21}}, | |
{{:normal, 1373}, {1994, 3, 21}}, | |
{{:normal, 1374}, {1995, 3, 21}}, | |
{{:leap, 1375}, {1996, 3, 20}}, | |
{{:normal, 1376}, {1997, 3, 21}}, | |
{{:normal, 1377}, {1998, 3, 21}}, | |
{{:normal, 1378}, {1999, 3, 21}}, | |
{{:leap, 1379}, {2000, 3, 20}}, | |
{{:normal, 1380}, {2001, 3, 21}}, | |
{{:normal, 1381}, {2002, 3, 21}}, | |
{{:normal, 1382}, {2003, 3, 21}}, | |
{{:leap, 1383}, {2004, 3, 20}}, | |
{{:normal, 1384}, {2005, 3, 21}}, | |
{{:normal, 1385}, {2006, 3, 21}}, | |
{{:normal, 1386}, {2007, 3, 21}}, | |
{{:leap, 1387}, {2008, 3, 20}}, | |
{{:normal, 1388}, {2009, 3, 21}}, | |
{{:normal, 1389}, {2010, 3, 21}}, | |
{{:normal, 1390}, {2011, 3, 21}}, | |
{{:leap, 1391}, {2012, 3, 20}}, | |
{{:normal, 1392}, {2013, 3, 21}}, | |
{{:normal, 1393}, {2014, 3, 21}}, | |
{{:normal, 1394}, {2015, 3, 21}}, | |
{{:leap, 1395}, {2016, 3, 20}}, | |
{{:normal, 1396}, {2017, 3, 21}}, | |
{{:normal, 1397}, {2018, 3, 21}}, | |
{{:normal, 1398}, {2019, 3, 21}}, | |
{{:leap, 1399}, {2020, 3, 20}}, | |
{{:normal, 1400}, {2021, 3, 21}}, | |
{{:normal, 1401}, {2022, 3, 21}}, | |
{{:normal, 1402}, {2023, 3, 21}}, | |
{{:leap, 1403}, {2024, 3, 20}}, | |
{{:normal, 1404}, {2025, 3, 21}}, | |
{{:normal, 1405}, {2026, 3, 21}}, | |
{{:normal, 1406}, {2027, 3, 21}}, | |
{{:normal, 1407}, {2028, 3, 20}}, | |
{{:leap, 1408}, {2029, 3, 20}}, | |
{{:normal, 1409}, {2030, 3, 21}}, | |
{{:normal, 1410}, {2031, 3, 21}}, | |
{{:normal, 1411}, {2032, 3, 20}}, | |
{{:leap, 1412}, {2033, 3, 20}}, | |
{{:normal, 1413}, {2034, 3, 21}}, | |
{{:normal, 1414}, {2035, 3, 21}}, | |
{{:normal, 1415}, {2036, 3, 20}}, | |
{{:leap, 1416}, {2037, 3, 20}}, | |
{{:normal, 1417}, {2038, 3, 21}}, | |
{{:normal, 1418}, {2039, 3, 21}}, | |
{{:normal, 1419}, {2040, 3, 20}} | |
] | |
test "persian to gregorian" do | |
g = PersianCalendar.persian_to_gregorian(1397, 8, 3) | |
assert g == {2018, 10, 25} | |
end | |
test "gregorian to persian" do | |
p = PersianCalendar.gregorian_to_persian(2018, 10, 25) | |
assert p == {1397, 8, 3} | |
end | |
test "persian leap years" do | |
[_ | current] = @years | |
zipped = Enum.zip(current, @years) | |
for {{{_year_type, py}, {gy, gm, gd}}, {{year_type0, _py0}, {_gy0, _gm0, _gd0}}} <- zipped do | |
pdate = PersianCalendar.gregorian_to_persian(gy, gm, gd) | |
assert pdate == {py, 1, 1} | |
gdate = PersianCalendar.persian_to_gregorian(py, 1, 1) | |
assert gdate == {gy, gm, gd} | |
{_py, _pm, pd} = PersianCalendar.gregorian_to_persian(gy, gm, gd - 1) | |
case year_type0 do | |
:normal -> | |
assert pd == 29 | |
:leap -> | |
assert pd == 30 | |
end | |
end | |
end | |
test "persian leap years recursive" do | |
check_leap(@years) | |
end | |
def check_leap([prev | [current | t]]) do | |
{{{_year_type, py}, {gy, gm, gd}}, {{year_type0, _py0}, {_gy0, _gm0, _gd0}}} = {current, prev} | |
pdate = PersianCalendar.gregorian_to_persian(gy, gm, gd) | |
assert pdate == {py, 1, 1} | |
gdate = PersianCalendar.persian_to_gregorian(py, 1, 1) | |
assert gdate == {gy, gm, gd} | |
{_py, _pm, pd} = PersianCalendar.gregorian_to_persian(gy, gm, gd - 1) | |
case year_type0 do | |
:normal -> | |
assert pd == 29 | |
:leap -> | |
assert pd == 30 | |
end | |
check_leap(t) | |
end | |
def check_leap([h | t]) do | |
{{_year_type, py}, {gy, gm, gd}} = h | |
pdate = PersianCalendar.gregorian_to_persian(gy, gm, gd) | |
assert pdate == {py, 1, 1} | |
gdate = PersianCalendar.persian_to_gregorian(py, 1, 1) | |
assert gdate == {gy, gm, gd} | |
check_leap(t) | |
end | |
def check_leap([]) do | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment