SoFunction
Updated on 2025-03-10

Sharing of the Gregorian to Lunar New Year code implemented by Shell script

I had nothing to do and wanted to write a script to convert the Gregorian to the Lunar New Year in Linux using shell. It was finally done intermittently for about a week. Now let’s take it out and share it with you.

1. Reason

The implementation principle of this script is table lookup method (because there is an error in the formula); based on the Lunar New Year, two different Lunar New Year are calculated.

Before writing this script, I wanted to add the Gregorian calendar and lunar calendar dates to the Linux terminal command prompt. There is Lunar software in Ubuntu to obtain lunar dates, but there is no similar software in Fedora or CentOS, so I want to implement one by myself, but I have written a lot in other languages ​​online, so it is not necessary to write it again. So I want to write one in a shell.

2. Functions and usage

Function: Convert specific solar calendar dates to lunar dates.

Time range: 1901~2099, corresponding to the lunar year period is 4598~4796

Parameter format (no parameters default to the current system date): yyyymmdd

As January 1, 2013:

Copy the codeThe code is as follows:

$./ 20130101
4709-11-20

3. Complete data

Complete data download link:
http://xiazai./201408/tools/lunar-20131202.7z

Files in the package:

Main script, specific implementation

datebases Lunar metadata

Change log

readme script instructions and precautions

The main script code is as follows:

#!/bin/sh

DATE=$@
[ "$DATE" = "" ] && DATE=$(date +%Y%m%d)

databases_path=databases

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)

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')

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)

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" -gt "$i" ] && lunar_day=$(($lunar_day - $i)) && lunar_month=$(($lunar_month + 1))
   [ "$lunar_day" = "$i" ] && break
  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
   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

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

4 Modify history

2013-12-02

Bug found: If the last month of the lunar calendar is the big month and this month is the small month, then the output of the thirty of the previous month is the first day of the month. The reason is that there are 30 days left last month, which is exactly the thirty of the previous month, and this month is 29 days, 29<30. The next cycle is the number of days of the month, making the thirty of the previous month the first day of the month.

Bug modification: Add judgment statement, if the remaining days in the lunar calendar are equal to the number of days in the current month, no longer loops