我的手机没有农历日历,想搜索一个WAP版的方便随时查看,但是一直以来都没看到合适的。刚好搜索到一个免费的python源代码。于是就把它移植和改造了一下,使它可以运行在 Google App Engine的环境,并增加了 Web 交互功能。Google App Engine以前也没正式用过,对一个没写过Python程序的眼光看来,感觉还是比较容易使用。
开发及改造过程
先花了几分钟看了文档中的 Hello World并跑起来,将找到的Python的农历代码替换了 Hello World 原来的程序,未料却碰到中文问题报错:
<type ‘exceptions.UnicodeEncodeError’>: ‘ascii’ codec can’t encode characters in position 1-2: ordinal not in range(128)
问了一下arbow, 发现将 u”中文” 改成 “中文”就搞定,原来是自己过度优化。然后把原来程序中的 print 改成了 IoString.write, 方便 response 输出。并增加向前和向后翻页功能,每一页是一月。App Engine 中的request, response 类似JSP中的request, response,所以很容易理解。
在本地调试通过,上传到服务器。用手机访问,OK
源码
# coding=utf-8 # Chinese Calendar for App Engine # author Tim: iso1600 (at) gmail (dot) com, the Chinese calendar code searched from Internet. from google.appengine.api import users from google.appengine.ext import webapp from google.appengine.ext.webapp.util import run_wsgi_app g_lunar_month_day = [ 0x4ae0, 0xa570, 0x5268, 0xd260, 0xd950, 0x6aa8, 0x56a0, 0x9ad0, 0x4ae8, 0x4ae0, #1910 0xa4d8, 0xa4d0, 0xd250, 0xd548, 0xb550, 0x56a0, 0x96d0, 0x95b0, 0x49b8, 0x49b0, #1920 0xa4b0, 0xb258, 0x6a50, 0x6d40, 0xada8, 0x2b60, 0x9570, 0x4978, 0x4970, 0x64b0, #1930 0xd4a0, 0xea50, 0x6d48, 0x5ad0, 0x2b60, 0x9370, 0x92e0, 0xc968, 0xc950, 0xd4a0, #1940 0xda50, 0xb550, 0x56a0, 0xaad8, 0x25d0, 0x92d0, 0xc958, 0xa950, 0xb4a8, 0x6ca0, #1950 0xb550, 0x55a8, 0x4da0, 0xa5b0, 0x52b8, 0x52b0, 0xa950, 0xe950, 0x6aa0, 0xad50, #1960 0xab50, 0x4b60, 0xa570, 0xa570, 0x5260, 0xe930, 0xd950, 0x5aa8, 0x56a0, 0x96d0, #1970 0x4ae8, 0x4ad0, 0xa4d0, 0xd268, 0xd250, 0xd528, 0xb540, 0xb6a0, 0x96d0, 0x95b0, #1980 0x49b0, 0xa4b8, 0xa4b0, 0xb258, 0x6a50, 0x6d40, 0xada0, 0xab60, 0x9370, 0x4978, #1990 0x4970, 0x64b0, 0x6a50, 0xea50, 0x6b28, 0x5ac0, 0xab60, 0x9368, 0x92e0, 0xc960, #2000 0xd4a8, 0xd4a0, 0xda50, 0x5aa8, 0x56a0, 0xaad8, 0x25d0, 0x92d0, 0xc958, 0xa950, #2010 0xb4a0, 0xb550, 0xb550, 0x55a8, 0x4ba0, 0xa5b0, 0x52b8, 0x52b0, 0xa930, 0x74a8, #2020 0x6aa0, 0xad50, 0x4da8, 0x4b60, 0x9570, 0xa4e0, 0xd260, 0xe930, 0xd530, 0x5aa0, #2030 0x6b50, 0x96d0, 0x4ae8, 0x4ad0, 0xa4d0, 0xd258, 0xd250, 0xd520, 0xdaa0, 0xb5a0, #2040 0x56d0, 0x4ad8, 0x49b0, 0xa4b8, 0xa4b0, 0xaa50, 0xb528, 0x6d20, 0xada0, 0x55b0, #2050 ] g_lunar_month = [ 0x00, 0x50, 0x04, 0x00, 0x20, #1910 0x60, 0x05, 0x00, 0x20, 0x70, #1920 0x05, 0x00, 0x40, 0x02, 0x06, #1930 0x00, 0x50, 0x03, 0x07, 0x00, #1940 0x60, 0x04, 0x00, 0x20, 0x70, #1950 0x05, 0x00, 0x30, 0x80, 0x06, #1960 0x00, 0x40, 0x03, 0x07, 0x00, #1970 0x50, 0x04, 0x08, 0x00, 0x60, #1980 0x04, 0x0a, 0x00, 0x60, 0x05, #1990 0x00, 0x30, 0x80, 0x05, 0x00, #2000 0x40, 0x02, 0x07, 0x00, 0x50, #2010 0x04, 0x09, 0x00, 0x60, 0x04, #2020 0x00, 0x20, 0x60, 0x05, 0x00, #2030 0x30, 0xb0, 0x06, 0x00, 0x50, #2040 0x02, 0x07, 0x00, 0x50, 0x03 #2050 ] #================================================================================== from datetime import date, datetime from calendar import Calendar as Cal START_YEAR = 1901 def is_leap_year(tm): y = tm.year return (not (y % 4)) and (y % 100) or (not (y % 400)) def show_month(tm, out): (ly, lm, ld) = get_ludar_date(tm) out.write('\r\n') out.write("%d年%d月%d日" % (tm.year, tm.month, tm.day)) out.write(" " + week_str(tm)) out.write("|农历:" + y_lunar(ly) + m_lunar(lm) + d_lunar(ld)) out.write('\r\n') out.write("日|一|二|三|四|五|六\r\n") c = Cal() ds = [d for d in c.itermonthdays(tm.year, tm.month)] count = 0 for d in ds: count += 1 if d == 0: out.write("| ") continue (ly, lm, ld) = get_ludar_date(datetime(tm.year, tm.month, d)) if count % 7 == 0: out.write('\r\n') d_str = str(d) if d == tm.day: d_str = "*" + d_str out.write(d_str + d_lunar(ld) + "|") out.write('\r\n') def this_month(out): show_month(datetime.now(), out) def week_str(tm): a = '星期一 星期二 星期三 星期四 星期五 星期六 星期日'.split() return a[tm.weekday()] def d_lunar(ld): a = '初一 初二 初三 初四 初五 初六 初七 初八 初九 初十\ 十一 十二 十三 十四 十五 十六 十七 十八 十九 廿十\ 廿一 廿二 廿三 廿四 廿五 廿六 廿七 廿八 廿九 三十'.split() return a[ld - 1] def m_lunar(lm): a = '正月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月'.split() return a[lm - 1] def y_lunar(ly): y = ly tg = '甲 乙 丙 丁 戊 己 庚 辛 壬 癸'.split() dz = '子 丑 寅 卯 辰 巳 午 未 申 酉 戌 亥'.split() sx = '鼠 牛 虎 免 龙 蛇 马 羊 猴 鸡 狗 猪'.split() return tg[(y - 4) % 10] + dz[(y - 4) % 12] + ' ' + sx[(y - 4) % 12] + '年' def date_diff(tm): return (tm - datetime(1901, 1, 1)).days def get_leap_month(lunar_year): flag = g_lunar_month[(lunar_year - START_YEAR) / 2] if (lunar_year - START_YEAR) % 2: return flag & 0x0f else: return flag >> 4 def lunar_month_days(lunar_year, lunar_month): if (lunar_year < START_YEAR): return 30 high, low = 0, 29 iBit = 16 - lunar_month; if (lunar_month > get_leap_month(lunar_year) and get_leap_month(lunar_year)): iBit -= 1 if (g_lunar_month_day[lunar_year - START_YEAR] & (1 << iBit)): low += 1 if (lunar_month == get_leap_month(lunar_year)): if (g_lunar_month_day[lunar_year - START_YEAR] & (1 << (iBit -1))): high = 30 else: high = 29 return (high, low) def lunar_year_days(year): days = 0 for i in range(1, 13): (high, low) = lunar_month_days(year, i) days += high days += low return days def get_ludar_date(tm): span_days = date_diff(tm) if (span_days <49): year = START_YEAR - 1 if (span_days <19): month = 11; day = 11 + span_days else: month = 12; day = span_days - 18 return (year, month, day) span_days -= 49 year, month, day = START_YEAR, 1, 1 tmp = lunar_year_days(year) while span_days >= tmp: span_days -= tmp year += 1 tmp = lunar_year_days(year) (foo, tmp) = lunar_month_days(year, month) while span_days >= tmp: span_days -= tmp if (month == get_leap_month(year)): (tmp, foo) = lunar_month_days(year, month) if (span_days < tmp): return (0, 0, 0) span_days -= tmp month += 1 (foo, tmp) = lunar_month_days(year, month) day += span_days return (year, month, day) from datetime import date, timedelta class MainPage(webapp.RequestHandler): def get(self): fontsize = 1 pc = False if self.request.headers['User-Agent'].find("MSIE") >= 0: pc = True elif self.request.headers['User-Agent'].find("Firefox") >= 0: pc = True if pc: fontsize = 2 mon = int(self.request.get("mon", default_value='0')) nm = datetime.now() out = self.response.out if mon != 0: dt = datetime.now() mo = dt.month yr = dt.year nm = datetime(yr, mo, 1) + timedelta(days=31) * mon nm = datetime(nm.year, nm.month, 1) if pc == False: self.response.headers['Content-Type'] = 'application/xhtml+xml; charaset=UTF8' out.write("""<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>WAP/Mobile/Web农历</title> </head> <body> <h3>WAP/Mobile/Web农历</h3> <div style="font-size:%dex; line-height:2ex;"> <a href="/nongli?mon=%d">上一月</a> <a href="/nongli?mon=%d">下一月</a> <a href="/">Tim's App Engine</a> </div> <pre style="font-size:%dex; line-height:2ex;">""" % (fontsize, mon - 1, mon + 1, fontsize)) show_month(nm, out) self.response.out.write("</pre></body></html>") application = webapp.WSGIApplication( [('/nongli', MainPage), ('/nongli', MainPage)], debug=True) def main(): run_wsgi_app(application) if __name__ == "__main__": main()
访问地址
另外排版有点乱,因为为了适应手机的屏幕,所以把空格都去掉了。下一步考虑用个TABLE套进去,这样界面会更整洁一些。
在我的手机上面访问,文字都挤一块了,无论是自带的浏览器或Opera Mini。320*240的分辨率
我故意把文字挤在一块的,因为不挤,我的手机上每一周都要折行,没法看。
屏幕大的可以考虑用 table套日期, 这样会非常整齐。
有bug,比如你跳到7月(http://tim-yang.appspot.com/nongli?mon=3)
2009年7月1日 星期三|农历:庚申 猴年十二月三十
日 一 二 三 四 五 六
*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初八 30初九 31初十