zoukankan      html  css  js  c++  java
  • 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

    主要代码

    因为时常更新,所以打包文件不再提供,只提供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: 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)
    "
          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
    " && Usage
                [ "$1" -ge "19000000" ] && [ "$1" -lt "19010101" ] || [ "$1" -gt "20991231" ] && [ "$1" -le "20999999" ] 
                && echo -e "Invalid parameter: $1
    " && Usage
                Date_data
             ;;
             *)
                echo -e "Invalid parameter: $1
    "
                Usage
             ;;
          esac
       ;;
       *)
          echo -e "The number of parameter greater than one !
    "
          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的天气插件源码,但没找到。

    最后

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

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

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

    更新

    2014-08-01

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

  • 相关阅读:
    选择
    git使用笔记
    “子查询返回的值不止一个。当子查询跟随在 =、!=、<、<=、>、>= 之后,或子查询用作表达式时,这种情况是不允许的。”SQL查询错误解析
    sql学习笔记
    python爬虫
    线性代数知识点
    centos7 安装git
    感知机——学习笔记
    Logistic Regression学习笔记
    朴素贝叶斯分类--笔记
  • 原文地址:https://www.cnblogs.com/snowsolf/p/lunar_next.html
Copyright © 2011-2022 走看看