파이썬 갖고 놀며 만든 만년력

#-------------------------------+----------------------------------------------------------------
from    datetime import datetime
import  math

class   cal:
    celes       = '甲乙丙丁戊己庚辛壬癸包焦'
    topos       = '寅卯辰巳午未申酉戌亥子丑'
    dayws       = '火水木金土日月'

    Auto        = 'A'
    Julian      = 'J'
    Gregorian   = 'G'

    secs_1m     = 60
    secs_1h     = secs_1m * 60
    secs_1d     = secs_1h * 24
    days_5m     = 153
    days_j1y    = 365.25
    days_j4y    = int( days_j1y * 4 )
    days_g1y    = 365.2425
    days_g400y  = int( days_g1y * 400 )
    years_jd2ad = 4712
    years_j400f = int( math.ceil( years_jd2ad / 400 ) * 400 )
    jd_base     = ( years_j400f - years_jd2ad ) * days_j1y - 60
    jd_base_ad  = years_jd2ad * days_j1y + 60
    j2g         = 38

    def __init__( self, jd = 0. ):
        self.jd = jd

    def __str__( self ):
        return  f'JD {self.jd:f}'

    @staticmethod
    def _time( h, n, s ):
        return  ( h * cal.secs_1h + n * cal.secs_1m + s ) / cal.secs_1d - 0.5

    @staticmethod
    def g( gy = -4712, gm = 1, d = 1, h = 12, n = 0, s = 0 ):
        y = gy - ( gm < 3 ) + cal.years_j400f
        return  cal( cal.j( gy, gm, d, h, n, s ).jd - y // 100 + y // 400 + cal.j2g )

    @staticmethod
    def j( jy = -4712, jm = 1, d = 1, h = 12, n = 0, s = 0 ):
        y = jy - ( jm < 3 )
        m = ( jm + 9 ) % 12
        return  cal( d - 1 + ( cal.days_5m * m + 2 ) // 5 +
                     y * cal.days_j4y // 4 + cal.jd_base_ad + cal._time( h, n, s ) )

    @staticmethod
    def a( y = -4712, m = 1, d = 1, h = 12, n = 0, s = 0 ):
        return  cal.j( y, m, d, h, n, s ) if y * 10000 + m * 100 + d < 15821015 else \
                cal.g( y, m, d, h, n, s )

    @staticmethod
    def _ymd( c, b = 0 ):
        y = ( 4 * c + 3 ) // cal.days_j4y
        e = c - ( cal.days_j4y * y ) // 4
        m = ( 5 * e + 2 ) // cal.days_5m

        D = e - ( cal.days_5m * m + 2 ) // 5 + 1
        M = ( m + 2 ) % 12 + 1
        Y = b + y - cal.years_j400f + ( M < 3 )
        return  Y, M, D

    @staticmethod
    def hns( jd ):
        ss  = int( ( jd + 0.5 ) * cal.secs_1d ) % cal.secs_1d
        return  ss // cal.secs_1h, ss // cal.secs_1m % 60, ss % 60

    def gdt( self ):
        a = int( self.jd + 0.5 + cal.jd_base ) - cal.j2g
        b = ( 4 * a + 3 ) // cal.days_g400y
        c = a - ( b * cal.days_g400y ) // 4
        return  self._ymd( c, b * 100 ), self.hns( self.jd )

    def jdt( self ):
        return  self._ymd( int( self.jd + 0.5 + cal.jd_base ) ), self.hns( self.jd )

    def __sub__( self, other ):
        return  self.jd - other.jd

    def __add__( self, other ):
        return  cal( self.jd + other )

    def day( self ):
        return  cal.dayws[ int( self.jd - 0.5 ) % 7 ]

    def kanji( self ):
        return  cal.celes[ int( self.jd - 0.5 ) % 10 ] + cal.topos[ int( self.jd - 0.5 ) % 12 ]

    def str( self, show_zero_heading = True ):
        msg = 'J.' if self.jd < cal.g( 1582, 10, 15, 0 ).jd else 'G.'
        d, t = ( self.jdt() ) if msg[0] == 'J' else ( self.gdt() )
        l = '02d' if show_zero_heading else 'd'
        msg += 'AD ' if d[0] > 0 else 'BC '
        msg += str( d[0] if d[0] > 0 else ( 1 - d[0] ) ) + '.'
        msg += f'{d[1]:{l}}.{d[2]:{l}}.{t[0]:{l}}:{t[1]:{l}}:{t[2]:{l}}'
        return  msg

    @staticmethod
    def leap( y, mode = Auto ):
        if mode == cal.Auto:
            mode = cal.Julian if y < 1583 else cal.Gregorian
        return  ( y % 4 == 0 ) if mode == cal.Julian else \
            ( ( y % 4 == 0 ) and ( y % 100 != 0 or y % 400 == 0 ) )

    @staticmethod
    def days_in( y, m, mode = Auto ):
        M = ( m + 9 ) % 12
        M1 = ( M + 1 )
        DS = ( cal.days_5m * M1 + 2 ) // 5 - ( cal.days_5m * M + 2 ) // 5
        return  DS + ( cal.leap( y, mode ) - 2 ) * ( m == 2 )

    @staticmethod
    def endar( y, m, mode = Auto, more = False ):
        if mode == cal.Auto:
            mode = cal.Julian if y * 100 + m < 158210 else cal.Gregorian
        h = cal.j( y, m, 1 ) if mode == cal.Julian else cal.g( y, m, 1 )
        b = -( int( h.jd + 1 ) % 7 )
        e = cal.days_in( y, m, mode )

        lines = [ '' ]
        if more:
            lines.append( 'Julian' if mode == cal.Julian else 'Gregorian' )
        if y <= 0:
            head = 'BC '
            y = -y + 1
        else: head = 'AD '
        lines.append( f'{head}{str(y):14}.{m:2d}' )
        lines.append( '日 月 火 水 木 金 土' if more else 'SU MO TU WE TH FR SA' )

        while b < e:
            msg = ''
            kmsg = [ '', '' ]
            for c in range( 7 ):
                kanji = cal( h.jd + b ).kanji()
                kmsg[ 0 ] += kanji[ 0 ] + ' '
                kmsg[ 1 ] += kanji[ 1 ] + ' '
                b += 1
                msg += f'{b:2d} ' if 0 < b <= e else '   '
            lines.append( msg )
            if more:
                lines.append( kmsg[ 0 ] )
                lines.append( kmsg[ 1 ] )
                lines.append( '-' * 20 )
        return  lines

    @staticmethod
    def datetime( dt ):
        return  cal.a( dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second )

    @staticmethod
    def now():
        return  cal.datetime( datetime.today() )

날짜계산 같은건 당연히 지원하고,
간단한 문자 달력도 제공함.

print( '\n'.join( cal.endar( 2020, 2 ) ) )
AD 2020            2
SU MO TU WE TH FR SA
__ __ __ __ __ __  1 
 2  3  4  5  6  7  8 
 9 10 11 12 13 14 15 
16 17 18 19 20 21 22 
23 24 25 26 27 28 29 

이정돈 다들하잖음?

print( '\n'.join( cal.endar( -20202020202020, 2 ) ) )
BC 20202020202021  2
SU MO TU WE TH FR SA
__ __ __  1  2  3  4 
 5  6  7  8  9 10 11 
12 13 14 15 16 17 18 
19 20 21 22 23 24 25 
26 27 28 29 __ __ __ 

이런 짓도 함.

그런데, more 파라메터를 True 로 두면

print( '\n'.join( cal.endar( -20202020202020, 2, cal.Auto, True ) ) )

한자 전각 문자에 최적화 되어있음;

# 그레고리력 2020 년 3월 1일 에서 100일째 되는 날은 언제?
print( '100th', ( cal.g( 2020, 3, 1 ) + 99 ).gdt() )
# 오늘은 그레고리력 2020 년 7월 1일의 며칠 전이지?
print( 'D-', cal.g( 2020, 7, 1 ) - cal.now() )
# 그래고리력 생일로부터 며칠이나 살았나
print( 'dif', cal.now() - cal.g( 생일 ) )

g 멤버 함수 ( 그레고리력 생성자 ) 나 j 멤버 함수 ( 율리우스력 생성자 ) 를 통해
JD 를 산출하는 방식으로 그레고리력과 율리우스력이 통합되어 있음

# 고유 문자열 출력
print( cal.now().str() )
G.AD 2020.03.01.18:00:46
# 고유값 출력
print( cal.now() )
JD 2458910.751725

JD 는 Julian Day 라고 해서, 기원전 4713년 1월 1일을 0 으로 잡아 1일 기준으로 1씩 증가하는 숫자를 말함.

흔히 전산쪽 일하는 분들이 JD 라고 하면 매년 1월 1일을 기준의 증가일자로 받아들이는데 원래의 의미와는 다른 것.
그런데 표준대로면 정오를 0으로 잡아야 되는거 같은데…
나는 그냥 시간대로 적어서 표준과 표기가 12시간 차이날 수 있음;
나중에 확인해보고 필요하면 고쳐놓겠음. >_<

p.s. 표준이랑 맞춰둠

G.AD 2020.03.01.21:33:49
JD 2458910.398484
9 Likes

멋지네요!! ㅎㅎㅎㅎㅎ

1 Like

이미지

1 Like

우와

1 Like

ㅋㅋ 12인치 맥북이네유

:snake:

1 Like