Linux-shell实现阳历转农历(序)


好些天没有登陆邮箱,前几天上班打开一看垃圾箱中有一封邮件让我好激动,还是国外友人的英文邮件。^_^
大概内容是我早些时候写的一个阳历转农历的shell小程序,他在用的时候发现了bug,但是这个bug我在去年年底就修改了。而且,他给了此程序的具体出处,但并不是我发布此程序的地址,显然是XX拷贝过去的(管它XX是人还是机器),庆幸的是我在程序的log文件中加入了我的邮箱地址,此友人才能找到我。废话少说,入正题。

缘由

本脚本实现原理是查表法(因为公式有误差);基于农历新年为基准,对农历新年前后两个不同的农历进行计算。

写这个脚本之前是想在Linux 终端命令提示符中加入阳历及农历日期。在Ubuntu中有Lunar软件可以获取农历日期,但在Fedora或CentOS中并没有类似软件,所以就想自己来实现一个,但网上用其他语言写的一大把,如果再写没什么必要。所以就想用shell来写一个。

主要功能

将阳历转换为农历。

Ubuntu 12.04 LTS dash和bash都测试通过(在此系统中,默认的/bin/sh是连接到/bin/dash的,dash是一个小而相容于POSIX标准的Unix shell,bash进行了扩展)

CentOS 6.4 测试通过(很久以前的事了,限于手中的资源,现在就不再在此系统上测试,不知道现在的版本还能否通过)

Fedora 18 测试通过(很久以前的事了,限于手中的资源,现在就不再在此系统上测试,不知道现在的版本还能否通过)

参数要求

  • 参数数据为8位,其中年为4位,月和日各2位,不足前面补0,如2013年1月1号:20130101
  • 无参数时默认当前系统日期为需转换日期


农历是通过观测及推算而得出的历法,所以通过通用公式计算得到的农历时间多少会有误差,特别是时间范围比较大的时候,这种误差就不好再无视它了。

基本算法:以每年的农历新年为基准。新年后的农历年份对应于阳历所在的年份;新年前为上一个农历年份。

验证

如果想验证可通过以下网址进行验证:

http://www.herongyang.com/Year_zh/Program-Chinese-Calendar-Algorithm.html

数据来源

以下关键的农历元数据来源于:

http://www.cppblog.com/ctou45/archive/2012/08/21/187846.html

//0~4 共5bit 春节日 //5~6 共2bit 春节月 //7~19 共13bit 13个月的大小月情况(如果无闰月,最后位无效),大月为1,小月为0(从左到右) //20~23 共4bit 记录闰月的月份,如果没有闰月为0 0x04AE53,0x0A5748,0x5526BD,0x0D2650,0x0D9544, 0x46AAB9,0x056A4D,0x09AD42,0x24AEB6,0x04AE4A, //1901-1910 0x6A4DBE,0x0A4D52,0x0D2546,0x5D52BA,0x0B544E, 0x0D6A43,0x296D37,0x095B4B,0x749BC1,0x049754, //1911-1920 0x0A4B48,0x5B25BC,0x06A550,0x06D445,0x4ADAB8, 0x02B64D,0x095742,0x2497B7,0x04974A,0x664B3E, //1921-1930 0x0D4A51,0x0EA546,0x56D4BA,0x05AD4E,0x02B644, 0x393738,0x092E4B,0x7C96BF,0x0C9553,0x0D4A48, //1931-1940 0x6DA53B,0x0B554F,0x056A45,0x4AADB9,0x025D4D, 0x092D42,0x2C95B6,0x0A954A,0x7B4ABD,0x06CA51, //1941-1950 0x0B5546,0x555ABB,0x04DA4E,0x0A5B43,0x352BB8, 0x052B4C,0x8A953F,0x0E9552,0x06AA48,0x7AD53C, //1951-1960 0x0AB54F,0x04B645,0x4A5739,0x0A574D,0x052642, 0x3E9335,0x0D9549,0x75AABE,0x056A51,0x096D46, //1961-1970 0x54AEBB,0x04AD4F,0x0A4D43,0x4D26B7,0x0D254B, 0x8D52BF,0x0B5452,0x0B6A47,0x696D3C,0x095B50, //1971-1980 0x049B45,0x4A4BB9,0x0A4B4D,0xAB25C2,0x06A554, 0x06D449,0x6ADA3D,0x0AB651,0x093746,0x5497BB, //1981-1990 0x04974F,0x064B44,0x36A537,0x0EA54A,0x86B2BF, 0x05AC53,0x0AB647,0x5936BC,0x092E50,0x0C9645, //1991-2000 0x4D4AB8,0x0D4A4C,0x0DA541,0x25AAB6,0x056A49, 0x7AADBD,0x025D52,0x092D47,0x5C95BA,0x0A954E, //2001-2010 0x0B4A43,0x4B5537,0x0AD54A,0x955ABF,0x04BA53, 0x0A5B48,0x652BBC,0x052B50,0x0A9345,0x474AB9, //2011-2020 0x06AA4C,0x0AD541,0x24DAB6,0x04B64A,0x69573D, 0x0A4E51,0x0D2646,0x5E933A,0x0D534D,0x05AA43, //2021-2030 0x36B537,0x096D4B,0xB4AEBF,0x04AD53,0x0A4D48, 0x6D25BC,0x0D254F,0x0D5244,0x5DAA38,0x0B5A4C, //2031-2040 0x056D41,0x24ADB6,0x049B4A,0x7A4BBE,0x0A4B51, 0x0AA546,0x5B52BA,0x06D24E,0x0ADA42,0x355B37, //2041-2050 0x09374B,0x8497C1,0x049753,0x064B48,0x66A53C, 0x0EA54F,0x06B244,0x4AB638,0x0AAE4C,0x092E42, //2051-2060 0x3C9735,0x0C9649,0x7D4ABD,0x0D4A51,0x0DA545, 0x55AABA,0x056A4E,0x0A6D43,0x452EB7,0x052D4B, //2061-2070 0x8A95BF,0x0A9553,0x0B4A47,0x6B553B,0x0AD54F, 0x055A45,0x4A5D38,0x0A5B4C,0x052B42,0x3A93B6, //2071-2080 0x069349,0x7729BD,0x06AA51,0x0AD546,0x54DABA, 0x04B64E,0x0A5743,0x452738,0x0D264A,0x8E933E, //2081-2090 0x0D5252,0x0DAA47,0x66B53B,0x056D4F,0x04AE45, 0x4A4EB9,0x0A4D4C,0x0D1541,0x2D92B5 //2091-2099 View Code

 

主要代码

此程序包括如下几个文件:

  • lunar.sh 主脚本,具体实现
  • datebases 农历元数据
  • change.log 更改日志
  • readme 脚本说明及注意事项
  • shengxiao 生肖数据

主脚本

lunar.sh代码如下:

######################################################################### # File Name: lunar.sh # Author: snowsolf # E-mail: snowsolf@hotmail.com # Created Time: 2013年07月*********** ######################################################################### #!/bin/sh Version=1.0 Editor=snowsolf Email=snowsolf@hotmail.com # print help function Usage() { cat << EOF ============================================================== Valid date: 19010101 ~ 20991231 But 'date' program support: 19011215 ~ 20380119 -h, --help display this help and exit -V, --version output version information and exit Usage: $0 [-h|--help|-V|--version] | [date(yyyymmdd)] Examples: Usage input time: $0 20130101 Usage system time: $0 Editor: $Editor E-mail: $Email EOF exit 0 } ################################################################# #get year,month,day and day of year #system 'date' program support:19011215 ~ 20380119 ################################################################# function Date_data() { date_year=$(echo $DATE |sed 's/^\(.\{4\}\).*/\1/') date_month=$(echo $DATE |sed 's/.*\(..\)..$/\1/') date_day=$(echo $DATE |sed 's/.*\(..\)$/\1/') date_days=$(date -d $DATE +%j) } DATE=$@ # handle difference input case "$#" in 0) echo "No parameters!" echo -e "Usage system time: $(date +%Y-%m-%d)\n" DATE=$(date +%Y%m%d) Date_data ;; 1) date -d $DATE +%j > /dev/null || ((Usage && exit 0)) case "$1" in -h|--help) Usage ;; -V|--version) echo "$0: Version $Version" echo "Editor: $Editor" echo "E-mail: $Email" exit 0 ;; [1][9][0-9][0-9][0-9][0-9][0-9][0-9]|[2][0][0-9][0-9][0-9][0-9][0-9][0-9]) [ "$1" -ge "19010101" ] && [ "$1" -lt "19011215" ] || [ "$1" -gt "20380119" ] && [ "$1" -le "20991231" ] \ && echo -e "'date' program no support: $1\n" && Usage [ "$1" -ge "19000000" ] && [ "$1" -lt "19010101" ] || [ "$1" -gt "20991231" ] && [ "$1" -le "20999999" ] \ && echo -e "Invalid parameter: $1\n" && Usage Date_data ;; *) echo -e "Invalid parameter: $1\n" Usage ;; esac ;; *) echo -e "The number of parameter greater than one !\n" Usage ;; esac # lunar databases databases_path=databases # get lunar year lunar_year=$(sed /$date_year/!d $databases_path |sed 's/^\(....\).*/\1/') # get all for lunar year, and form hexadecimal to binary # include lunar year, month, day, and leap month lunar_year_data=$(sed /$date_year/!d $databases_path |sed 's/.*\ \(.*\)/\1/') lunar_year_data_bin=$(echo "ibase=16;obase=2;$lunar_year_data"|bc |sed -e :a -e 's/^.\{1,23\}$/0&/;ta') new_year_month_bin=$(echo $lunar_year_data_bin |sed -e 's/^.\{17\}\(.\{2\}\).*/\1/') new_year_month=$(echo "ibase=2;$new_year_month_bin"|bc |sed -e :a -e 's/^.\{1,1\}$/0&/;ta') new_year_day_bin=$(echo $lunar_year_data_bin |sed -e 's/.*\(.\{5\}\)$/\1/') new_year_day=$(echo "ibase=2;$new_year_day_bin"|bc |sed -e :a -e 's/^.\{1,1\}$/0&/;ta') new_year_days=$(date -d $date_year$new_year_month$new_year_day +%j) lunar_days=$(expr $date_days - $new_year_days + 1) # flag befor_or_after=0 if [ "$lunar_days" -le "0" ]; then befor_or_after=1 date_year=$(($date_year - 1)) lunar_year=$(sed /$date_year/!d $databases_path |sed 's/^\(....\).*/\1/') lunar_year_data=$(sed /$date_year/!d $databases_path |sed 's/.*\ \(.*\)/\1/') lunar_year_data_bin=$(echo "ibase=16;obase=2;$lunar_year_data"|bc |sed -e :a -e 's/^.\{1,23\}$/0&/;ta') fi lunar_leap_month_bin=$(echo $lunar_year_data_bin |sed -e 's/^\(.\{4\}\).*/\1/') lunar_leap_month=$(echo "ibase=2;$lunar_leap_month_bin"|bc) lunar_month_all_bin=$(echo $lunar_year_data_bin |sed -e 's/^.\{4\}\(.\{13\}\).*/\1/') [ "$lunar_leap_month" = "0" ] && lunar_month_all_bin=$(echo $lunar_year_data_bin |sed -e 's/^.\{4\}\(.\{12\}\).*/\1/') lunar_month_all=$(echo $lunar_month_all_bin |sed -e 's/0/29\ /g' |sed -e 's/1/30\ /g') if [ "$befor_or_after" = "0" ];then lunar_month=1 lunar_day=$lunar_days for i in $lunar_month_all do [ "$lunar_day" -eq "$i" ] && break [ "$lunar_day" -gt "$i" ] && lunar_day=$(($lunar_day - $i)) && lunar_month=$(($lunar_month + 1)) done else lunar_month=12 lunar_day=$((-$lunar_days)) lunar_month_all_bin=$(echo $lunar_month_all_bin |rev) lunar_month_all=$(echo $lunar_month_all_bin |sed -e 's/0/29\ /g' |sed -e 's/1/30\ /g') for i in $lunar_month_all do [ "$lunar_day" -eq "$i" ] && break if [ "$lunar_day" -gt "$i" ]; then lunar_day=$(($lunar_day - $i)) lunar_month=$(($lunar_month - 1)) else lunar_day=$(($i - $lunar_day)) break fi done fi # output if [ "$lunar_leap_month" = "0" ]; then echo $lunar_year-$lunar_month-$lunar_day else if [ "$lunar_leap_month" -ge "$lunar_month" ]; then echo $lunar_year-$lunar_month-$lunar_day elif [ "$befor_or_after" = "0" ]; then if [ "$(($lunar_leap_month + 1))" = "$lunar_month" ];then lunar_month=$(($lunar_month - 1)) echo $lunar_year-*$lunar_month-$lunar_day else lunar_month=$(($lunar_month - 1)) echo $lunar_year-$lunar_month-$lunar_day fi else echo $lunar_year-$lunar_month-$lunar_day fi fi sed -n $(($(($lunar_year - 4598 + 2)) % 12))p shengxiao lunar.sh

 

数据文件

databases文件中存储了日期的元数据。为了更容易查表,此文件对元数据进行了处理。

4598 1901 04AE53 4599 1902 0A5748 4600 1903 5526BD 4601 1904 0D2650 4602 1905 0D9544 4603 1906 46AAB9 4604 1907 056A4D 4605 1908 09AD42 4606 1909 24AEB6 4607 1910 04AE4A 4608 1911 6A4DBE 4609 1912 0A4D52 4610 1913 0D2546 4611 1914 5D52BA 4612 1915 0B544E 4613 1916 0D6A43 4614 1917 296D37 4615 1918 095B4B 4616 1919 749BC1 4617 1920 049754 4618 1921 0A4B48 4619 1922 5B25BC 4620 1923 06A550 4621 1924 06D445 4622 1925 4ADAB8 4623 1926 02B64D 4624 1927 095742 4625 1928 2497B7 4626 1929 04974A 4627 1930 664B3E 4628 1931 0D4A51 4629 1932 0EA546 4630 1933 56D4BA 4631 1934 05AD4E 4632 1935 02B644 4633 1936 393738 4634 1937 092E4B 4635 1938 7C96BF 4636 1939 0C9553 4637 1940 0D4A48 4638 1941 6DA53B 4639 1942 0B554F 4640 1943 056A45 4641 1944 4AADB9 4642 1945 025D4D 4643 1946 092D42 4644 1947 2C95B6 4645 1948 0A954A 4646 1949 7B4ABD 4647 1950 06CA51 4648 1951 0B5546 4649 1952 555ABB 4650 1953 04DA4E 4651 1954 0A5B43 4652 1955 352BB8 4653 1956 052B4C 4654 1957 8A953F 4655 1958 0E9552 4656 1959 06AA48 4657 1960 7AD53C 4658 1961 0AB54F 4659 1962 04B645 4660 1963 4A5739 4661 1964 0A574D 4662 1965 052642 4663 1966 3E9335 4664 1967 0D9549 4665 1968 75AABE 4666 1969 056A51 4667 1970 096D46 4668 1971 54AEBB 4669 1972 04AD4F 4670 1973 0A4D43 4671 1974 4D26B7 4672 1975 0D254B 4673 1976 8D52BF 4674 1977 0B5452 4675 1978 0B6A47 4676 1979 696D3C 4677 1980 095B50 4678 1981 049B45 4679 1982 4A4BB9 4680 1983 0A4B4D 4681 1984 AB25C2 4682 1985 06A554 4683 1986 06D449 4684 1987 6ADA3D 4685 1988 0AB651 4686 1989 093746 4687 1990 5497BB 4688 1991 04974F 4689 1992 064B44 4690 1993 36A537 4691 1994 0EA54A 4692 1995 86B2BF 4693 1996 05AC53 4694 1997 0AB647 4695 1998 5936BC 4696 1999 092E50 4697 2000 0C9645 4698 2001 4D4AB8 4699 2002 0D4A4C 4700 2003 0DA541 4701 2004 25AAB6 4702 2005 056A49 4703 2006 7AADBD 4704 2007 025D52 4705 2008 092D47 4706 2009 5C95BA 4707 2010 0A954E 4708 2011 0B4A43 4709 2012 4B5537 4710 2013 0AD54A 4711 2014 955ABF 4712 2015 04BA53 4713 2016 0A5B48 4714 2017 652BBC 4715 2018 052B50 4716 2019 0A9345 4717 2020 474AB9 4718 2021 06AA4C 4719 2022 0AD541 4720 2023 24DAB6 4721 2024 04B64A 4722 2025 69573D 4723 2026 0A4E51 4724 2027 0D2646 4725 2028 5E933A 4726 2029 0D534D 4727 2030 05AA43 4728 2031 36B537 4729 2032 096D4B 4730 2033 B4AEBF 4731 2034 04AD53 4732 2035 0A4D48 4733 2036 6D25BC 4734 2037 0D254F 4735 2038 0D5244 4736 2039 5DAA38 4737 2040 0B5A4C 4738 2041 056D41 4739 2042 24ADB6 4740 2043 049B4A 4741 2044 7A4BBE 4742 2045 0A4B51 4743 2046 0AA546 4744 2047 5B52BA 4745 2048 06D24E 4746 2049 0ADA42 4747 2050 355B37 4748 2051 09374B 4749 2052 8497C1 4750 2053 049753 4751 2054 064B48 4752 2055 66A53C 4753 2056 0EA54F 4754 2057 06B244 4755 2058 4AB638 4756 2059 0AAE4C 4757 2060 092E42 4758 2061 3C9735 4759 2062 0C9649 4760 2063 7D4ABD 4761 2064 0D4A51 4762 2065 0DA545 4763 2066 55AABA 4764 2067 056A4E 4765 2068 0A6D43 4766 2069 452EB7 4767 2070 052D4B 4768 2071 8A95BF 4769 2072 0A9553 4770 2073 0B4A47 4771 2074 6B553B 4772 2075 0AD54F 4773 2076 055A45 4774 2077 4A5D38 4775 2078 0A5B4C 4776 2079 052B42 4777 2080 3A93B6 4778 2081 069349 4779 2082 7729BD 4780 2083 06AA51 4781 2084 0AD546 4782 2085 54DABA 4783 2086 04B64E 4784 2087 0A5743 4785 2088 452738 4786 2089 0D264A 4787 2090 8E933E 4788 2091 0D5252 4789 2092 0DAA47 4790 2093 66B53B 4791 2094 056D4F 4792 2095 04AE45 4793 2096 4A4EB9 4794 2097 0A4D4C 4795 2098 0D1541 4796 2099 2D92B5 databases

 

生肖数据

shengxiao文件中是生肖:

鼠 牛 虎 兔 龙 蛇 马 羊 猴 鸡 狗 猪 shengxiao

 

下个功能点

下个功能点是阳历日期的实现,因为系统中date程序支持的时间范围是1901-12-15到2038-01-19,显然有时此时间段并不能满足一些人的要求。所以,下一步需要单独实现如date程序功能的代码,以支持更大的时间段。

但你可知道1752年的9月是有问题的,具体缘由你可以google(话说这些天已经不好使了,我只能说,是不是被玩坏了!)或者baidu。

下个shell程序

Ubuntu 14.04 LTS出来的时候,我就迫不及待的将12.04 LTS升级到14.04 LTS,但是除了问题TMD还是问题:

*开机情况下合起笔记本盖子再打开时X僵死了,好烦躁!
*蓝牙适配器不能用了!
*每次打开电脑都会有系统错误提示,还不止一个!

所以,前天晚上将必要的数据备份后还是装回12.04,感觉整个人都舒服了。

之前UbuntuKylin出来的时候就下了天气插件使用,但不管12.04还是14.04上都会莫名其妙的死掉,所以就萌生了用shell实现一个天气察看程序,但不知道天气元数据怎么获取(中国气象局的数据),之前看了UbuntuKylin的天气插件源码,但没找到。

最后

农历是通过观测及推算而得出的历法,一直没有找到元数据的出处,这个应该天文台有,但网上找不到。

还有天气数据是从那里获取?

还望知道的大哥大姐小弟小妹告诉我。不胜感激!

相关内容