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

时间:2023-03-09 09:12:41
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

主要代码

因为时常更新,所以打包文件不再提供,只提供github。

github地址:

https://github.com/snowsolf/lunar

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

  • 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: ~
But 'date' program support: ~ -h, --help display this help and exit
-V, --version output version information and exit Usage: $ [-h|--help|-V|--version] | [date(yyyymmdd)] Examples:
Usage input time: $
Usage system time: $ Editor: $Editor
E-mail: $Email
EOF
exit
} #################################################################
#get year,month,day and day of year
#system 'date' program support: ~
#################################################################
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
)
echo "No parameters!"
echo -e "Usage system time: $(date +%Y-%m-%d)\n"
DATE=$(date +%Y%m%d)
Date_data
;;
)
date -d $DATE +%j > /dev/null || ((Usage && exit ))
case "$1" in
-h|--help)
Usage
;;
-V|--version)
echo "$0: Version $Version"
echo "Editor: $Editor"
echo "E-mail: $Email"
exit
;;
[][][-][-][-][-][-][-]|[][][-][-][-][-][-][-])
[ "$1" -ge "" ] && [ "$1" -lt "" ] || [ "$1" -gt "" ] && [ "$1" -le "" ] \
&& echo -e "'date' program no support: $1\n" && Usage
[ "$1" -ge "" ] && [ "$1" -lt "" ] || [ "$1" -gt "" ] && [ "$1" -le "" ] \
&& 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 + )
# flag
befor_or_after= if [ "$lunar_days" -le "" ]; then
befor_or_after=
date_year=$(($date_year - )) 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" = "" ] && 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" = "" ];then
lunar_month=
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 + ))
done
else
lunar_month=
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 - ))
else
lunar_day=$(($i - $lunar_day))
break
fi
done
fi # output
if [ "$lunar_leap_month" = "" ]; 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" = "" ]; then
if [ "$(($lunar_leap_month + 1))" = "$lunar_month" ];then
lunar_month=$(($lunar_month - ))
echo $lunar_year-*$lunar_month-$lunar_day
else
lunar_month=$(($lunar_month - ))
echo $lunar_year-$lunar_month-$lunar_day
fi
else
echo $lunar_year-$lunar_month-$lunar_day
fi
fi sed -n $(($(($lunar_year - + )) % ))p shengxiao

lunar.sh

数据文件

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

  04AE53
0A5748
5526BD
0D2650
0D9544
46AAB9
056A4D
09AD42
24AEB6
04AE4A
6A4DBE
0A4D52
0D2546
5D52BA
0B544E
0D6A43
296D37
095B4B
749BC1 0A4B48
5B25BC
06A550
06D445
4ADAB8
02B64D 2497B7
04974A
664B3E
0D4A51
0EA546
56D4BA
05AD4E
02B644 092E4B
7C96BF
0C9553
0D4A48
6DA53B
0B554F
056A45
4AADB9
025D4D
092D42
2C95B6
0A954A
7B4ABD
06CA51
0B5546
555ABB
04DA4E
0A5B43
352BB8
052B4C
8A953F
0E9552
06AA48
7AD53C
0AB54F
04B645
4A5739
0A574D 3E9335
0D9549
75AABE
056A51
096D46
54AEBB
04AD4F
0A4D43
4D26B7
0D254B
8D52BF
0B5452
0B6A47
696D3C
095B50
049B45
4A4BB9
0A4B4D
AB25C2
06A554
06D449
6ADA3D
0AB651 5497BB
04974F
064B44
36A537
0EA54A
86B2BF
05AC53
0AB647
5936BC
092E50
0C9645
4D4AB8
0D4A4C
0DA541
25AAB6
056A49
7AADBD
025D52
092D47
5C95BA
0A954E
0B4A43
4B5537
0AD54A
955ABF
04BA53
0A5B48
652BBC
052B50
0A9345
474AB9
06AA4C
0AD541
24DAB6
04B64A
69573D
0A4E51
0D2646
5E933A
0D534D
05AA43
36B537
096D4B
B4AEBF
04AD53
0A4D48
6D25BC
0D254F
0D5244
5DAA38
0B5A4C
056D41
24ADB6
049B4A
7A4BBE
0A4B51
0AA546
5B52BA
06D24E
0ADA42
355B37
09374B
8497C1 064B48
66A53C
0EA54F
06B244
4AB638
0AAE4C
092E42
3C9735
0C9649
7D4ABD
0D4A51
0DA545
55AABA
056A4E
0A6D43
452EB7
052D4B
8A95BF
0A9553
0B4A47
6B553B
0AD54F
055A45
4A5D38
0A5B4C
052B42
3A93B6 7729BD
06AA51
0AD546
54DABA
04B64E
0A5743 0D264A
8E933E
0D5252
0DAA47
66B53B
056D4F
04AE45
4A4EB9
0A4D4C
0D1541
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的天气插件源码,但没找到。

最后

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

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

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

更新

2014-08-01

将此脚本托管到github上,并将readme内容更新到README.md文件中。