python3实现万年历(包括公历、农历、节气、节日)

转载:https://www.cnblogs.com/lanzhi/p/6468355.html

#!/usr/local/bin/python3
# coding=utf-8
# Created:     20/07/2012
# Copyright:   http://www.cnblogs.com/txw1958/
'''
A Chinese Calendar Library in Python
'''


import os, io, sys, re, time, datetime, base64

__version__ = "$Rev: 123 $"
__all__ = ['LunarDate']


solar_year          = 1900
solar_month         = 1
solar_day           = 31
solar_weekday          = 0

lunar_year          = 0
lunar_month         = 0
lunar_day           = 0
lunar_isLeapMonth   = False


class LunarDate(object):
    _startDate = datetime.date(1900, 1, 31)

    def __init__(self, year, month, day, isLeapMonth=False):
        global lunar_year
        global lunar_month
        global lunar_day
        global lunar_isLeapMonth

        lunar_year          = int(year)
        lunar_month         = int(month)
        lunar_day           = int(day)
        lunar_isLeapMonth   = bool(isLeapMonth)

        self.year = year
        self.month = month
        self.day = day
        self.isLeapMonth = bool(isLeapMonth)

    def __str__(self):
        return 'LunarDate(%d, %d, %d, %d)' % (self.year, self.month, self.day, self.isLeapMonth)

    __repr__ = __str__

    @staticmethod
    def fromSolarDate(year, month, day):
        solarDate = datetime.date(year, month, day)
        offset = (solarDate - LunarDate._startDate).days
        return LunarDate._fromOffset(offset)

    def toSolarDate(self):
        def _calcDays(yearInfo, month, day, isLeapMonth):
            isLeapMonth = int(isLeapMonth)
            res = 0
            ok = False
            for _month, _days, _isLeapMonth in self._enumMonth(yearInfo):
                if (_month, _isLeapMonth) == (month, isLeapMonth):
                    if 1 <= day <= _days:
                        res += day - 1
                        return res
                    else:
                        raise ValueError("day out of range")
                res += _days

            raise ValueError("month out of range")

        offset = 0
        if self.year < 1900 or self.year >= 2050:
            raise ValueError('year out of range [1900, 2050)')
        yearIdx = self.year - 1900
        for i in range(yearIdx):
            offset += yearDays[i]

        offset += _calcDays(yearInfos[yearIdx], self.month, self.day, self.isLeapMonth)
        return self._startDate + datetime.timedelta(days=offset)

    def __sub__(self, other):
        if isinstance(other, LunarDate):
            return self.toSolarDate() - other.toSolarDate()
        elif isinstance(other, datetime.date):
            return self.toSolarDate() - other
        elif isinstance(other, datetime.timedelta):
            res = self.toSolarDate() - other
            return LunarDate.fromSolarDate(res.year, res.month, res.day)
        raise TypeError

    def __rsub__(self, other):
        if isinstance(other, datetime.date):
            return other - self.toSolarDate()

    def __add__(self, other):
        if isinstance(other, datetime.timedelta):
            res = self.toSolarDate() + other
            return LunarDate.fromSolarDate(res.year, res.month, res.day)
        raise TypeError

    def __radd__(self, other):
        return self + other

    def __lt__(self, other):
        return self - other < datetime.timedelta(0)

    def __le__(self, other):
        return self - other <= datetime.timedelta(0)

    @classmethod
    def today(cls):
        res = datetime.date.today()
        return cls.fromSolarDate(res.year, res.month, res.day)

    @staticmethod
    def _enumMonth(yearInfo):
        months = [(i, 0) for i in range(1, 13)]
        leapMonth = yearInfo % 16
        if leapMonth == 0:
            pass
        elif leapMonth <= 12:
            months.insert(leapMonth, (leapMonth, 1))
        else:
            raise ValueError("yearInfo %r mod 16 should in [0, 12]" % yearInfo)

        for month, isLeapMonth in months:
            if isLeapMonth:
                days = (yearInfo >> 16) % 2 + 29
            else:
                days = (yearInfo >> (16 - month)) % 2 + 29
            yield month, days, isLeapMonth

    @classmethod
    def _fromOffset(cls, offset):
        def _calcMonthDay(yearInfo, offset):
            for month, days, isLeapMonth in cls._enumMonth(yearInfo):
                if offset < days:
                    break
                offset -= days
            return (month, offset + 1, isLeapMonth)

        offset = int(offset)

        for idx, yearDay in enumerate(Info.yearDays()):
            if offset < yearDay:
                break
            offset -= yearDay
        year = 1900 + idx

        yearInfo = Info.yearInfos[idx]
        month, day, isLeapMonth = _calcMonthDay(yearInfo, offset)
        return LunarDate(year, month, day, isLeapMonth)

class ChineseWord():
    def weekday_str(tm):
        a = '星期日 星期一 星期二 星期三 星期四 星期五 星期六'.split()
        return a[tm]

    def solarTerm(year, month, day):
        a = '小寒 大寒 立春 雨水 惊蛰 春分\
             清明 谷雨 立夏 小满 芒种 夏至\
             小暑 大暑 立秋 处暑 白露 秋分\
             寒露 霜降 立冬 小雪 大雪 冬至'.split()
        return

    def day_lunar(ld):
        a = '初一 初二 初三 初四 初五 初六 初七 初八 初九 初十\
             十一 十二 十三 十四 十五 十六 十七 十八 十九 廿十\
             廿一 廿二 廿三 廿四 廿五 廿六 廿七 廿八 廿九 三十'.split()
        return a[ld - 1]

    def month_lunar(le, lm):
        a = '正月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月'.split()
        if le:
            return "闰" + a[lm - 1]
        else:
            return a[lm - 1]

    def year_lunar(ly):
        y = ly
        tg = '甲 乙 丙 丁 戊 己 庚 辛 壬 癸'.split()
        dz = '子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥'.split()
        sx = '鼠 牛 虎 兔 龙 蛇 马 羊 猴 鸡 狗 猪'.split()
        return tg[(y - 4) % 10] + dz[(y - 4) % 12] + '[' + sx[(y - 4) % 12] + ']' + '年'

class Festival():
    #国历节日 *表示放假日
    def solar_Fstv(solar_month, solar_day):
        sFtv = [
        "0101#元旦节#",
        "0202#世界湿地日#",
        "0210#国际气象节#",
        "0214#情人节#",
        "0301#国际海豹日#",
        "0303#全国爱耳日#",
        "0305#学雷锋纪念日#",
        "0308#妇女节#",
        "0312#植树节# #孙中山逝世纪念日#",
        "0314#国际警察日#",
        "0315#消费者权益日#",
        "0317#中国国医节# #国际航海日#",
        "0321#世界森林日# #消除种族歧视国际日# #世界儿歌日#",
        "0322#世界水日#",
        "0323#世界气象日#",
        "0324#世界防治结核病日#",
        "0325#全国中小学生安全教育日#",
        "0330#巴勒斯坦国土日#",
        "0401#愚人节# #全国爱国卫生运动月(四月)# #税收宣传月(四月)#",
        "0407#世界卫生日#",
        "0422#世界地球日#",
        "0423#世界图书和版权日#",
        "0424#亚非新闻工作者日#",
        "0501#劳动节#",
        "0504#青年节#",
        "0505#碘缺乏病防治日#",
        "0508#世界红十字日#",
        "0512#国际护士节#",
        "0515#国际家庭日#",
        "0517#国际电信日#",
        "0518#国际博物馆日#",
        "0520#全国学生营养日#",
        "0523#国际牛奶日#",
        "0531#世界无烟日#",
        "0601#国际儿童节#",
        "0605#世界环境保护日#",
        "0606#全国爱眼日#",
        "0617#防治荒漠化和干旱日#",
        "0623#国际奥林匹克日#",
        "0625#全国土地日#",
        "0626#国际禁毒日#",
        "0701#中国共·产党诞辰# #香港回归纪念日# #世界建筑日#",
        "0702#国际体育记者日#",
        "0707#抗日战争纪念日#",
        "0711#世界人口日#",
        "0730#非洲妇女日#",
        "0801#建军节#",
        "0808#中国男子节(爸爸节)#",
        "0815#抗日战争胜利纪念#",
        "0908#国际扫盲日# #国际新闻工作者日#",
        "0909#毛·泽东逝世纪念#",
        "0910#中国教师节#",
        "0914#世界清洁地球日#",
        "0916#国际臭氧层保护日#",
        "0918#九·一八事变纪念日#",
        "0920#国际爱牙日#",
        "0927#世界旅游日#",
        "0928#孔子诞辰#",
        "1001#国庆节# #世界音乐日# #国际老人节#",
        "1002#国庆节假日# #国际和平与民主自由斗争日#",
        "1003#国庆节假日#",
        "1004#世界动物日#",
        "1006#老人节#",
        "1008#全国高血压日# #世界视觉日#",
        "1009#世界邮政日# #万国邮联日#",
        "1010#辛亥革命纪念日# #世界精神卫生日#",
        "1013#世界保健日# #国际教师节#",
        "1014#世界标准日#",
        "1015#国际盲人节(白手杖节)#",
        "1016#世界粮食日#",
        "1017#世界消除贫困日#",
        "1022#世界传统医药日#",
        "1024#联合国日#",
        "1031#世界勤俭日#",
        "1107#十月社会主义革命纪念日#",
        "1108#中国记者日#",
        "1109#全国消防安全宣传教育日#",
        "1110#世界青年节#",
        "1111#国际科学与和平周(本日所属的一周)#",
        "1112#孙中山诞辰纪念日#",
        "1114#世界糖尿病日#",
        "1116#国际宽容日#",
        "1117#国际大学生节# #世界学生节#",
        "1120#彝族年#",
        "1121#彝族年# #世界问候日# #世界电视日#",
        "1122#彝族年#",
        "1129#国际声援巴勒斯坦人民国际日#",
        "1201#世界艾滋病日#",
        "1203#世界残疾人日#",
        "1205#国际经济和社会发展志愿人员日#",
        "1208#国际儿童电视日#",
        "1209#世界足球日#",
        "1210#世界人权日#",
        "1212#西安事变纪念日#",
        "1213#南京大屠杀(1937年)纪念日#",
        "1220#澳门回归纪念#",
        "1221#国际篮球日#",
        "1224#平安夜#",
        "1225#圣诞节#",
        "1226#毛·泽东诞辰纪念日#"
        ]
        solar_month_str = str(solar_month) if solar_month > 9 else "0" + str(solar_month)
        solar_day_str = str(solar_day) if solar_day > 9 else "0" + str(solar_day)
        pattern = "(" + solar_month_str + solar_day_str + ")([\w+?\#?\(?\)?\d+\s?·?]*)"
        for solar_fstv_item in sFtv:
            result = re.search(pattern, solar_fstv_item)
            if result is not None:
                return result.group(2)


    def lunar_Fstv(lunar_month, lunar_day):
        #农历节日 *表示放假日
        #每年单独来算
        lFtv = [
        "0101#春节#",
        "0115#元宵节#",
        "0202#春龙节",
        #"0314#清明节#", #每年不一样,此为2012年,事实上为公历节日
        "0505#端午节#",
        "0707#七夕情人节#",
        "0715#中元节#",
        "0815#中秋节#",
        "0909#重阳节#",
        "1208#腊八节#",
        "1223#小年#",
        "1229#除夕#"   #每年不一样,此为2011年
        ]
        lunar_month_str = str(lunar_month) if lunar_month > 9 else "0" + str(lunar_month)
        lunar_day_str = str(lunar_day) if lunar_day > 9 else "0" + str(lunar_day)
        pattern = "(" + lunar_month_str + lunar_day_str + ")([\w+?\#?\s?]*)"
        for lunar_fstv_item in lFtv:
            result = re.search(pattern, lunar_fstv_item)
            if result is not None:
                return result.group(2)

    #国历节日 *表示放假日
    def weekday_Fstv(solar_month, solar_day, solar_weekday):
        #某月的第几个星期几
        wFtv = [
        "0150#世界防治麻风病日#", #一月的最后一个星期日(月倒数第一个星期日)
        "0520#国际母亲节#",
        "0530#全国助残日#",
        "0630#父亲节#",
        "0730#被奴役国家周#",
        "0932#国际和平日#",
        "0940#国际聋人节# #世界儿童日#",
        "0950#世界海事日#",
        "1011#国际住房日#",
        "1013#国际减轻自然灾害日(减灾日)#",
        "1144#感恩节#"]

        #7,14等应该属于1, 2周,能整除的那天实际属于上一周,做个偏移
        offset = -1 if solar_day % 7 == 0 else 0
        #计算当前日属于第几周,得出来从0开始计周,再向后偏移1
        weekday_ordinal = solar_day // 7 + offset + 1

        solar_month_str = str(solar_month) if solar_month > 9 else "0" + str(solar_month)
        solar_weekday_str = str(weekday_ordinal) + str(solar_weekday)

        pattern = "(" + solar_month_str + solar_weekday_str + ")([\w+?\#?\s?]*)"
        for weekday_fstv_item in wFtv:
            result = re.search(pattern, weekday_fstv_item)
            if result is not None:
                return result.group(2)

        #如何计算某些最后一个星期几的情况,..........

    #24节气
    def solar_Term(solar_month, solar_day):
        #每年数据不一样,此为2012年内的数据
        stFtv = [
        "0106#小寒#",
        "0120#大寒#",
        "0204#立春#",
        "0219#雨水#",
        "0305#惊蛰#",
        "0320#春分#",
        "0404#清明#",
        "0420#谷雨#",
        "0505#立夏#",
        "0521#小满#",
        "0605#芒种#",
        "0621#夏至#",
        "0707#小暑#",
        "0722#大暑#",
        "0807#立秋#",
        "0823#处暑#",
        "0907#白露#",
        "0922#秋分#",
        "1008#寒露#",
        "1023#霜降#",
        "1107#立冬#",
        "1122#小雪#",
        "1206#大雪#",
        "1221#冬至#",
        ]
        solar_month_str = str(solar_month) if solar_month > 9 else "0" + str(solar_month)
        solar_day_str = str(solar_day) if solar_day > 9 else "0" + str(solar_day)
        pattern = "(" + solar_month_str + solar_day_str + ")([\w+?\#?]*)"
        for solarTerm_fstv_item in stFtv:
            result = re.search(pattern, solarTerm_fstv_item)
            if result is not None:
                return result.group(2)

class Info():
    yearInfos = [
        #    /* encoding:
        #               b bbbbbbbbbbbb bbbb
        #       bit#    1 111111000000 0000
        #               6 543210987654 3210
        #               . ............ ....
        #       month#    000000000111
        #               M 123456789012   L
        #
        #    b_j = 1 for long month, b_j = 0 for short month
        #    L is the leap month of the year if 1<=L<=12; NO leap month if L = 0.
        #    The leap month (if exists) is long one iff M = 1.
        #    */
        0x04bd8,                                    #   /* 1900 */
        0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950,#   /* 1905 */
        0x16554, 0x056a0, 0x09ad0, 0x055d2, 0x04ae0,#   /* 1910 */
        0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540,#   /* 1915 */
        0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970,#   /* 1920 */
        0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54,#   /* 1925 */
        0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566,#   /* 1930 */
        0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60,#   /* 1935 */
        0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0,#   /* 1940 */
        0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0,#   /* 1945 */
        0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0,#   /* 1950 */
        0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573,#   /* 1955 */
        0x052d0, 0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6,#   /* 1960 */
        0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260,#   /* 1965 */
        0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0,#   /* 1970 */
        0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250,#   /* 1975 */
        0x0d558, 0x0b540, 0x0b5a0, 0x195a6, 0x095b0,#   /* 1980 */
        0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50,#   /* 1985 */
        0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5,#   /* 1990 */
        0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58,#   /* 1995 */
        0x05ac0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960,#   /* 2000 */
        0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0,#   /* 2005 */
        0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950,#   /* 2010 */
        0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0,#   /* 2015 */
        0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954,#   /* 2020 */
        0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6,#   /* 2025 */
        0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, 0x05aa0,#   /* 2030 */
        0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0,#   /* 2035 */
        0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0,#   /* 2040 */
        0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0,#   /* 2045 */
        0x0aa50, 0x1b255, 0x06d20, 0x0ada0          #   /* 2049 */
        ]

    def yearInfo2yearDay(yearInfo):
        yearInfo = int(yearInfo)

        res = 29 * 12

        leap = False
        if yearInfo % 16 != 0:
            leap = True
            res += 29

        yearInfo //= 16

        for i in range(12 + leap):
            if yearInfo % 2 == 1:
                res += 1
            yearInfo //= 2
        return res

    def yearDays():
        yearDays = [Info.yearInfo2yearDay(x) for x in Info.yearInfos]
        return yearDays

    def day2LunarDate(offset):
        offset = int(offset)
        res = LunarDate()

        for idx, yearDay in enumerate(yearDays()):
            if offset < yearDay:
                break
            offset -= yearDay
        res.year = 1900 + idx

class SolarDate():

    def __init__(self):
        global solar_year
        global solar_month
        global solar_day
        global solar_weekday

        solar_year      = int(time.strftime("%Y", time.localtime()))
        solar_month     = int(time.strftime("%m", time.localtime()))
        solar_day       = int(time.strftime("%d", time.localtime()))
        solar_weekday   = int(time.strftime("%w", time.localtime()))

        self.year = solar_year
        self.month = solar_month
        self.day = solar_day
        self.weekday = solar_weekday

    def __str__(self):
        return 'LunarDate(%d, %d, %d, %d)' % (self.year, self.month, self.day, self.isLeapMonth)


def getCalendar_today():
    solar = SolarDate()
    LunarDate.fromSolarDate(solar_year, solar_month, solar_day)

    festival = ""

    if Festival.solar_Term(solar_month, solar_day):
        festival = festival + " 今日节气:" + Festival.solar_Term(solar_month, solar_day)
    if Festival.solar_Fstv(solar_month, solar_day):
        festival = festival + " 公历节日:" + Festival.solar_Fstv(solar_month, solar_day)
    if Festival.weekday_Fstv(solar_month, solar_day, solar_weekday):
        if festival.find("公历节日") == -1:
            festival = festival + " 公历节日:" + Festival.weekday_Fstv(solar_month, solar_day, solar_weekday)
        else:
            festival = festival + " " + Festival.weekday_Fstv(solar_month, solar_day, solar_weekday)
    if Festival.lunar_Fstv(lunar_month, lunar_day):
        festival = festival + " 农历节日:" + Festival.lunar_Fstv(lunar_month, lunar_day)

    twitter = \
    "今天是" + str(solar_year) + "年" + str(solar_month) + "月" + str(solar_day) + "日" + " " \
    + ChineseWord.weekday_str(solar_weekday) + " 农历" + ChineseWord.year_lunar(lunar_year) \
    + ChineseWord.month_lunar(lunar_isLeapMonth,lunar_month) \
    + ChineseWord.day_lunar(lunar_day) + festival
    print(twitter)
    return twitter

def getCalendar_all_day():
    #solar = SolarDate()
    global solar_year
    global solar_month
    global solar_day
    global solar_weekday

    solar_year = 2012
    solar_month = 1
    weekday_offset = 0 #1月1号星期几?
    index_day = 0
    for solar_month in range(1, 13):
        if solar_month in [1, 3, 5, 7, 8, 10, 12]:
            solar_day_max = 31
        elif solar_month in [4, 6, 9, 11]:
            solar_day_max = 30
        elif solar_month == 2:
            if ((solar_year % 4 == 0) and (solar_year % 100 != 0)) or (solar_year % 400 == 0 ):
                solar_day_max = 29
            else:
                solar_day_max = 28
        else:
            None

        for solar_day in range(1, solar_day_max + 1):
            index_day += 1
            solar_weekday = (index_day )% 7 +  - 1
            solar_weekday = 0 if solar_weekday == 7 else solar_weekday
            solar_weekday = 6 if solar_weekday == -1 else solar_weekday

            LunarDate.fromSolarDate(solar_year, solar_month, solar_day)
            festival = ""

            if Festival.solar_Term(solar_month, solar_day):
                festival = festival + " 节气:" + Festival.solar_Term(solar_month, solar_day)
            if Festival.solar_Fstv(solar_month, solar_day):
                festival = festival + " 节日:" + Festival.solar_Fstv(solar_month, solar_day)
            if Festival.weekday_Fstv(solar_month, solar_day, solar_weekday):
                if festival.find("节日") == -1:
                    festival = festival + " 节日:" + Festival.weekday_Fstv(solar_month, solar_day, solar_weekday)
                else:
                    festival = festival + " " + Festival.weekday_Fstv(solar_month, solar_day, solar_weekday)
            if Festival.lunar_Fstv(lunar_month, lunar_day):
                if festival.find("节日") == -1:
                    festival = festival + " 节日:" + Festival.lunar_Fstv(lunar_month, lunar_day)
                else:
                    festival = festival + " " + Festival.lunar_Fstv(lunar_month, lunar_day)

            index_yy = str(solar_year)
            if int(solar_month) < 10:
                index_mm = "0" + str(solar_month)
            else:
                index_mm = str(solar_month)
            if int(solar_day) < 10:
                index_dd = "0" + str(solar_day)
            else:
                index_dd = str(solar_day)

            index_yyddmm = index_yy + index_mm + index_dd


            twitter = ("message(" + str(index_yyddmm) + ') = "') + \
            str(solar_year) + "年" + str(solar_month) + "月" + str(solar_day) + "日" + " " \
            + ChineseWord.weekday_str(solar_weekday) + " 农历" + ChineseWord.year_lunar(lunar_year) \
            + ChineseWord.month_lunar(lunar_isLeapMonth,lunar_month) \
            + ChineseWord.day_lunar(lunar_day) + festival + '"'
            print(twitter)

    return twitter


def main():
    "main function"
    print(base64.b64decode(b'Q29weXJpZ2h0IChjKSAyMDEyIERvdWN1YmUgSW5jLiBBbGwgcmlnaHRzIHJlc2VydmVkLg==').decode())
    getCalendar_all_day()
    getCalendar_today()

if __name__ == '__main__':
    main()

你可能感兴趣的:(python3实现万年历(包括公历、农历、节气、节日))