pax_global_header00006660000000000000000000000064152140054210014504gustar00rootroot0000000000000052 comment=128a9b482dd34ea6185298d335de2c8d3f378d4c korean_lunar_calendar_py-0.4.0/000077500000000000000000000000001521400542100165265ustar00rootroot00000000000000korean_lunar_calendar_py-0.4.0/.gitignore000066400000000000000000000000611521400542100205130ustar00rootroot00000000000000korean_lunar_calendar.egg-info/ *.pyc *.swp *.eggkorean_lunar_calendar_py-0.4.0/LICENSE000066400000000000000000000020671521400542100175400ustar00rootroot00000000000000Copyright (c) 2018-2026 Jinil Lee Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. korean_lunar_calendar_py-0.4.0/README.md000066400000000000000000000125661521400542100200170ustar00rootroot00000000000000

korean_lunar_calendar

Convert between the Korean lunar calendar (음력) and the Gregorian solar calendar (양력) — entirely offline, following the KARI (Korea Astronomy and Space Science Institute) standard.

PyPI version MIT license Zero dependencies Live demo

> The Korean and Chinese lunar calendars share the same astronomical basis but can fall on > different dates. This library uses the Korean (KARI) standard. ## Features - **Two-way conversion** — solar → lunar and lunar → solar. - **Offline** — the conversion table is bundled; no network calls, no external services. - **GapJa (간지) strings** — the sexagenary year/month/day in both Korean (정유년 병오월) and Chinese (丁酉年 丙午月). - **Leap-month aware** — handles intercalation months (윤달) and the 1582 Gregorian reform gap. - **Input validation** — every setter returns a `bool`; out-of-range, non-integer, and impossible-leap-month dates are rejected. - **Pure standard library** — no third-party dependencies. ## Supported range | Calendar | From | To | | ------------ | ------------ | ------------ | | Lunar (음력) | `1000-01-01` | `2050-11-18` | | Solar (양력) | `1000-02-13` | `2050-12-31` | Dates outside this range cause the corresponding setter to return `False`. ## Install ```bash pip install korean_lunar_calendar ``` ```python from korean_lunar_calendar import KoreanLunarCalendar ``` ## Usage ### Solar → Lunar (양력 → 음력) ```python calendar = KoreanLunarCalendar() # setSolarDate(year, month, day) -> bool calendar.setSolarDate(2017, 6, 24) print(calendar.LunarIsoFormat()) # 2017-05-01 Intercalation print(calendar.getGapJaString()) # 정유년 병오월 임오일 (윤월) print(calendar.getChineseGapJaString()) # 丁酉年 丙午月 壬午日 (閏月) ``` ### Lunar → Solar (음력 → 양력) ```python calendar = KoreanLunarCalendar() # setLunarDate(year, month, day, isIntercalation) -> bool calendar.setLunarDate(1956, 1, 21, False) print(calendar.SolarIsoFormat()) # 1956-03-03 print(calendar.getGapJaString()) # 병신년 경인월 기사일 # Intercalation (leap) month, e.g. lunar 1727-03-01 (leap) -> solar 1727-04-21 calendar.setLunarDate(1727, 3, 1, True) print(calendar.SolarIsoFormat()) # 1727-04-21 print(calendar.getGapJaString()) # 정미년 갑진월 정사일 (윤월) ``` > Always check the return value of `setSolarDate` / `setLunarDate` before reading the result — > the accessors reflect the **last successful** set call. ## API `KoreanLunarCalendar()` creates a stateful converter. Set a date, then read it back in the other calendar. | Member | Returns | Description | | ------ | ------- | ----------- | | `setSolarDate(year, month, day)` | `bool` | Set a Gregorian date. Returns `False` for out-of-range, non-integer, or nonexistent dates. | | `setLunarDate(year, month, day, isIntercalation)` | `bool` | Set a lunar date. `isIntercalation` requests the leap month; returns `False` if that month has no leap month. | | `LunarIsoFormat()` | `str` | Lunar date as `YYYY-MM-DD`, with ` Intercalation` appended for a leap month. | | `SolarIsoFormat()` | `str` | Solar date as `YYYY-MM-DD`. | | `getGapJaString()` | `str` | Sexagenary cycle in Korean, e.g. `정유년 병오월 임오일 (윤월)`. | | `getChineseGapJaString()` | `str` | Sexagenary cycle in Chinese characters, e.g. `丁酉年 丙午月 壬午日 (閏月)`. | After a successful set you can also read the components directly: `lunarYear`, `lunarMonth`, `lunarDay`, `isIntercalation`, `solarYear`, `solarMonth`, `solarDay`. ## Validation Every setter validates its input and returns a `bool`, so you can branch on the result: ```python calendar = KoreanLunarCalendar() # Rejected -> returns False calendar.setLunarDate(99, 1, 1, False) # before supported range calendar.setSolarDate(2051, 1, 1) # after supported range calendar.setSolarDate(2017, 6, 24.5) # non-integer input calendar.setSolarDate(1582, 10, 8) # skipped by the 1582 Gregorian reform calendar.setLunarDate(2017, 3, 1, True) # month 3 of 2017 has no leap month # Accepted -> returns True calendar.setLunarDate(1000, 1, 1, False) calendar.setSolarDate(2050, 12, 31) ``` ## Tests ```bash python -m unittest -v ``` ## Other languages - **Java** — [usingsky/KoreanLunarCalendar](https://github.com/usingsky/KoreanLunarCalendar) - **Python** — [usingsky/korean_lunar_calendar_py](https://github.com/usingsky/korean_lunar_calendar_py) - **JavaScript** — [usingsky/korean_lunar_calendar_js](https://github.com/usingsky/korean_lunar_calendar_js) ## Acknowledgements Conversion data follows the [KARI (Korea Astronomy and Space Science Institute)](https://astro.kasi.re.kr/) Korean lunar–solar standard. Many thanks to KARI for publishing the reference tables. ## License MIT © [Jinil Lee](https://github.com/usingsky) korean_lunar_calendar_py-0.4.0/dist/000077500000000000000000000000001521400542100174715ustar00rootroot00000000000000korean_lunar_calendar_py-0.4.0/dist/korean_lunar_calendar-0.3.1-py3-none-any.whl000066400000000000000000000215111521400542100274060ustar00rootroot00000000000000PKaP0UԜWx!korean_lunar_calendar/__init__.pyK+U/JM̋)K,ONIKI,R-/*QK䜡R\\eEřy zzĜ`: \PKU0UMc.korean_lunar_calendar/korean_lunar_calendar.py]oܶ ^؍]HHIEպM8=EM-v7=_D] g7/r+>.z\>~[|-;uჇ=zOW?9V#<|ôru[9VL]a(.#>|>7Kyo(Oo޾<9xۋO.yœ*_UVذvHI˪`lalDN_^䭝 1/N~xqpsׯ/~~s5Tgw뀄yZџo3 﫧LiߗuuTPu"H!^9LUE%ǔi F󹫨<:|j0d*s/Dґ$~<5DDۛcOCmg&腞D\WF 3â JHۄ?~<r}KiDZZ5ho^+Zj0RrJDžL^?w^DCHYECk0 Rk&-WiB5 LU)DLN޽|ɻ7e4ipct8ywƚ[\Ջ"IP2)G=f6)r mFkrLfoxX& "ТL1%DdT'nkbgnFYJBv,ƪ'̋P/8QS΁&)]&Rfة#&zczguKSe-௓6eXqE͝a*8;.Z`6,9%֎\ xD1ؙ mQCQt=o^ 1wĤ`xGzh_[tHPJCh.A :n8D XjsVB0W_1{@:13P91j0 4qP^ -k1E^} lK/V>4@6H/񢶨PzĚ5]?d?A%& !;3YCǬ~z:f)b {# Pc;3$h )ic Y4h>!#So )Xc3V$I'tryz„MY0&$M}ǰ( ! gpb'4E HcMגNϠ@!~Bc#x l5"BMC3Z{ 9$[&@ˠ@Hn`>f"2!/)6%)#/I 댫j 5xG]>Ճ)jӛʶI&& ęb yk:6ck(iemauE)4XUk@VCCb61;jX `6+3v=?j2PG{y@R$!yoݿ-i:25>X5"jGmkh˄"swM'j]gVrXk\r4Ϗ(naZ6\{=:(VWXݑdġB))bbʻyjV 0uzaBym=cg + h\>VzHu >BX ;W}/"0;C,vPZ'5dP(XPa #5:sϬ|(/hPיMPP8h pBT:3``C 0W5x(naRPS 5 b-Wy7?buG!!jLC&!j4ԐPn}5CR 5OxܢV5,B 1AwPsbF41}akNA72 pP [8:1 5;OACͫ'Teh@Fֺ ք pYi:kb&Am9|¢4(DMZ䘆\Për`OtxlqCܲgDPÊX/nt5a>o` 5bjzmQ&bYb4Wkj5Py̮Bɼz(l/cr5PI5 D52vL j{cU÷ݰ1!l;FM*8p>S*Vxcx@4RG|P&F\xvFM5kWPwӔ_M$[TT5= j̧556x0 :BdfGG5B<>5zlCI$ä~M)j$_!kNNQ(i r.O&!d&".aCͣ9iPӵ l Lx TTt'"7#³Q$t(O5"DAN ؆Zk4-$-O)lӫ4Dpi4 tS6 Ն0P+/D ԎoF\=~^,DD :HM1`P O,9^|- EO T؄ I#& (A$G5U 4ND 5Da`" <)<8 a'D)=3>H'FY|֎GY5b2,I&;$XA%Wu*q:?(m2j7J\A|hIѭl :pRۼ*6a'9Cp5>Zd 5j$0[>@ 0 9].)jL׾)pFQ>a eTHG-$$Ai&޼Eg\?ijO ORoO4*+)|—oI#9XwxUfLz.mv~j7Jea㙰 ܮ\(.>e']~xRn_<' 2ol> $E4J!j4i;(6e1Y=4&qOڽR[z5{Cx|R(SvfߘO <ʄ<#OPQ r|?yH59_93]VB#Ԡ;FC9-zF;:х !6vBԡm(*5m>h|8y(vJX3Ͷyr!nS(9EurFwagln Z&_d R7Æ65{v} Qǣؘƚ0I7{FW~B,巁g[>&B r|rHU#1[H># '!- ̀C`>P 5&N m.lD(8'25?@Go<#֡U= ~7xPF{>/wӢXt8?>7œ^>nȭt6_r#^4΍ۮ/m}юn^\|yOWI?5`d))FvjDk;G_I} ?PGBi \߆2S,G~f}ެpmJ s>{rv͒w\tT\Yzk2Hsj_ LӧEE,UnY[Jzr^I)T_4='L <W]&_MޏHqIny:sD rTj#2bҰo+VWʻ:,e1IRT &H3'?/ڙWy{6-֛N hYʇ>T,Wņ>N{e&}\ S?F Wb&px5A`%ڿX[?>@hI=KefZeJes|̊ܔG&Pg[&0MJ%~!\GJFIxwo't#`['wG_@55R,Wv=II,4/uyf mF-,&7.( | ^L;1TQ08v<˜}ޏcnI!CLnj! 3 r{ͽN2bIfE8=jǁ]?t mz)y`7Wp{7K},;O*L՝+s67.RciC HJYUdDWJE[Ϯ$&4K_ZEf Dz#yUܰo{[(Ṽ^(Dlٵ|k‡\`9ݠsEa8^ c&%|! a><7񜓘M<ͻg 1@ 3yK4H21YP h޻*u28}[!uogѕQOr9j$ߟ,z:E)o}-5 {vxqof'@Xpp;i54S jQV[W^U`thbz%y'l4Ǻ#(Ń+2\f[ W6[>VmVu3sAD䶸 }Ј\7p;}>4Qdԍ>K2Q" /*0[--/#nGźn&c6w8n_4o\kx}l~JҶ=GlXmWJFs^|k!SvWVA_h1]T*CYӺdןW. )wo/y݊R*{YsVUo qܓ[;]MG1a6|Y>A 3Xh)/>maLчzȯV +{><:l}FqoنqtO([9 %llVVL(}oĿ~?v~S~' 1<9(X&<8n`M|ހPKU0UtJ-korean_lunar_calendar-0.3.1.dist-info/LICENSE]R_o0G;I4M4dfy$oG,ʷIuR$Kl[;x=|7 {mLwW0B㜱= n" ;j?Qv+PAG}<4N7P9gkS!!4'鴃[j)wJ. L|֎`j"u76ݙJ|Z  lcvSk#h qoGEGZ$Nwh ) ZkrT96AGqQTO&_T:{tCg:frz{qAg8rmu׭27@?:8a?daj$QB!'nXB-,S YYH^0""U"G!0b)H%xIlK.>LBm0 <`Rx2 Jy@ege P.XVdHPӄcq┉e [G>rx4w1)2J%LI|FTZ<&EI;|i'fBC7gzJ g)R>OUPKU0U͞i .korean_lunar_calendar-0.3.1.dist-info/METADATAU]oE}_q%^֒ʊ"|n/=WY& JQ"4 D*^ )~ 5^ޙ6.ᑑڽsw=33M9N1 ѕĦ,2GxZXԩOoALЄ[גYNg)\f^7 N(i*c}f8tZ̵á |0 ȃmpdU(U:*]c'*|#td Y+_&M$ϤHݥ L6d1 K8MS;ŜY;<籺Gl[8SP^ꈷi]+&'%\愢Ç}pGw6Íht%-tYQ>aMƦ,Y&p ǻ"G3(9#غ=(f5P؛E#ھy! KTQfiaqsSI<@, AaphPf7eKPgJsPP:wO.o$<^I6hw:7:Z 'u> pl6^[ocz쨜~.o?>v6K*69_gS׷~UAes%紼5!:9~CYXVOg.[FcOaQ)0mmV ,/%AV)n$Wn.Fc6\y*\Ѳq?6_ewW\fQ/Ct?Hu#ڟNjDn^# g14)NaSi,*߫bSg|Dj.wҐ{;?by&g>q=v}T[[ĽCAP:^!ϋ2u,i%ʡeGv !^Mb6<ĖjmŶ [o@X uڗ>X[m /vAEͥP_XjS#5E’/4āe%)C$sZE!%|&DqII_R*Z6[HNhabu&s㋟֤v| #PKU0Uh\\+korean_lunar_calendar-0.3.1.dist-info/WHEEL HM K-*ϳR03rOK-J,/RHJ,./Q0363 /, (-JLR()*M ILR(4KM̫PKU0U3korean_lunar_calendar-0.3.1.dist-info/top_level.txt/JM̋)K,ONIKI,PKU0U@9e,korean_lunar_calendar-0.3.1.dist-info/RECORDr0}%RBETåTOVΜE;_msR S; ʃB/RecVI%w;#>ύꊒ` (NN@C8 cq b!ȈƐciƣ  AZtw7kN\}Z]Rm>@-Z;oMy٨_\kw`woپi 긳:۝}4.:.m~ l]lu߮/U6;;x=[{[=pn!l!0 [^kmmaXK 4="X߽^}pmwk.^i#ZWV]6鵋A(gB<ݝ=&Yۮ^iiQPǝ6˂7tDnm3ejiKKK,?lwWw&. (7ڭd8+-zi/?ߟ/J_`yb8g1з_D"6 &8ݳ u*2R%mJdҝ pr $nF1hģ(OW/_> se)l͢d%%K$WA>ETنg#<MޭRhOWAV"['.Up: {! 1NVn4@?>b0B>h0zn@7`z;>qvi] AVLbx2@$b]2pƱ{<1F|OF8[px8C,\?Fk;ߴvӥkl}~3TKewR{ReZO~O闿O_;NE_2.Z@2>!1Nz!j%Bj$W'h!!Bqv3Z"N# #,GN^xIS4oLro:Ud4 "z ;iF(ͥҊo<}ӯ<{5$ Oo|_>7_#:0,E=#/@SLR*დ?ɇi.Td8J?*2%b Ek:j ] 8"P11q&>=zyAH@N7bUpxcشVV1dz_IG{}oO]Qj5 c1nXJYramq̞ɧ)/8/1sпM؟bYHa50;0֓oG-h5R~>yEDAr燲PSH |CQ8cj*e_ύ+qY8\y`+/(•W CWVb f4쭰RW7ޭ ;gmV;]^TP{vvϒ@v8v0R(|?|evyGK.OvYzyo-R0V/&Խm 4(_L!٫s` ,Y'oeIep 8K_?Ao*YϱҫsRm;O3T,EANU0z&KM֌}\m㾈M\};? ?f{H*VKVn9j`fkvn;;onϤXQeZ6۠6f-hȘʚeGV}vIЁhMqhuwu 70JP ho ?Ljd֣]x/YSW2ksK Kyz*jن?]ϧI~:<n^ گI= ot(vhc1i'IWɧxdO4k4Oߴs@:٧]N˳|#J~FmFJ9Ϸ<%͸CS񭄌Y(h)M9Rs] QZS+ 5M2)0$i6cFy-'eX6˪+]v]M9% 7%c0eL,))ÚRCK+oq ˤ' ɢl_s]3eXäRC6̼31ҡ812W1,O^?]ҁt؂H)D(hkQHȤ#! Nj_x"-3EaCckA5ed9=lfNAy0zks뚓0c^jm0JR":?` ncָF"ꁣlMFj[1ncQp0LMFf;,$LYLj\dgKp)D00c(IgAƤ}!!JK5f"Bc ?,IMξ5輭" UU% ?A楆̞7rS`;Qt' 9O)K2rRC =>KfEy"d̼`@^R[#b%JJ ' [,5^Fj!HEH\O0ͨ:Gv&59Ƣh[^NjxqӛVtp"y3<5}n\ޟҐC;G RoyJ1/ ۴4e13R#N9}&00sP mk<:=7gxR)Z|0cp4m &Eo+>!a "dB5ok8* W' yczy![.HM?PSI29`WJh-c`WClIG H`{g8y&q&rX58eΑhz" ۼ[M9zf^AeeZ7,yZ*&WJœE%9 :{QOzH6Ͻ|r=֌YAsyS2Ȩtl]Mh2 uUɑj@_szBܶ!\CfB 5e jܲZRaz.h R|'Fb!&BQj3ϑP4eqG6, ̜MqRcA.&8+ 1E-*1t2?jfFj䦜iFTjT7N xYRXTSHIy!OH&̜xIILyl)U<]O0Y VFjdˮ1#j)Zd|jH4& XHti„2 <ƖU`?3/kRcǪW5z> k4LkXjhB̈́ԀI hlBMskDN [6-PIcA?gx&dAN. X*Boz>枒PC&ʒ-hH'"EFqBm.l:&'BHY9 Z6L ֘P$Ԥk>cPs~G|*jnQ~"ol%퍩*o j'SOp Kf4}sޞ'MRDj&PsN ǻ7DIMPs̛KROX C̮j.% v-PcRls ]My|C>s tό_>yҜB(E1ğkL4Y{cW17*%' H (C2b KH)W| ;CEDѕ'Jmj,7jE$q)fvϓK/iwR@yǫ0FLP LȮKMAarğ2H9|6% 4&UyYiDk'h,]I㶐d-ߔ(sݼK&(?yh)kʈP"=7QJvuTaAݹR$9$fy]}@^͟Oऊ d3Üd( mrB~tRCc* ÖU33R#Y(BR>yDԛPvP(5r7 IY/#5v?a d$vo_EmM"5RoySyF~gL3azGDnb7{yrmS}]("|(Mۚnd=g8sS>DmA3zm–+'7Dj`w# .wNB'GSx.J &3\`#9$ok蔼<Ȳ `͔ùk%4t[͝~aӠ߫~ ga8fe>FhjNdX *:2 Z i25][ׁtE@TU 19a0;)i1] zIpN,/ܸ`AutA_Uk*a\$ ڹ+'X9?YD6O|OE sv ͨ5KHis}!ٳsZ xaNLZ@%JNM<L1u%$`֩:oܩ\Q/T2Gm = 5ꉀxu +2yUt\s,K]qn$s߹Wh(7UYL]C-$geƥ5op$HgфIT`¦f2fh*h3c֜$X$ I'(ɜz$0_JZ`\9f y0. Hi- Q<8DEwQ ÒXQ%ȿ]!j- \ Ճ Gk$ #%HcTkP' Łsps4$Lod~Us'=7~Ja?r*I ,.vOy1{ew>$?F:V g]P>.Ibچ'y204x,^$]12*9 _HsN&(p>}եDD,4޳h5qZXyOKܲkn QQxGw-}N?SBᔌDUU^!RD$_YTeR+apȻ1$Φob9c~\`m6ӗ G,8_ky4A{J as 1dR!AGXن`L29 ^xnFR`zDt҈fQs[Wޮ ט#lZË,mܶPE&u?h^1^vN,"hdT]9N",?סթ:y2ЭI0)\#ZH~}Aq/uv3-_5|E"F;5N=ۮf]**b-G{SwM2x ~0BtkU *K)"l}46Haq-q+ٝS+zs3S͏V ᄱ^9S3iebyk)5?"-@ D:rUlM9'gvp'% qbr,t^1e0L }0v-~A}VcCNT1Df=4Mk>Z>U>jaA9g8IB1Ep \4P\NA{4b?-W_@_jyi* t]S/we;#xwh+ReU@!o6%lT89^M^GƸAc шGQ4^|FG3Krz=9*-*UF8 `+KOm#/;:o^0";K7㻣?]M=X KuڿbX?fS tq8f>}j:Cܘ7h4ơuG.~ycd`-v';*!IR-E(wx(m?cćdt83"cCM;k=]fGYot5]w(r;Ŗ*kբg?x'|xO-xgV &w.qL4{Y&E0 }e&_9RJ+NyᣓO~'=x듯}z7N>/O~[{mO߻O޳_Z*TMT=!xO>,N#}tpJW%áUs17*P_!xx#N*K; q8!/ N/VZȟ6ۘ!'By !}x0PB.VDmQSt֤/jqM_ctG-->RELP#ljjWgM@3D:]@oی'dUa3wbR5I2[BXz{t֏n?1e[x<LJۧ?~wJRUIqǂUb-mP^">KA/c݆6=p >?Ec"OTz@q0+F1/=p|pp޲rp;3k tJ~dA?^atD0~> U" HrMDB!:c{=iZɔ,e)KYR,e)KYR,e)KYR,e)KYR?@skorean_lunar_calendar_py-0.4.0/dist/korean_lunar_calendar-0.4.0-py3-none-any.whl000066400000000000000000000260731521400542100274160ustar00rootroot00000000000000PK \Ts!korean_lunar_calendar/__init__.pyK+U/JM̋)K,ONIKI,R-/*QK䜡R\\eEřy z&z ĜP: \PK/\Q}Mj.korean_lunar_calendar/korean_lunar_calendar.py]{s6_gWn5>֮;Q[TʥA5޼zU%2*(5Ŕ7.zvLFYsq*$_˷ȁ\N?:8Y=[pZ^Yq8sd$4D;(<{rGr1dͪJ~ӿ9QR\N'Ƿ:_n3Vdo/4 6kخ!\ϝO05R>B.!B#߾=[MXU0{EτҘ+NϩJ]L$B&Q狦U\`V:@%S3eJ,zvjxP EL07yh(gig$y\|o/^ H}mUUѐŒZ,z>!A8&݋tN=Ht솭xK KMʣf.vŷoSď_g1~|gpJ`X~]vr9S3*X^z3:Oyt2MҁSTшh(uU_=Pg?a"̔ H﬚vLԘhĕiChƐڏij}\!Ug-T`<1E[RiGܘPKnf<$Tim̃3J5 ]h_!(wY3sXbP"%gXZnk)jnuSpw|WXSouZ9n9(?vaaAəP 趚ss0M!o ˜8Z}ͼg匤:-%?^ݶNsJIgSoKiV=^x=Hchͺt[l"nċUKWI`6ZxV@x[ z>B/ls-R*y?YRkH4n߁,~ hyu';f^/QE۪ivW{Nyk(B:~r09 qCx+?59ΫSWvsrljy J|:)?4Eb\,YՒ"u/.jϏ(Kcu$KZ>Gr9E$-.KK}Z藼o'z0 -/@.?5a}#:%>ubL 'z]*a$% usExi`,[%^xAHԉA-# 0{Ni4dG %}-I2^Y 6N+ l1T6l\W@ou n?^,Kr錖 ^˽o"/WF/zei6=݊_C^<^+˫: 'Aǚ8+sZõth<59958+u^YXs2վ[S1ʼN "쫴)236/a;-˴/!?xw~lA`{e%et-u7ۙ?r]^Yq^~^V$2ߺ׼nј3=9K?{"XLtFX;){e/ yCv $^pjG p+s@SE+<.Zg̔ow<+&u^ ze\y1:K")9u+ kp4>.5{e)^=}dO-5rًazO$WB.$gjO50VkuKSCl^YsB?AcM {e4K+Fj;aǝ^Y |Ⱦp{e/?ؾy>+kNG{eU(- Ib{e螚ǎkLϑ{"i|k=<)Z=^KiL,`f ՠnKS2 c>HѵG!Я@@SxO74(Zinps\p~}]i|pZ/X/ޥ_@פU @ rά}|S/,I{cfy0Rz$X& r Ml8#&~M0h{B`0'jxJr!|cرxe/1z8,ċY``.jƚ(?HG傽]{LQ~N'/;S ?OQ V4y"XjaG?u bE9aOO#_YB@Ë/x}A>EV}?0Pw~Džlq? OKEn(t-0>]_bM"O |{ M6*ݒ?h^/I%=O' 8%~<{R}Gr4'={?F9\X#?!^7"~T䐘{1;T{48R&=tMcM?܃pD^=Podރ+H:% &My5ruZ/X_&!v~}H~NJWa|E76;u7O kU OP2*,ȉ+A+֤˫)Oc ̱A?GaxQ3IHzxItl_v\ZKvN{q!n .?)G5qr|yYza ǽ14k_ݯV,O}DZn2zdKz/u>}gӌ /^`c>EdU3;)^ozhi%{c] S 7@7¯oBx"~W寿 S6 7]WW 7X2L~⛌~mW<;nPq>boK)k+_"Sд@Wgjr:0Ug+y/<6E^ÏTӞ] Vc۝Of;yϏMA>K4%Sw@fqD-$8#PА" f7îgFexا)aoD͚`ɍE-DW~!7OR`s}i<='8IbK'Q`Ȁe'$ ؐ~r!><*:DvSMdćh9کOce_t]DchLjm;~r;aڶa76rl))*cuцN<"m}?ڠ\5$2v=j֩QǍO CpKv] D1z=P?qn&ؙ?F(x#;Dy~^ %Y`4 Pg|˪n7zez?YBl^ntg9,|ylM#eZea]}& q 2 ?I)|^*l@$Ey̋,e$g?ˍ%6 dUfe~jwڎ і<r#IЮ/2}^M)O~v8>Wy?Hn7-^|;McaOWdAp! upxN+? c]o7/Bd$5X€Ã.|uZQoI7pJ~XVjEA|ipv0\mTt'5)e#*|do#m`Xi %wƃWXWB?F@:{}n9} %Rn!wa5tЭa 1[C.' ;_eZ1*/&z?޼l'_=el: 7#=Dnk|ASa"DHW7HH"0_ݭ OMuKAc.4Ccvlכ&mdm~mE֙÷}Q^l>xd2P*]kw߁k$?H ̀2 eʐ}?p˝5`w`l|9Foĉ0 փvڏ #׿P{ɂusڡ(yjwRcmҶFQn޺f{}_v1zTMM Aٝhz %w{(B'C4© Pz*["Ԯ e m)Q6e܂ ƇVvs{U !1jcmk}Cmz;,z*V#xzm#d{n!GZhSԸ$GPҌF_ Tk[whjK(*\n޹V/׫^[)0I<-[8~? 9k8r%Ča-B4bހ6Cd\"S&$]DSeRC*B#@W* "[r,ɦ"zGs3K rHV)ST. 3D6W—<T'|@`iJR[{E FDžLgS4) L,c%{#J"h N%c%ZȌb$2 1T)QBJ.։9 .Z5{UBq"WA`<<PK=r\٧ .korean_lunar_calendar-0.4.0.dist-info/METADATAYok/wxXF(qb"5hIvfflʕk)iז&%;B7(RDgfw% 6oƻ\3i|ĥE؄\&Yx'C&tYCr)ZwϹrL&M! C 2ZТ֓bhv/y_H7)7-ؼ J3z*n끐2?hu?c}uOܮnK53S]*\IqҞwyW;iŐGϫ0:RZA!&omD[<B&vJ\o˥<9S<UKS54U/_?:!ySS.m l~ y4^|p;({{>Rntohl!G%ԓQv_(vX؏1D`rmu`_D~[ߑL[q,&l\^u+pՕ~D;XhvK{usp_lo]u铐 n}'Kracf|kƆ!Kl#CSJK7fz-} K6*uJt.#WEj?xq @yA pA (Tg{ g=`w%wdmaz!s:lab[ÄjumbmJ:ּwl4g!J"_l-.1lA\5y)Sp6 má P kfoa^ײ$t8Rc-b)'\ G.2dW9iWf]M;ݯ33l(?34e c7f߀`N*>U Ԁy ,M7w@SA'ָ? !^K 8wrQn L+I V\秙)Z.-.5]UR)X^?KkʙcyT-?MZè]٧P⣯pmkV X!Y #!0J@(0|AX;HL|:M˪.(c#L%갇ɑ'M\"'=DLd:jQ$$Vs ec0~7<T=F^w iJ}4NBےLvdL4V $+jÐ@5.Co+ߣŜ66bcy/"z4҈i Xx; ,zjXr)#L&>Dޔ$2\2xn}Ҷ&*fcH! ?qYI]<%?vy%}aXVPI ss'VUx + ^J\ÊbzRjҩa* }Mi&3cFџз6NdǴL1^^)}װݗSb C&60650f}f`ػ'6'RckF M 1a2`@N}EMX^?),?)7;,Q dq3q~弑vW眕\qGV2'agߞk=}$!&ilf]Aw?}'L1CXbmܮ6b{PDƼPwIf@T-YbVHZ#cyˇ\=ƣP8mgxL}zK3c ibRH'^i3+].oS3`kOo{?JIJ#[!) cLmB|GwrMCc8Ε+*!ʇ6LiǢȞɃl ,tz't iM;Llӣ(lx vlI?۬˥V(]Ś,lYă#dTxR0+,IKf!TԒ@}#eZZ)&h &4< s4RoH&ҔǼrĴ#jTσHkE#G.ȃpEܵJƢЃ+׫pԁA' Pa '&&p4b2w `2 v"H}j^:CCssX$iFT+쐥E쌔i73*AuB¼̣mizFywջȻ*(^ӕqRo^yv. OD-7/".vYHEC#&Ww_ `\\s+_z۷n8qPK=r\'L\[+korean_lunar_calendar-0.4.0.dist-info/WHEEL1 0 ]2I_t+%$ &` CnayL=aV) i,f2CR"PK=r\3korean_lunar_calendar-0.4.0.dist-info/top_level.txt/JM̋)K,ONIKI,PK=r\=m,korean_lunar_calendar-0.4.0.dist-info/RECORDKs0{ = DQVd"F#Q N7{ג4u ✰)u1mX9ߥhޥr4PsFh++6BpJt>/}Ǿ7O<\w%6({ %l#JA[!Ӧ#Fiv}G;]hֻtj{kNv嵎ʱD#ķOGp[^&Uw['[{'8 '8@ᮀCM23Enn] 7+_eS#O{^1{ |G;V7ne뀅ԥ!m|'XnhvVC:bDI+ZRɊv2tA6 Kx]u}0ҟzP(r!hS[kh[3ѧ 5Ɋvzl`o> :v`ixxA#6h_$X_af#r'c6/Wn/f,x1P}Ol$sQuNS?gE <6 ; t Tll4ٝql^C-]< y=[L8̖ࠎFXc/g0Xk 8 ٳp?$x[,B/P># O@wE[\y㻫"dMY7yIFY1g_n*Yz BE#Cpq2ޖks_~78%'6LՔ NR1 &뫛(7SΛN824M

ođ`6!F&Ccww'c1T<Ʀc}O{t>%kŧ-'[Bg 8s3QyB1*??|o{w?y߯e&iWog'~cI&zԂ+ 4z#\Pr#3$؟%DH' ?\(Bzp@%q@(}xga4币dS!F`.BcvSA"2?̍#!1LMƄNy;c:M0~Bc O=P$> "b}NV2:.h7ޅr©;Q%i*F*PhDԓx/) h=: EHI49 *Dk-q y.8&AN53ޭN7A%e9ƽEõZ-&[6Z&6-d!7'\d`(dy'gt&*pE~d7?K>$݊!^>bb!ʉ [L ӤLUƻ[6Zm]>-zpZ~/aZD2m'k(kqX4kt9f=.*P4 PNڼӧ-[ 2"_>SN@F$I\1HM 9fƳl2]u u#saa1 $PhCNo?|̆C8Wz VIā\K>  &9`:"V  ^MȆ+-'Dde.4Fdž7D"}o=P2܄ v+ pCac"$Ix4B nH+Kl ~0Gp5;;uu%$::G*9PF'ĩ ]\;Ϗک4vǑ$rCFXLSA9]#h[Zĺq;$0ɾ5a1b =ќ>" 78<#.s&V'ԇve3924t/R#HG H40;"2 rO +ib CYV@ߪd<=Nr/l\D$! FBF$7 TC%m]6)ȬE/=MP'r#Q\1qߧOhw=J ;[[UTEoWCYw1Z/'\.E}by<{0u; J`"  }'nKX'B+/’Lb15? v8v\Ѻ(|oQX8pвjo{;iUwvv# ammka߰*&5E^_[ eydב<|/d0ח7|鴡&' Oƌ$a&_tKe 5EPa› o&^aPRZN>?%4`bA$N j˦eɴ3K$W|W geL ŋ$j7|P{e7Lܟם{eט˧%~K@Dz}o6R7g4Mi+wS]Y{ɽ\jg^mbO[[:LR/1yFO2U鼤p.LL?;k*3y uZϨC3_q}O7)CpXv{Dx8 t7EnO_pN:e؃^!kv8upT u)r;bL ] htͶ;싿zG2Zߛ{{ku{gMhRI>E!'{YK:JsZa_x߻]!jż3Y< :㬒11(P/e{-nF(E]n8td|ɒ/^j}Vۖ퇔_] k~mcvwBoʼiN0cBeKO[V,H]m遙Ԯv삮ᲈ&j>KGhvuF$]Sޱ9o62&1^/ u:Y>UЃ$B#F6[v;/j!᝞!sl]ӕpFQߑO H-,r^gu;6M" q b[WeuE}IPj[7SHrJl!o ʇەm03rIij%n*k$|arEYt(QgC$sѕV/$ݎa^.9ԒlxYwr2_n:&8LPںѓƙSWC_`%_P|SI|@~;9~˺̲ iN-0vO2؞OʾtJRvdoJr%be¿4h/%Y2_,*2nh:t*S`}9V&(uw*]LE#غ.mE:T #:"bef_,,uwߩl92~HeXޱrR,5YbQ,UKuuG)'SB>:VVbfbeY_nvUCiJeT+KԐm]q#ԧK7u[v1_ C`ff쇈_D̤yx'+SvC֍uY>Vf,+SEߢnA,|B,on[JxՍr"+NX ؆/)ce}ɮ9~Q^ߔxP*غɱ$T㐍R^Ƽ6Vfe{›bevI_]-d"s麬@م?E%beY\iW24]sQRLU $۳2S6lGGD <beOZv*tlcK~/}KK4Vf6|fRr+H1)+U s"\++Tѳ qfgNZW/쇌]"jcYT9NicRt])"?4 j/N'tAx,/Q?ep"]2쇮<.UMʤx-A\\s;q9SWs|yFjJE%;m2eq>kRNZ(jSZ?eI 2A]TwdZi'Q7y8;Rs'kMZۼArr 'k5;nt"Ӧ'/<89R ?gr4&q{9/ `:/.$ށq9W_ɚbT㝘ߍ'1QORAp.A,ۯ0db|՜Oj/y_ɠj8~P4">),sj9~Q,3;~rQZARޱ^1_v E$[j?tTn*根|K/*)((vS/XېzZF.}2ӸϯK &,v#w:1:SsR~i,_ry=[[h=-s8w3Љޥv\;e c涼9c85bgq ~E_"Zv4ȷVQdc8kq{y`aװuְن2kb_XY$f-`Am#d䣸+nɻ:/m ĖRɾE.%bZ7Khj~4 {dK}1'ӷ+O`bF+|o]7و~l&E{hayh+ظԴ69~fCD󍤡ސ1a&C t5twxΟr-KK4V״r[mhQ8_ VWZ2yrgi0m7PFQboޫIhhګܖ޹^EJP}=O,}4{/])SVA>&}Ŀm(&_şeF72B>SlZln\޸<:mïrsȟJSDwcʌ!M 9sfr"ϒ X+Ѧj-CUyjY,@9sp-%֭h$-@sdbW|LtZSlIԊUucuȔFH29ԹR0Δ\@#^68-'p7gWb.6E)A}+vLϟ6q2y'J'ȟ\Z]ϘxFv9R'G p~HM7xQo, e Y3Z$\`H(g:ł0D}-2q'l4Kz[HCN% װ@א, [A^+ |lFQrbZ sGY'!ͻ%B|^=L~/"7%ĸ vBHm9#rbR.%[:uv ql :n8' ޔI(DTzo1L"YѪ8x8+(0IpF `(cE-ldg<~H;'vKk J7B]vJP?0`7pSܿ:wewV ,7!'48W>Wo ׀9 n$/<'qDOZv@rjp72= \~J /ΐ}jBt RRiCN>/RVTkеje8R%=/#" Yd-hn$z>ײ. L '.~u>K:|z ďMCp Ѯ$|wգXd&/AF))ېLYpEل7/u19@KX&:/AfpDhŌ&dS?:56}$X ,4p8_OMD0is͖ԕb)X;JvnԎ+!g)Z0-<-)h3H+/YCj5&0 c~kАȣRW)%{/1ŝ3J2= ׀6_>u1!f(_a尮oQLuԓ!G= BJ<Fc1W9?W9WS%=>jH%eeܽ8B"՗B*Fr!ʨȵZvݔژ5'$Kp~j *:G(dGː_t 'a椐2:,QBeE[F rxĘŪF7MPNql_۝Fu9'hۭnwZnScuGvyFv~<ہ[Ĭ:7wo$+{{GKzѲuBҐ6>d| b4;+! $-)dE;Ft: 1](`"|ef]GI5<Cx֚+? 7d1rxDv@qk{ #s{+ڃ r dur`c} f6Y᪹|2;f?SLl}c4 yݴV8So=n{@|g:c:NI7{ۻ'P># A[\Uxr~_雼$_~E# ,|~P妒Goɚ Cpq2ޖks_~70z8*LՔ NR1 5OW7QnY&'~'͝ɂ*a2xMz>f1hotIV WM֟*'``<woboyˮoSDagL bC7 40fSތF>H`2RHrx8Xb8㖍{-@t$OrB@+*3iDnO:n%WQ;>ođ`O'Մwhd 2ß>' t Yl6O? J: < 1*??|o{w?y߯e&iWog'~c?]GQG-ة@&/? (D̉UY%cĎbS,Ғ 6AˋWby̅YhH9e6†hb2ƭ;ۘi8pgL^'ğC| َ3>!ЎO@YJO|C5X^,K e.h7ޅ׷$b4Thu^*2hwiGb཈Zf,t_t*`Z&D/1lTe-|AI 㴝-yP1Wp ƏǕ q /~+46 t - =z:1:AzR`fe)d7)ee AG "9 o!㯿)Y4IiφA/:1Ҽ?VT A0Sɷ m肃}`F [|2˧_No??9L\6_"w5/NӻFc2KūNK{=KVLo?xo?!&DlS\W(FKù_r!Y~]ğ=帑A \C1 $PhC޾ PH"i0@p'X%m^gʄZ0Xy+qMX|j }t Ǒ2HޖRuUؕȈF< W\Λk`뎝+%IB*82Q(9{~?q0;"2Ɍ?l>=|:XYM(|WV@ߪd<徙QzAe"$Vs60,P2b'Y@ z<4,j, m벑FNQDf-z$llmRl<q5⊉c>}B#QUڢȭB-rx Bєժ}9uI|/[tK!W>iPQPG@W/S''C^OJWl_%V_HZzN-]7qU*_JW.ҵ?fˬl+|om? ~9_ ep߶noZ]ozdϼTg4%V&ͻ&< '-G,("3mF@I%j?k9\i}MjI4/I&CЈ|[-ת}ON{I(]EH<IvNA*3m|@G%^|XK|r8P| ^(t}_RnΦL?~54ZyhCOdPdfʾg.qRQsY^3b3-^sK@GeρH+[Wڽjb ._/F6wn߈./1{.iՊWV@korean_lunar_calendar_py-0.4.0/korean_lunar_calendar/000077500000000000000000000000001521400542100230375ustar00rootroot00000000000000korean_lunar_calendar_py-0.4.0/korean_lunar_calendar/__init__.py000066400000000000000000000001631521400542100251500ustar00rootroot00000000000000from .korean_lunar_calendar import KoreanLunarCalendar __version__ = '0.4.0' __all__ = [ 'KoreanLunarCalendar' ] korean_lunar_calendar_py-0.4.0/korean_lunar_calendar/korean_lunar_calendar.py000066400000000000000000000652151521400542100277330ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ KoreanLunarCalendar Here is a library to convert Korean lunar-calendar to Gregorian calendar. Korean calendar and Chinese calendar is same lunar calendar but have different date. This follow the KARI(Korea Astronomy and Space Science Institute) @author : usingsky@gmail.com """ import datetime import numbers class KoreanLunarCalendar(object) : KOREAN_LUNAR_MIN_VALUE = 10000101 KOREAN_LUNAR_MAX_VALUE = 20501118 KOREAN_SOLAR_MIN_VALUE = 10000213 KOREAN_SOLAR_MAX_VALUE = 20501231 KOREAN_LUNAR_BASE_YEAR = 1000 SOLAR_LUNAR_DAY_DIFF = 43 LUNAR_SMALL_MONTH_DAY = 29 LUNAR_BIG_MONTH_DAY = 30 SOLAR_SMALL_YEAR_DAY = 365 SOLAR_BIG_YEAR_DAY = 366 SOLAR_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 29] KOREAN_CHEONGAN = [0xac11, 0xc744, 0xbcd1, 0xc815, 0xbb34, 0xae30, 0xacbd, 0xc2e0, 0xc784, 0xacc4] KOREAN_GANJI = [0xc790, 0xcd95, 0xc778, 0xbb18, 0xc9c4, 0xc0ac, 0xc624, 0xbbf8, 0xc2e0, 0xc720, 0xc220, 0xd574] KOREAN_GAPJA_UNIT = [0xb144, 0xc6d4, 0xc77c] CHINESE_CHEONGAN = [0x7532, 0x4e59, 0x4e19, 0x4e01, 0x620a, 0x5df1, 0x5e9a, 0x8f9b, 0x58ec, 0x7678] CHINESE_GANJI = [0x5b50, 0x4e11, 0x5bc5, 0x536f, 0x8fb0, 0x5df3, 0x5348, 0x672a, 0x7533, 0x9149, 0x620c, 0x4ea5] CHINESE_GAPJA_UNIT = [0x5e74, 0x6708, 0x65e5] INTERCALATION_STR = [0xc724, 0x958f] # Offsets that anchor the sexagenary cycle to the base year (1000): the # cheongan (10) and ganji (12) wheels each start at a fixed position. GAPJA_YEAR_CHEONGAN_OFFSET = 6 GAPJA_YEAR_GANJI_OFFSET = 0 GAPJA_MONTH_CHEONGAN_OFFSET = 3 GAPJA_MONTH_GANJI_OFFSET = 1 GAPJA_DAY_CHEONGAN_OFFSET = 4 GAPJA_DAY_GANJI_OFFSET = 2 KOREAN_LUNAR_DATA = [ 0x82c60a57, 0x82fec52b, 0x82c40d2a, 0x82c60d55, 0xc30095ad, 0x82c4056a, 0x82c6096d, 0x830054dd, 0xc2c404ad, 0x82c40a4d, 0x83002e4d, 0x82c40b26, 0xc300ab56, 0x82c60ad5, 0x82c4035a, 0x8300697a, 0xc2c6095b, 0x82c4049b, 0x83004a9b, 0x82c40a4b, 0xc301caa5, 0x82c406aa, 0x82c60ad5, 0x830092dd, 0xc2c402b5, 0x82c60957, 0x82fe54ae, 0x82c60c97, 0xc2c4064b, 0x82ff254a, 0x82c60da9, 0x8300a6b6, 0xc2c6066d, 0x82c4026e, 0x8301692e, 0x82c4092e, 0xc2c40c96, 0x83004d95, 0x82c40d4a, 0x8300cd69, 0xc2c40b58, 0x82c80d6b, 0x8301926b, 0x82c4025d, 0xc2c4092b, 0x83005aab, 0x82c40a95, 0x82c40b4a, 0xc3021eab, 0x82c402d5, 0x8301b55a, 0x82c604bb, 0xc2c4025b, 0x83007537, 0x82c4052b, 0x82c40695, 0xc3003755, 0x82c406aa, 0x8303cab5, 0x82c40275, 0xc2c404b6, 0x83008a5e, 0x82c40a56, 0x82c40d26, 0xc3005ea6, 0x82c60d55, 0x82c405aa, 0x83001d6a, 0xc2c6096d, 0x8300b4af, 0x82c4049d, 0x82c40a4d, 0xc3007d2d, 0x82c40aa6, 0x82c60b55, 0x830045d5, 0xc2c4035a, 0x82c6095d, 0x83011173, 0x82c4045b, 0xc3009a4f, 0x82c4064b, 0x82c40aa5, 0x83006b69, 0xc2c606b5, 0x82c402da, 0x83002ab6, 0x82c60937, 0xc2fec497, 0x82c60c97, 0x82c4064b, 0x82fe86aa, 0xc2c60da5, 0x82c405b4, 0x83034a6d, 0x82c402ae, 0xc2c40e61, 0x83002d2e, 0x82c40c96, 0x83009d4d, 0x82c40d4a, 0x82c60d65, 0x83016595, 0x82c6055d, 0xc2c4026d, 0x83002a5d, 0x82c4092b, 0x8300aa97, 0xc2c40a95, 0x82c40b4a, 0x83008b5a, 0x82c60ad5, 0xc2c6055b, 0x830042b7, 0x82c40457, 0x82c4052b, 0xc3001d2b, 0x82c40695, 0x8300972d, 0x82c405aa, 0xc2c60ab5, 0x830054ed, 0x82c404b6, 0x82c60a57, 0xc2ff344e, 0x82c40d26, 0x8301be92, 0x82c60d55, 0xc2c405aa, 0x830089ba, 0x82c6096d, 0x82c404ae, 0xc3004a9d, 0x82c40a4d, 0x82c40d25, 0x83002f25, 0xc2c40b54, 0x8303ad69, 0x82c402da, 0x82c6095d, 0xc301649b, 0x82c4049b, 0x82c40a4b, 0x83004b4b, 0xc2c406a5, 0x8300bb53, 0x82c406b4, 0x82c60ab6, 0xc3018956, 0x82c60997, 0x82c40497, 0x83004697, 0xc2c4054b, 0x82fec6a5, 0x82c60da5, 0x82c405ac, 0xc303aab5, 0x82c4026e, 0x82c4092e, 0x83006cae, 0xc2c40c96, 0x82c40d4a, 0x83002f4a, 0x82c60d55, 0xc300b56b, 0x82c6055b, 0x82c4025d, 0x8300793d, 0xc2c40927, 0x82c40a95, 0x83015d15, 0x82c40b4a, 0xc2c60b55, 0x830112d5, 0x82c604db, 0x82fe925e, 0xc2c60a57, 0x82c4052b, 0x83006aab, 0x82c40695, 0xc2c406aa, 0x83003baa, 0x82c60ab5, 0x8300b4b7, 0xc2c404ae, 0x82c60a57, 0x82fe752e, 0x82c40d26, 0xc2c60e93, 0x830056d5, 0x82c405aa, 0x82c609b5, 0xc300256d, 0x82c404ae, 0x8301aa4d, 0x82c40a4d, 0xc2c40d26, 0x83006d65, 0x82c40b52, 0x82c60d6a, 0xc30026da, 0x82c6095d, 0x8301c49d, 0x82c4049b, 0xc2c40a4b, 0x83008aab, 0x82c406a5, 0x82c40b54, 0xc3004bb4, 0x82c60ab6, 0x82c6095b, 0x83002537, 0xc2c40497, 0x8300964f, 0x82c4054b, 0x82c406a5, 0xc30176c5, 0x82c405ac, 0x82c60ab6, 0x8301386e, 0xc2c4092e, 0x8300cc97, 0x82c40c96, 0x82c40d4a, 0xc3008daa, 0x82c60b55, 0x82c4056a, 0x83025adb, 0xc2c4025d, 0x82c4092e, 0x83002d2b, 0x82c40a95, 0xc3009d4d, 0x82c40b2a, 0x82c60b55, 0x83007575, 0xc2c404da, 0x82c60a5b, 0x83004557, 0x82c4052b, 0xc301ca93, 0x82c40693, 0x82c406aa, 0x83008ada, 0xc2c60ae5, 0x82c404b6, 0x83004aae, 0x82c60a57, 0xc2c40527, 0x82ff2526, 0x82c60e53, 0x8300a6cb, 0xc2c405aa, 0x82c605ad, 0x830164ad, 0x82c404ae, 0xc2c40a4e, 0x83004d4d, 0x82c40d26, 0x8300bd53, 0xc2c40b52, 0x82c60b6a, 0x8301956a, 0x82c60557, 0xc2c4049d, 0x83015a1b, 0x82c40a4b, 0x82c40aa5, 0xc3001ea5, 0x82c40b52, 0x8300bb5a, 0x82c60ab6, 0xc2c6095b, 0x830064b7, 0x82c40497, 0x82c4064b, 0xc300374b, 0x82c406a5, 0x8300b6b3, 0x82c405ac, 0xc2c60ab6, 0x830182ad, 0x82c4049e, 0x82c40a4d, 0xc3005d4b, 0x82c40b25, 0x82c40b52, 0x83012e52, 0xc2c60b5a, 0x8300a95e, 0x82c6095b, 0x82c4049b, 0xc3006a57, 0x82c40a4b, 0x82c40aa5, 0x83004ba5, 0xc2c406d4, 0x8300cad6, 0x82c60ab6, 0x82c60937, 0x8300849f, 0x82c40497, 0x82c4064b, 0x82fe56ca, 0xc2c60da5, 0x82c405aa, 0x83001d6c, 0x82c60a6e, 0xc300b92f, 0x82c4092e, 0x82c40c96, 0x83007d55, 0xc2c40d4a, 0x82c60d55, 0x83013555, 0x82c4056a, 0xc2c60a6d, 0x83001a5d, 0x82c4092b, 0x83008a5b, 0xc2c40a95, 0x82c40b2a, 0x83015b2a, 0x82c60ad5, 0xc2c404da, 0x83001cba, 0x82c60a57, 0x8300952f, 0xc2c40527, 0x82c40693, 0x830076b3, 0x82c406aa, 0xc2c60ab5, 0x83003575, 0x82c404b6, 0x8300ca67, 0xc2c40a2e, 0x82c40d16, 0x83008e96, 0x82c40d4a, 0xc2c60daa, 0x830055ea, 0x82c6056d, 0x82c404ae, 0xc301285d, 0x82c40a2d, 0x8300ad17, 0x82c40aa5, 0xc2c40b52, 0x83007d74, 0x82c60ada, 0x82c6055d, 0xc300353b, 0x82c4045b, 0x82c40a2b, 0x83011a2b, 0xc2c40aa5, 0x83009b55, 0x82c406b2, 0x82c60ad6, 0xc3015536, 0x82c60937, 0x82c40457, 0x83003a57, 0xc2c4052b, 0x82feaaa6, 0x82c60d95, 0x82c405aa, 0xc3017aac, 0x82c60a6e, 0x82c4052e, 0x83003cae, 0xc2c40a56, 0x8300bd2b, 0x82c40d2a, 0x82c60d55, 0xc30095ad, 0x82c4056a, 0x82c60a6d, 0x8300555d, 0xc2c4052b, 0x82c40a8d, 0x83002e55, 0x82c40b2a, 0xc300ab56, 0x82c60ad5, 0x82c404da, 0x83006a7a, 0xc2c60a57, 0x82c4051b, 0x83014a17, 0x82c40653, 0xc301c6a9, 0x82c405aa, 0x82c60ab5, 0x830092bd, 0xc2c402b6, 0x82c60a37, 0x82fe552e, 0x82c40d16, 0x82c60e4b, 0x82fe3752, 0x82c60daa, 0x8301b5b4, 0xc2c6056d, 0x82c402ae, 0x83007a3d, 0x82c40a2d, 0xc2c40d15, 0x83004d95, 0x82c40b52, 0x8300cb69, 0xc2c60ada, 0x82c6055d, 0x8301925b, 0x82c4045b, 0xc2c40a2b, 0x83005aab, 0x82c40a95, 0x82c40b52, 0xc3001eaa, 0x82c60ab6, 0x8300c55b, 0x82c604b7, 0xc2c40457, 0x83007537, 0x82c4052b, 0x82c40695, 0xc3014695, 0x82c405aa, 0x8300cab5, 0x82c60a6e, 0xc2c404ae, 0x83008a5e, 0x82c40a56, 0x82c40d2a, 0xc3006eaa, 0x82c60d55, 0x82c4056a, 0x8301295a, 0xc2c6095d, 0x8300b4af, 0x82c4049b, 0x82c40a4d, 0xc3007d2d, 0x82c40b2a, 0x82c60b55, 0x830045d5, 0xc2c402da, 0x82c6095b, 0x83011157, 0x82c4049b, 0xc3009a4f, 0x82c4064b, 0x82c406a9, 0x83006aea, 0xc2c606b5, 0x82c402b6, 0x83002aae, 0x82c60937, 0xc2ffb496, 0x82c40c96, 0x82c60e4b, 0x82fe76b2, 0xc2c60daa, 0x82c605ad, 0x8300336d, 0x82c4026e, 0xc2c4092e, 0x83002d2d, 0x82c40c95, 0x83009d4d, 0xc2c40b4a, 0x82c60b69, 0x8301655a, 0x82c6055b, 0xc2c4025d, 0x83002a5b, 0x82c4092b, 0x8300aa97, 0xc2c40695, 0x82c4074a, 0x83008b5a, 0x82c60ab6, 0xc2c6053b, 0x830042b7, 0x82c40257, 0x82c4052b, 0xc3001d2b, 0x82c40695, 0x830096ad, 0x82c405aa, 0xc2c60ab5, 0x830054ed, 0x82c404ae, 0x82c60a57, 0xc2ff344e, 0x82c40d2a, 0x8301bd94, 0x82c60b55, 0x82c4056a, 0x8300797a, 0x82c6095d, 0x82c404ae, 0xc3004a9b, 0x82c40a4d, 0x82c40d25, 0x83011aaa, 0xc2c60b55, 0x8300956d, 0x82c402da, 0x82c6095b, 0xc30054b7, 0x82c40497, 0x82c40a4b, 0x83004b4b, 0xc2c406a9, 0x8300cad5, 0x82c605b5, 0x82c402b6, 0xc300895e, 0x82c6092f, 0x82c40497, 0x82fe4696, 0xc2c40d4a, 0x8300cea5, 0x82c60d69, 0x82c6056d, 0xc301a2b5, 0x82c4026e, 0x82c4092e, 0x83006cad, 0xc2c40c95, 0x82c40d4a, 0x83002f4a, 0x82c60b59, 0xc300c56d, 0x82c6055b, 0x82c4025d, 0x8300793b, 0xc2c4092b, 0x82c40a95, 0x83015b15, 0x82c406ca, 0xc2c60ad5, 0x830112b6, 0x82c604bb, 0x8300925f, 0xc2c40257, 0x82c4052b, 0x82fe6aaa, 0x82c60e95, 0xc2c406aa, 0x83003baa, 0x82c60ab5, 0x8300b4b7, 0xc2c404ae, 0x82c60a57, 0x82fe752d, 0x82c40d26, 0xc2c60d95, 0x830055d5, 0x82c4056a, 0x82c6096d, 0xc300255d, 0x82c404ae, 0x8300aa4f, 0x82c40a4d, 0xc2c40d25, 0x83006d69, 0x82c60b55, 0x82c4035a, 0xc3002aba, 0x82c6095b, 0x8301c49b, 0x82c40497, 0xc2c40a4b, 0x83008b2b, 0x82c406a5, 0x82c406d4, 0xc3034ab5, 0x82c402b6, 0x82c60937, 0x8300252f, 0xc2c40497, 0x82fe964e, 0x82c40d4a, 0x82c60ea5, 0xc30166a9, 0x82c6056d, 0x82c402b6, 0x8301385e, 0xc2c4092e, 0x8300bc97, 0x82c40a95, 0x82c40d4a, 0xc3008daa, 0x82c60b4d, 0x82c6056b, 0x830042db, 0xc2c4025d, 0x82c4092d, 0x83002d2b, 0x82c40a95, 0xc3009b4d, 0x82c406aa, 0x82c60ad5, 0x83006575, 0xc2c604bb, 0x82c4025b, 0x83013457, 0x82c4052b, 0xc2ffba94, 0x82c60e95, 0x82c406aa, 0x83008ada, 0xc2c609b5, 0x82c404b6, 0x83004aae, 0x82c60a4f, 0xc2c20526, 0x83012d26, 0x82c60d55, 0x8301a5a9, 0xc2c4056a, 0x82c6096d, 0x8301649d, 0x82c4049e, 0xc2c40a4d, 0x83004d4d, 0x82c40d25, 0x8300bd53, 0xc2c40b54, 0x82c60b5a, 0x8301895a, 0x82c6095b, 0xc2c4049b, 0x83004a97, 0x82c40a4b, 0x82c40aa5, 0xc3001ea5, 0x82c406d4, 0x8302badb, 0x82c402b6, 0xc2c60937, 0x830064af, 0x82c40497, 0x82c4064b, 0xc2fe374a, 0x82c60da5, 0x8300b6b5, 0x82c6056d, 0xc2c402ae, 0x8300793e, 0x82c4092e, 0x82c40c96, 0xc3015d15, 0x82c40d4a, 0x82c60da5, 0x83013555, 0xc2c4056a, 0x83007a7a, 0x82c60a5d, 0x82c4092d, 0xc3006aab, 0x82c40a95, 0x82c40b4a, 0x83004baa, 0xc2c60ad5, 0x82c4055a, 0x830128ba, 0x82c60a5b, 0xc3007537, 0x82c4052b, 0x82c40693, 0x83015715, 0xc2c406aa, 0x82c60ad5, 0x830035b5, 0x82c404b6, 0xc3008a5e, 0x82c40a4e, 0x82c40d26, 0x83006ea6, 0xc2c40d52, 0x82c60daa, 0x8301466a, 0x82c6056d, 0xc2c404ae, 0x83003a9d, 0x82c40a4d, 0x83007d2b, 0xc2c40b25, 0x82c40d52, 0x83015d54, 0x82c60b5a, 0xc2c6055d, 0x8300355b, 0x82c4049b, 0x83007657, 0x82c40a4b, 0x82c40aa5, 0x83006b65, 0x82c406d2, 0xc2c60ada, 0x830045b6, 0x82c60937, 0x82c40497, 0xc3003697, 0x82c4064d, 0x82fe76aa, 0x82c60da5, 0xc2c405aa, 0x83005aec, 0x82c60aae, 0x82c4092e, 0xc3003d2e, 0x82c40c96, 0x83018d45, 0x82c40d4a, 0xc2c60d55, 0x83016595, 0x82c4056a, 0x82c60a6d, 0xc300455d, 0x82c4052d, 0x82c40a95, 0x83013c95, 0xc2c40b4a, 0x83017b4a, 0x82c60ad5, 0x82c4055a, 0xc3015a3a, 0x82c60a5b, 0x82c4052b, 0x83014a17, 0xc2c40693, 0x830096ab, 0x82c406aa, 0x82c60ab5, 0xc30064f5, 0x82c404b6, 0x82c60a57, 0x82fe452e, 0xc2c40d16, 0x82c60e93, 0x82fe3752, 0x82c60daa, 0xc30175aa, 0x82c6056d, 0x82c404ae, 0x83015a1d, 0xc2c40a2d, 0x82c40d15, 0x83004da5, 0x82c40b52, 0xc3009d6a, 0x82c60ada, 0x82c6055d, 0x8301629b, 0xc2c4045b, 0x82c40a2b, 0x83005b2b, 0x82c40a95, 0xc2c40b52, 0x83012ab2, 0x82c60ad6, 0x83017556, 0xc2c60537, 0x82c40457, 0x83005657, 0x82c4052b, 0xc2c40695, 0x83003795, 0x82c405aa, 0x8300aab6, 0xc2c60a6d, 0x82c404ae, 0x83006a6e, 0x82c40a56, 0xc2c40d2a, 0x83005eaa, 0x82c60d55, 0x82c405aa, 0xc3003b6a, 0x82c60a6d, 0x830074bd, 0x82c404ab, 0xc2c40a8d, 0x83005d55, 0x82c40b2a, 0x82c60b55, 0xc30045d5, 0x82c404da, 0x82c6095d, 0x83002557, 0xc2c4049b, 0x83006a97, 0x82c4064b, 0x82c406a9, 0x83004baa, 0x82c606b5, 0x82c402ba, 0x83002ab6, 0xc2c60937, 0x82fe652e, 0x82c40d16, 0x82c60e4b, 0xc2fe56d2, 0x82c60da9, 0x82c605b5, 0x8300336d, 0xc2c402ae, 0x82c40a2e, 0x83002e2d, 0x82c40c95, 0xc3006d55, 0x82c40b52, 0x82c60b69, 0x830045da, 0xc2c6055d, 0x82c4025d, 0x83003a5b, 0x82c40a2b, 0xc3017a8b, 0x82c40a95, 0x82c40b4a, 0x83015b2a, 0xc2c60ad5, 0x82c6055b, 0x830042b7, 0x82c40257, 0xc300952f, 0x82c4052b, 0x82c40695, 0x830066d5, 0xc2c405aa, 0x82c60ab5, 0x8300456d, 0x82c404ae, 0xc2c60a57, 0x82ff3456, 0x82c40d2a, 0x83017e8a, 0xc2c60d55, 0x82c405aa, 0x83005ada, 0x82c6095d, 0xc2c404ae, 0x83004aab, 0x82c40a4d, 0x83008d2b, 0xc2c40b29, 0x82c60b55, 0x83007575, 0x82c402da, 0xc2c6095d, 0x830054d7, 0x82c4049b, 0x82c40a4b, 0xc3013a4b, 0x82c406a9, 0x83008ad9, 0x82c606b5, 0xc2c402b6, 0x83015936, 0x82c60937, 0x82c40497, 0xc2fe4696, 0x82c40e4a, 0x8300aea6, 0x82c60da9, 0xc2c605ad, 0x830162ad, 0x82c402ae, 0x82c4092e, 0xc3005cad, 0x82c40c95, 0x82c40d4a, 0x83013d4a, 0xc2c60b69, 0x8300757a, 0x82c6055b, 0x82c4025d, 0xc300595b, 0x82c4092b, 0x82c40a95, 0x83004d95, 0xc2c40b4a, 0x82c60b55, 0x830026d5, 0x82c6055b, 0xc3006277, 0x82c40257, 0x82c4052b, 0x82fe5aaa, 0xc2c60e95, 0x82c406aa, 0x83003baa, 0x82c60ab5, 0x830084bd, 0x82c404ae, 0x82c60a57, 0x82fe554d, 0xc2c40d26, 0x82c60d95, 0x83014655, 0x82c4056a, 0xc2c609ad, 0x8300255d, 0x82c404ae, 0x83006a5b, 0xc2c40a4d, 0x82c40d25, 0x83005da9, 0x82c60b55, 0xc2c4056a, 0x83002ada, 0x82c6095d, 0x830074bb, 0xc2c4049b, 0x82c40a4b, 0x83005b4b, 0x82c406a9, 0xc2c40ad4, 0x83024bb5, 0x82c402b6, 0x82c6095b, 0xc3002537, 0x82c40497, 0x82fe6656, 0x82c40e4a, 0xc2c60ea5, 0x830156a9, 0x82c605b5, 0x82c402b6, 0xc30138ae, 0x82c4092e, 0x83017c8d, 0x82c40c95, 0xc2c40d4a, 0x83016d8a, 0x82c60b69, 0x82c6056d, 0xc301425b, 0x82c4025d, 0x82c4092d, 0x83002d2b, 0xc2c40a95, 0x83007d55, 0x82c40b4a, 0x82c60b55, 0xc3015555, 0x82c604db, 0x82c4025b, 0x83013857, 0xc2c4052b, 0x83008a9b, 0x82c40695, 0x82c406aa, 0xc3006aea, 0x82c60ab5, 0x82c404b6, 0x83004aae, 0xc2c60a57, 0x82c40527, 0x82fe3726, 0x82c60d95, 0xc30076b5, 0x82c4056a, 0x82c609ad, 0x830054dd, 0xc2c404ae, 0x82c40a4e, 0x83004d4d, 0x82c40d25, 0xc3008d59, 0x82c40b54, 0x82c60d6a, 0x8301695a, 0xc2c6095b, 0x82c4049b, 0x83004a9b, 0x82c40a4b, 0xc300ab27, 0x82c406a5, 0x82c406d4, 0x83026b75, 0xc2c402b6, 0x82c6095b, 0x830054b7, 0x82c40497, 0xc2c4064b, 0x82fe374a, 0x82c60ea5, 0x830086d9, 0xc2c605ad, 0x82c402b6, 0x8300596e, 0x82c4092e, 0xc2c40c96, 0x83004e95, 0x82c40d4a, 0x82c60da5, 0xc3002755, 0x82c4056c, 0x83027abb, 0x82c4025d, 0xc2c4092d, 0x83005cab, 0x82c40a95, 0x82c40b4a, 0xc3013b4a, 0x82c60b55, 0x8300955d, 0x82c404ba, 0xc2c60a5b, 0x83005557, 0x82c4052b, 0x82c40a95, 0xc3004b95, 0x82c406aa, 0x82c60ad5, 0x830026b5, 0xc2c404b6, 0x83006a6e, 0x82c60a57, 0x82c40527, 0xc2fe56a6, 0x82c60d93, 0x82c405aa, 0x83003b6a, 0xc2c6096d, 0x8300b4af, 0x82c404ae, 0x82c40a4d, 0xc3016d0d, 0x82c40d25, 0x82c40d52, 0x83005dd4, 0xc2c60b6a, 0x82c6096d, 0x8300255b, 0x82c4049b, 0xc3007a57, 0x82c40a4b, 0x82c40b25, 0x83015b25, 0xc2c406d4, 0x82c60ada, 0x830138b6] def __init__(self): self.lunarYear = 0 self.lunarMonth = 0 self.lunarDay = 0 self.isIntercalation = False self.solarYear = 0 self.solarMonth = 0 self.solarDay = 0 # Memoized cumulative day counts from the base year, so the repeated # lookups inside the month-search loops don't re-sum ~1000 years. self.__cumulativeLunarDays = {} self.__cumulativeSolarDays = {} # Default to today's solar date until an explicit date is set. today = datetime.date.today() self.setSolarDate(today.year, today.month, today.day) def LunarIsoFormat(self): dateStr = "%04d-%02d-%02d" % (self.lunarYear, self.lunarMonth, self.lunarDay) if self.isIntercalation : dateStr += " Intercalation" return dateStr def SolarIsoFormat(self): return "%04d-%02d-%02d" % (self.solarYear, self.solarMonth, self.solarDay) def __getLunarData(self, year): return self.KOREAN_LUNAR_DATA[year - self.KOREAN_LUNAR_BASE_YEAR] def __getLunarIntercalationMonth(self, lunarData): return (lunarData >> 12) & 0x000F def __getLunarDays(self, year, month=None, isIntercalation=None): lunarData = self.__getLunarData(year) if month is not None and isIntercalation is not None : if (isIntercalation == True) and (self.__getLunarIntercalationMonth(lunarData) == month): days = self.LUNAR_BIG_MONTH_DAY if ((lunarData >>16) & 0x01) > 0 else self.LUNAR_SMALL_MONTH_DAY else: days = self.LUNAR_BIG_MONTH_DAY if ((lunarData >> (12 - month)) & 0x01) > 0 else self.LUNAR_SMALL_MONTH_DAY else: days = (lunarData >> 17) & 0x01FF return days def __accumulateYearDays(self, year, cache, perYearFn): # Sum of per-year day counts from the base year through `year`, memoized. # Extends the previous year's cached sum when available (amortized O(1)), # otherwise sums from the base year once and caches the total. if year in cache: return cache[year] if (year - 1) in cache and year > self.KOREAN_LUNAR_BASE_YEAR: days = cache[year - 1] + perYearFn(year) else: days = 0 for baseYear in range(self.KOREAN_LUNAR_BASE_YEAR, year+1): days += perYearFn(baseYear) cache[year] = days return days def __getLunarDaysBeforeBaseYear(self, year): return self.__accumulateYearDays(year, self.__cumulativeLunarDays, lambda y: self.__getLunarDays(y)) def __getLunarDaysBeforeBaseMonth(self, year, month, isIntercalation): days = 0 if (year >= self.KOREAN_LUNAR_BASE_YEAR) and (month > 0): for baseMonth in range(1, month+1): days += self.__getLunarDays(year, baseMonth, False) if isIntercalation == True: intercalationMonth = self.__getLunarIntercalationMonth(self.__getLunarData(year)) if (intercalationMonth > 0) and intercalationMonth < month+1: days += self.__getLunarDays(year, intercalationMonth, True) return days def __getLunarAbsDays(self, year, month, day, isIntercalation): days = self.__getLunarDaysBeforeBaseYear(year-1) + self.__getLunarDaysBeforeBaseMonth(year, month-1, True) + day if (isIntercalation == True) and (self.__getLunarIntercalationMonth(self.__getLunarData(year)) == month): days += self.__getLunarDays(year, month, False) return days def __isSolarIntercalationYear(self, lunarData): return ((lunarData >> 30) & 0x01) > 0 def __getSolarDays(self, year, month=None): lunarData = self.__getLunarData(year) if month is not None : days = self.SOLAR_DAYS[12] if (month == 2) and self.__isSolarIntercalationYear(lunarData) else self.SOLAR_DAYS[month - 1] else: days = self.SOLAR_BIG_YEAR_DAY if self.__isSolarIntercalationYear(lunarData) else self.SOLAR_SMALL_YEAR_DAY return days def __getSolarDaysBeforeBaseYear(self, year): return self.__accumulateYearDays(year, self.__cumulativeSolarDays, lambda y: self.__getSolarDays(y)) def __getSolarDaysBeforeBaseMonth(self, year, month): days = 0 for baseMonth in range(1, month+1): days += self.__getSolarDays(year, baseMonth) return days def __getSolarAbsDays(self, year, month, day): days = self.__getSolarDaysBeforeBaseYear(year-1) + self.__getSolarDaysBeforeBaseMonth(year, month-1) + day days -= self.SOLAR_LUNAR_DAY_DIFF return days def __setSolarDateByLunarDate(self, lunarYear, lunarMonth, lunarDay, isIntercalation): absDays = self.__getLunarAbsDays(lunarYear, lunarMonth, lunarDay, isIntercalation) solarYear = 0 solarMonth = 0 solarDay = 0 solarYear = lunarYear if (absDays < self.__getSolarAbsDays(lunarYear+1, 1, 1)) else lunarYear+1 for month in range(12, 0, -1) : absDaysByMonth = self.__getSolarAbsDays(solarYear, month, 1) if (absDays >= absDaysByMonth) : solarMonth = month solarDay = absDays - absDaysByMonth +1 break self.solarYear = solarYear self.solarMonth = solarMonth self.solarDay = solarDay def __setLunarDateBySolarDate(self, solarYear, solarMonth, solarDay): absDays = self.__getSolarAbsDays(solarYear, solarMonth, solarDay) lunarYear = solarYear if (absDays >= self.__getLunarAbsDays(solarYear, 1, 1, False)) else solarYear-1 lunarMonth = 0 lunarDay = 0 isIntercalation = False for month in range(12, 0, -1) : absDaysByMonth = self.__getLunarAbsDays(lunarYear, month, 1, False) if absDays >= absDaysByMonth: lunarMonth = month if self.__getLunarIntercalationMonth(self.__getLunarData(lunarYear)) == month : isIntercalation = absDays >= self.__getLunarAbsDays(lunarYear, month, 1, True) lunarDay = absDays - self.__getLunarAbsDays(lunarYear, lunarMonth, 1, isIntercalation) + 1 break self.lunarYear = lunarYear self.lunarMonth = lunarMonth self.lunarDay = lunarDay self.isIntercalation = isIntercalation def __checkValidDate(self, isLunar, isIntercalation, year, month, day): isValid = False # Reject non-integer inputs before they corrupt the bitfield/index math. if not all(isinstance(value, numbers.Integral) for value in (year, month, day)): return isValid dateValue = year*10000 + month*100 + day #1582. 10. 5 ~ 1582. 10. 14 is not valid minValue = self.KOREAN_LUNAR_MIN_VALUE if isLunar else self.KOREAN_SOLAR_MIN_VALUE maxValue = self.KOREAN_LUNAR_MAX_VALUE if isLunar else self.KOREAN_SOLAR_MAX_VALUE if minValue <= dateValue and maxValue >= dateValue : if month > 0 and month < 13 and day > 0 : # A leap-month request is only valid when this year's actual # intercalation month matches the requested month. if isLunar and isIntercalation and (self.__getLunarIntercalationMonth(self.__getLunarData(year)) != month) : return isValid dayLimit = self.__getLunarDays(year, month, isIntercalation) if isLunar else self.__getSolarDays(year, month) # 1582.10.5 ~ 1582.10.14 were skipped by the Gregorian reform; the # month still ends at day 31, so only those 10 days are rejected. if isLunar == False and year == 1582 and month == 10 and day > 4 and day < 15 : return isValid if day <= dayLimit : isValid = True return isValid def setLunarDate(self, lunarYear, lunarMonth, lunarDay, isIntercalation) : isValid = False if self.__checkValidDate(True, isIntercalation, lunarYear, lunarMonth, lunarDay): self.lunarYear = lunarYear self.lunarMonth = lunarMonth self.lunarDay = lunarDay self.isIntercalation = isIntercalation and (self.__getLunarIntercalationMonth(self.__getLunarData(lunarYear)) == lunarMonth) self.__setSolarDateByLunarDate(lunarYear, lunarMonth, lunarDay, isIntercalation) isValid = True return isValid def setSolarDate(self, solarYear, solarMonth, solarDay): isValid = False if self.__checkValidDate(False, False, solarYear, solarMonth, solarDay) : self.solarYear = solarYear self.solarMonth = solarMonth self.solarDay = solarDay self.__setLunarDateBySolarDate(solarYear, solarMonth, solarDay) isValid = True return isValid def __getGapJa(self): # Sexagenary-cycle indices for the current lunar date as # ((yearCheongan, yearGanji), (monthCheongan, monthGanji), (dayCheongan, dayGanji)). # Pure: returns a fresh tuple instead of mutating instance state. When no # valid date is set (absDays <= 0) it returns the zero indices, preserving # the historical default for an unset converter. absDays = self.__getLunarAbsDays(self.lunarYear, self.lunarMonth, self.lunarDay, self.isIntercalation) if absDays <= 0 : return ((0, 0), (0, 0), (0, 0)) cheonganLen = len(self.KOREAN_CHEONGAN) ganjiLen = len(self.KOREAN_GANJI) monthCount = self.lunarMonth + 12 * (self.lunarYear - self.KOREAN_LUNAR_BASE_YEAR) yearInx = (((self.lunarYear + self.GAPJA_YEAR_CHEONGAN_OFFSET) - self.KOREAN_LUNAR_BASE_YEAR) % cheonganLen, ((self.lunarYear + self.GAPJA_YEAR_GANJI_OFFSET) - self.KOREAN_LUNAR_BASE_YEAR) % ganjiLen) monthInx = ((monthCount + self.GAPJA_MONTH_CHEONGAN_OFFSET) % cheonganLen, (monthCount + self.GAPJA_MONTH_GANJI_OFFSET) % ganjiLen) dayInx = ((absDays + self.GAPJA_DAY_CHEONGAN_OFFSET) % cheonganLen, (absDays + self.GAPJA_DAY_GANJI_OFFSET) % ganjiLen) return (yearInx, monthInx, dayInx) def getGapJaString(self) : (yearInx, monthInx, dayInx) = self.__getGapJa() gapjaStr = "%c%c%c %c%c%c %c%c%c" % (chr(self.KOREAN_CHEONGAN[yearInx[0]]), chr(self.KOREAN_GANJI[yearInx[1]]), chr(self.KOREAN_GAPJA_UNIT[0]), chr(self.KOREAN_CHEONGAN[monthInx[0]]), chr(self.KOREAN_GANJI[monthInx[1]]), chr(self.KOREAN_GAPJA_UNIT[1]), chr(self.KOREAN_CHEONGAN[dayInx[0]]), chr(self.KOREAN_GANJI[dayInx[1]]), chr(self.KOREAN_GAPJA_UNIT[2])) if self.isIntercalation == True : gapjaStr += " (%c%c)" % (chr(self.INTERCALATION_STR[0]), chr(self.KOREAN_GAPJA_UNIT[1])) return gapjaStr def getChineseGapJaString(self) : (yearInx, monthInx, dayInx) = self.__getGapJa() gapjaStr = "%c%c%c %c%c%c %c%c%c" % (chr(self.CHINESE_CHEONGAN[yearInx[0]]), chr(self.CHINESE_GANJI[yearInx[1]]), chr(self.CHINESE_GAPJA_UNIT[0]), chr(self.CHINESE_CHEONGAN[monthInx[0]]), chr(self.CHINESE_GANJI[monthInx[1]]), chr(self.CHINESE_GAPJA_UNIT[1]), chr(self.CHINESE_CHEONGAN[dayInx[0]]), chr(self.CHINESE_GANJI[dayInx[1]]), chr(self.CHINESE_GAPJA_UNIT[2])) if self.isIntercalation == True : gapjaStr += " (%c%c)" % (chr(self.INTERCALATION_STR[1]), chr(self.CHINESE_GAPJA_UNIT[1])) return gapjaStr korean_lunar_calendar_py-0.4.0/pyproject.toml000066400000000000000000000022021521400542100214360ustar00rootroot00000000000000[build-system] requires = ["setuptools>=77"] build-backend = "setuptools.build_meta" [project] name = "korean_lunar_calendar" dynamic = ["version"] description = "Convert the Korean lunar calendar to/from the Gregorian solar calendar (KARI standard)." readme = "README.md" requires-python = ">=3.6" license = "MIT" license-files = ["LICENSE"] authors = [{ name = "Jinil Lee", email = "usingsky@gmail.com" }] maintainers = [{ name = "Jinil Lee", email = "usingsky@gmail.com" }] keywords = ["calendar", "korean", "lunar", "solar", "음력", "양력", "달력"] classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries", ] [project.urls] Homepage = "https://github.com/usingsky/korean_lunar_calendar_py" Repository = "https://github.com/usingsky/korean_lunar_calendar_py" Issues = "https://github.com/usingsky/korean_lunar_calendar_py/issues" [tool.setuptools] packages = ["korean_lunar_calendar"] [tool.setuptools.dynamic] version = { attr = "korean_lunar_calendar.__version__" } korean_lunar_calendar_py-0.4.0/test_korean_lunar_calendar.py000066400000000000000000000105151521400542100244520ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Tests for KoreanLunarCalendar. Run from this directory: python -m unittest -v Expected values are taken from the KARI standard (cross-checked against the JavaScript/Java ports of this library). """ import datetime import os import sys import unittest sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from korean_lunar_calendar import KoreanLunarCalendar class SolarToLunarTest(unittest.TestCase): def test_known_conversions(self): cal = KoreanLunarCalendar() self.assertTrue(cal.setSolarDate(2017, 6, 24)) self.assertEqual(cal.LunarIsoFormat(), "2017-05-01 Intercalation") self.assertEqual(cal.getGapJaString(), "정유년 병오월 임오일 (윤월)") self.assertEqual(cal.getChineseGapJaString(), "丁酉年 丙午月 壬午日 (閏月)") self.assertTrue(cal.setSolarDate(1956, 3, 3)) self.assertEqual(cal.LunarIsoFormat(), "1956-01-21") self.assertEqual(cal.getGapJaString(), "병신년 경인월 기사일") self.assertEqual(cal.getChineseGapJaString(), "丙申年 庚寅月 己巳日") self.assertTrue(cal.setSolarDate(1000, 2, 13)) # minimum supported solar date self.assertEqual(cal.LunarIsoFormat(), "1000-01-01") self.assertEqual(cal.getGapJaString(), "경자년 무인월 기묘일") self.assertTrue(cal.setSolarDate(2050, 12, 31)) # maximum supported solar date self.assertEqual(cal.LunarIsoFormat(), "2050-11-18") self.assertEqual(cal.getGapJaString(), "경오년 무자월 을유일") self.assertTrue(cal.setSolarDate(1727, 4, 21)) # falls on a leap-month lunar date self.assertEqual(cal.LunarIsoFormat(), "1727-03-01 Intercalation") self.assertEqual(cal.getGapJaString(), "정미년 갑진월 정사일 (윤월)") self.assertEqual(cal.getChineseGapJaString(), "丁未年 甲辰月 丁巳日 (閏月)") class LunarToSolarTest(unittest.TestCase): def test_known_conversions(self): cal = KoreanLunarCalendar() self.assertTrue(cal.setLunarDate(2017, 5, 1, True)) # intercalation (leap) month self.assertEqual(cal.SolarIsoFormat(), "2017-06-24") self.assertTrue(cal.setLunarDate(1956, 1, 21, False)) self.assertEqual(cal.SolarIsoFormat(), "1956-03-03") self.assertTrue(cal.setLunarDate(1320, 11, 24, False)) self.assertEqual(cal.SolarIsoFormat(), "1321-01-01") # Leap (intercalation) month: lunar 1727-03-01 (leap) -> solar 1727-04-21 self.assertTrue(cal.setLunarDate(1727, 3, 1, True)) self.assertEqual(cal.SolarIsoFormat(), "1727-04-21") self.assertEqual(cal.getGapJaString(), "정미년 갑진월 정사일 (윤월)") class DefaultDateTest(unittest.TestCase): def test_defaults_to_today(self): cal = KoreanLunarCalendar() today = datetime.date.today() self.assertEqual(cal.solarYear, today.year) self.assertEqual(cal.solarMonth, today.month) self.assertEqual(cal.solarDay, today.day) class ValidationTest(unittest.TestCase): def test_invalid_dates_rejected(self): cal = KoreanLunarCalendar() # Out of supported range. self.assertFalse(cal.setLunarDate(999, 1, 1, False)) self.assertFalse(cal.setSolarDate(2051, 1, 1)) # 1582.10.5 ~ 1582.10.14 were removed by the Gregorian reform. self.assertFalse(cal.setSolarDate(1582, 10, 8)) # October 1582 still ends at day 31; days beyond that must be rejected. self.assertFalse(cal.setSolarDate(1582, 10, 35)) # A leap month can only be requested when the year actually has one in # that position; month 3 of 2017 is not a leap month. self.assertFalse(cal.setLunarDate(2017, 3, 1, True)) # Non-integer inputs are rejected instead of corrupting the math. self.assertFalse(cal.setSolarDate(2017, 6, 24.5)) def test_valid_boundary_dates_accepted(self): cal = KoreanLunarCalendar() self.assertTrue(cal.setLunarDate(1000, 1, 1, False)) self.assertTrue(cal.setSolarDate(2050, 12, 31)) self.assertTrue(cal.setSolarDate(1582, 10, 4)) # day just before the gap self.assertTrue(cal.setSolarDate(1582, 10, 15)) # day just after the gap self.assertTrue(cal.setLunarDate(2017, 5, 1, True)) # real leap month if __name__ == "__main__": unittest.main()