PB中关于公历转农历的算法,网上有很多,思路也大致一样,在这里我就不再进行说明了。
本文主要是想跟所有PB爱好者,分享农历转公历的方法。
转换思路为:根据传入的农历日期,找到第一个小于传入日期的基准日期(基准日期梅10年1天,基准日期对应的农历日期为正月初一),通过计算,得出传入日期与基准日期之间相差的天数ll_sumday,然后通过RelativeDate ( 基准日期, ll_sumday ),即可求出传入的农历日期对应的公历日期。
本文将给出一个用户自定义对象,该用户对象中包含两个public函数,第一个是:
********************************************************************
函数名称: of_day_to_lunar
传递参数: date ad_date; - 传入公历日期
string as_lunar - 返回农历日期
integer ai_type - 类型,1取完整信息, 2取日信息, 3取农历写法的月日
返回参数: STRING
********************************************************************
功能描述: 计算农历:传入公历计算农历
********************************************************************
第二个是:
********************************************************************
函数名称: of_lunar_to_day
传递参数: string as_lunar - 传入农历日期,
写法为: 20110101 表示2011年正月初一
20090506R 表示2009年闰五月初六
返回参数: date - 返回公历日期
********************************************************************
功能描述: 计算公历:传入农历计算公历
********************************************************************
需要说明的是第二个函数“农历转公历”,由于农历日期的写法不是一种规范化的写法,因此我将该函数设定为只能接受格式化了的参数。如果你想要传入形如:五月廿八 这样的农历写法的日期,那么,请自己写一个将农历写法的日期格式化为of_lunar_to_day函数所能接受的参数的函数吧。
nvo_lunar代码明细:(自定义一个不可见的对象nvo_lunar,然后对该对象进行Edit Source,将以下内容复制替换即可)
forward
global type nvo_lunar from nonvisualobject
end type
end forward
global type nvo_lunar from nonvisualobject autoinstantiate
end type
type variables
//农历年1900 到 2051年 每年的信息数据:
string is_lunarinfostr[152] = { &
'04bd8','04ae0','0a570','054d5','0d260','0d950','16554','056a0','09ad0','055d2', &
'04ae0','0a5b6','0a4d0','0d250','1d255','0b540','0d6a0','0ada2','095b0','14977', &
'04970','0a4b0','0b4b5','06a50','06d40','1ab54','02b60','09570','052f2','04970', &
'06566','0d4a0','0ea50','06e95','05ad0','02b60','186e3','092e0','1c8d7','0c950', &
'0d4a0','1d8a6','0b550','056a0','1a5b4','025d0','092d0','0d2b2','0a950','0b557', &
'06ca0','0b550','15355','04da0','0a5d0','14573','052d0','0a9a8','0e950','06aa0', &
'0aea6','0ab50','04b60','0aae4','0a570','05260','0f263','0d950','05b57','056a0', &
'096d0','04dd5','04ad0','0a4d0','0d4d4','0d250','0d558','0b540','0b5a0','195a6', &
'095b0','049b0','0a974','0a4b0','0b27a','06a50','06d40','0af46','0ab60','09570', &
'04af5','04970','064b0','074a3','0ea50','06b58','055c0','0ab60','096d5','092e0', &
'0c960','0d954','0d4a0','0da50','07552','056a0','0abb7','025d0','092d0','0cab5', &
'0a950','0b4a0','0baa4','0ad50','055d9','04ba0','0a5b0','15176','052b0','0a930', &
'07954','06aa0','0ad50','05b52','04b60','0a6e6','0a4e0','0d260','0ea65','0d530', &
'05aa0','076a3','096d0','04bd7','04ad0','0a4d0','1d0b6','0d250','0d520','0dd45', &
'0b5a0','056d0','055b2','049b0','0a577','0a4b0','0aa50','1b255','06d20','0ada0', &
'14b63','09370'}
/*
如 2001 0x0d954 01101100101010100 1、2、4、5、8、10、12月大3、6、7、9、11月小,4月为闰月,闰月小
*/
end variables
forward prototypes
public function string of_day_to_lunar (date ad_date, ref string as_lunar, integer ai_type)
public function date of_lunar_to_day (ref string as_lunar)
private function string of_get_jq (date ad_date)
private function integer of_get_monthdays_byyear (string as_str, ref long al_mdays[14], string as_end_lunar)
end prototypes
public function string of_day_to_lunar (date ad_date, ref string as_lunar, integer ai_type);/*
********************************************************************
函数名称: of_day_to_lunar
传递参数: date ad_date;
- 传入公历日期
string as_lunar
- 返回农历日期
integer ai_type
- 类型,1取完整信息, 2取日信息, 3取农历写法的月日
返回参数: STRING
********************************************************************
功能描述: 计算农历:传入公历计算农历
********************************************************************
备注:
********************************************************************
*/
if ad_date <= date('1900-1-1') or ad_date > date('2051-12-31') or isnull(ad_date) then
as_lunar = '19000101'
return ''
end if
string ls_ret, ls_lunar_mmdd
//干
string Gan[10] = {"甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"}
//支
string Zhi[12] = {"子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"}
//生肖
string Animals[12] = {"鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"}
string nStr1[11] = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十"}
string nStr2[5] = {"初", "十", "廿", "卅", " "}
string ls_daysto[17] = {'1900-01-30', '1910-02-09','1920-02-19','1930-01-29','1940-02-07','1950-02-16','1960-01-27','1970-02-05','1980-02-15','1990-01-26','2000-02-04','2010-02-13','2020-01-24','2030-02-02','2040-02-11', '2050-01-22', '2101-02-05'}
long ll_now, ll_nowstart
long ll_year,ll_mon,ll_day
long ll_lyear,ll_lmon,ll_lday
long ll_lunarmd[14]
//年,月,日
ll_year = year(ad_date)
ll_mon = month(ad_date)
ll_day = day(ad_date)
double ld_num
long ll_leap,ll_sumday,ll_leapdays
long ll_info,k,i
long ll_temp,ll_offset
string ls_bin,ls_code
long ll_month_prior //当月的前一个月
ll_leap = 0
ll_temp = 0
//取得最近的一个基准点,10年一个,可提高效率
k = int((ll_year - 1900) / 10) + 1
if ll_mon > 2 then k ++
for i = k to 17
if ad_date <= date(ls_daysto[i]) then
ll_now = i -1
ll_nowstart = long(left(ls_daysto[i - 1],4))
ll_offset = daysafter(date(ls_daysto[i - 1]), ad_date)
exit
end if
next
//计算农历
for i = ll_nowstart to 2051
//求当年农历年的天数
//农历全年天数
//ll_lunarmd[14]说明,1-12为农历年每个月的天数,13为闰月的天数(无闰月则为0),14为闰月月份(无闰月则为0)
ll_sumday = of_get_monthdays_byyear( is_lunarinfostr[i - 1900 + 1], ll_lunarmd, '')
ll_offset = ll_offset - ll_sumday
if ll_offset < 1 then exit
next
ll_offset = ll_offset + ll_sumday
//农历年
ll_lyear = i
long ll_leapmonth
long ll_pritmp,ll_thistmp
//取得闰月月份
ll_leapmonth = ll_lunarmd[14]
for i = 1 to 13
if ll_leapmonth = 0 or ll_leapmonth = 12 then //没有闰月或闰12月,按顺序增加
ll_temp = ll_lunarmd[i]
ll_pritmp = i
else//否则,加到有闰月的月份后,需要把闰月天数加进来
if i <= ll_leapmonth then
ll_temp = ll_lunarmd[i]
ll_pritmp = i
elseif i = ll_leapmonth + 1 then
ll_temp = ll_lunarmd[13]
ll_pritmp = ll_leapmonth
else
ll_temp = ll_lunarmd[i - 1]
ll_pritmp = i - 1
end if
end if
ll_offset = ll_offset - ll_temp
ll_lmon = ll_pritmp
if ll_offset <= 0 then exit
ll_month_prior = ll_pritmp
next
ll_offset = ll_offset + ll_temp
//农历月
//农历日
ll_lday = ll_offset
long ll_days
as_lunar = fill('0', 2 - len(string(ll_lyear))) + string(ll_lyear) + fill('0', 2 - len(string(ll_lmon))) + string(ll_lmon) + fill('0', 2 - len(string(ll_lday))) + string(ll_lday)
//转换成农历的写法:
string ls_str,ls_mstr,ls_ystr,ls_astr
if ai_type = 1 then
//农历年的干、支
ls_ystr = gan[mod(mod(ll_lyear - 1864, 60), 10) + 1] + zhi[mod(mod(ll_lyear - 1864, 60), 12) + 1]
//生肖
ls_astr = Animals[mod(ll_lyear - 1900, 12) + 1]
end if
long ll_mtmp
//日期
choose case ll_lday
case 10
ls_str = '初十'
case 20
ls_str = '二十'
case 30
ls_str = '三十'
case else
if int(ll_lday / 10) + 1>=1 and int(ll_lday / 10) + 1<=5 then
ls_str = nstr2[int(ll_lday / 10) + 1]
end if
if mod(ll_lday,10)>11 or mod(ll_lday,10)<=0 then
else
ls_str += nstr1[mod(ll_lday,10)]
end if
end choose
//月份
choose case ll_lmon
case 1
ls_mstr = '正'
case 11
ls_mstr = '十一'
case 12
ls_mstr = '十二'
case else
if ll_lmon>11 or ll_lmon<=0 then
else
ls_mstr = nstr1[ll_lmon]
end if
end choose
if ll_leapmonth = ll_lmon and ll_lmon = ll_month_prior then ls_mstr = '闰' + ls_mstr
if ai_type = 1 then
ls_ret = ls_ystr + "年(" + ls_astr + ") " + ls_mstr + "月" + ls_str
//计算农历上的节气,从1900.1.6 02:05:00 (小寒)开始
string ls_jq
ls_jq = of_get_jq(ad_date)
if ls_jq <> '' then ls_ret = ls_ret + " 节气:" + ls_jq
elseif ai_type = 3 then //返回月日
return ls_mstr + "月" + ls_str
else
if ls_str = '初一' then
ls_ret = ls_mstr + "月"
else
ls_ret = ls_str
end if
end if
return ls_ret
end function
public function date of_lunar_to_day (ref string as_lunar);/*
********************************************************************
函数名称: of_lunar_to_day
传递参数: string as_lunar
- 传入农历日期,写法为: 20110101表示2011年正月初一
20090506R表示2009年闰五月初六
返回参数: date - 返回公历日期
********************************************************************
功能描述: 计算公历:传入农历计算公历
********************************************************************
备注:
********************************************************************
*/
if len(as_lunar) <> 8 and len(as_lunar) <> 9 then
return 1900-01-01
end if
long ll_year,ll_mon,ll_day
boolean lb_leapmonth
long i, k, ll_sumday
ll_year = integer(left(as_lunar, 4))
ll_mon = integer(mid(as_lunar, 5,2))
ll_day = integer(mid(as_lunar, 7, 2))
lb_leapmonth = upper(mid(as_lunar, 9, 1)) = 'R' //是否是闰月
if ll_year < 1900 or ll_year > 2050 then return 1900-01-01
if ll_mon < 1 or ll_mon > 12 then return 1900-01-01
if ll_day < 1 or ll_day > 31 then return 1900-01-01
string ls_daysto[17] = {'1900-01-30', '1910-02-09','1920-02-19','1930-01-29','1940-02-07','1950-02-16','1960-01-27','1970-02-05','1980-02-15','1990-01-26','2000-02-04','2010-02-13','2020-01-24','2030-02-02','2040-02-11', '2050-01-22', '2101-02-05'}
long ll_lunarmd[14]
//取得最近的一个基准点,10年一个,可提高效率
k = int((ll_year - 1900) / 10) + 1
//计算农历天数
for i = integer(left(ls_daysto[k],4)) to ll_year - 1
//求当年农历年的天数
//农历全年天数
//ll_lunarmd[14]说明,1-12为农历年每个月的天数,13为闰月的天数(无闰月则为0),14为闰月月份(无闰月则为0)
ll_sumday += of_get_monthdays_byyear( is_lunarinfostr[i - 1900 + 1], ll_lunarmd, '')
next
i = of_get_monthdays_byyear( is_lunarinfostr[ll_year - 1900 + 1], ll_lunarmd, as_lunar)
if i = 0 then return 1900-01-01
ll_sumday += i
return RelativeDate ( date(ls_daysto[k]), ll_sumday )
end function
private function string of_get_jq (date ad_date);//24节气
string SolarTerm[24] = {"小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", &
"小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至"}
//*************************************************************************
// 该函数已经加密
//**************************************************************************
return ''
end function
private function integer of_get_monthdays_byyear (string as_str, ref long al_mdays[14], string as_end_lunar);//取得指定农历年的天数
string ls_hextobin[15] = {'0001','0010','0011','0100','0101','0110','0111','1000','1001','1010','1011','1100','1101','1110','1111'}
string ls_bin
long ll_leapdays
long ll_leapm
long ll_yeardays
long ll_mon, ll_day
//结束日期信息
if as_end_lunar <> '' then
ll_mon = integer(mid(as_end_lunar, 5, 2))
ll_day = integer(mid(as_end_lunar, 7, 2))
else
ll_mon = 12
end if
as_str = upper(as_str)
if left(as_str,1) = '1' then
ll_leapdays = 30
else
ll_leapdays = 29
end if
choose case right(as_Str,1)
case '1','2','3','4','5','6','7','8','9'
ll_leapm = integer(right(as_Str,1))
case 'A'
ll_leapm = 10
case 'B'
ll_leapm = 11
case 'C'
ll_leapm = 12
case else
ll_leapm = 0
end choose
if upper(mid(as_end_lunar, 9, 1)) = 'R' then
//传入的日期是闰月的,但实际上该日期不是闰月,则表示日期有问题,返回0
if ll_mon <> ll_leapm then return 0
if ll_day > ll_leapdays then return 0 //日期有误
ll_mon ++
end if
if ll_leapm = 0 then ll_leapdays = 0
long i
for i = 2 to 4
choose case mid(as_str,i,1)
case '1','2','3','4','5','6','7','8','9'
ls_bin += ls_hextobin[integer(mid(as_str,i,1))]
case 'A'
ls_bin += ls_hextobin[10]
case 'B'
ls_bin += ls_hextobin[11]
case 'C'
ls_bin += ls_hextobin[12]
case 'D'
ls_bin += ls_hextobin[13]
case 'E'
ls_bin += ls_hextobin[14]
case 'F'
ls_bin += ls_hextobin[15]
case else
ls_bin += '0000'
end choose
next
for i = 1 to ll_mon
choose case mid(ls_bin,i,1)
case '1'
al_mdays[i] = 30
case '0'
al_mdays[i] = 29
case else
end choose
if as_end_lunar <> '' and ll_mon = i then exit
ll_yeardays += al_mdays[i]
next
al_mdays[13] = ll_leapdays
al_mdays[14] = ll_leapm
if ll_day > 0 then //有截止日期的
if ll_day > al_mdays[integer(mid(as_end_lunar, 5, 2))] then return 0 //日期有误
if integer(mid(as_end_lunar, 5, 2)) > ll_leapm then ll_yeardays += ll_leapdays
ll_yeardays += ll_day
else
ll_yeardays += ll_leapdays
end if
return ll_yeardays
end function
on nvo_lunar.create
call super::create
TriggerEvent( this, "constructor" )
end on
on nvo_lunar.destroy
TriggerEvent( this, "destructor" )
call super::destroy
end on
根据
- gui41021
- (鬼鬼)
的思路,可以使用另外一种方法来实现农历转阳历,该方法需要使用到网上随处可见的一个动态链接库:calendar.dll 。需要其中的一个函数:
function long MyGetLunarDate(long iYear, long iMonth, long iDay, ref long iLunarYear, ref long iLunarMonth, ref long iLunarDay) library "calendar.dll"
该方法的思路是:首先记录传入的农历日期的年份、月份和天数,然后取到第一个可以用date类型表示并且小于传入的农历日期的日期new_date,然后通过函数MyGetLunarDate来获取new_date对应的农历日期(很显然的,此时获取的农历日期一定小于传入的农历日期),接着使用RelativeDate(new_date,n),将new_date往后增加,直到new_date对应的农历日期为传入的日期。
具体的代码如下:
/*
********************************************************************
函数名称: of_lunar_to_day
传递参数: string as_lunar
- 传入农历日期,写法为: 2011-01-01表示2011年正月初一
2009-05-06R表示2009年闰五月初六
返回参数: date - 返回公历日期
********************************************************************
功能描述: 计算公历:传入农历计算公历
********************************************************************
备注:
********************************************************************
*/
long ll_oldyear,ll_oldmonth,ll_oldday
long ll_newyear,ll_newmonth,ll_newday
date new_date, first_date //第一次吻合的日期
boolean i =false
//ls_lunar = '2009-05-06'// 求这天的阳历
boolean lb_leap = false //是否是闰月
int li
lb_leap = upper(mid(as_lunar, 11, 1)) = 'R'
as_lunar = left(as_lunar, 10)
ll_oldyear = integer(left(as_lunar, 4))
ll_oldmonth = integer(mid(as_lunar, 6, 2))
ll_oldday = integer(mid(as_lunar, 9, 2))
if ll_oldyear < 1900 or ll_oldyear > 2050 then return 1900-01-01
if ll_oldmonth < 1 or ll_oldmonth > 12 then return 1900-01-01
if ll_oldday < 1 or ll_oldday > 31 then return 1900-01-01
if not isdate(as_lunar) then //如果农历无法用公历表示,则往前走,找到第一个可以用农历表示的日期
li = ll_oldday
do while true
li --
if li = 0 then return 1900-01-01
if isdate(left(as_lunar, 8) + string(li)) then
new_date = date(left(as_lunar, 8) + string(li))
exit
end if
loop
else
new_date = date(as_lunar)
end if
do while i = false //一般是2-4次左右,最长可能循环6次
mygetlunardate(year(new_date),month(new_date),day(new_date),ll_newyear,ll_newmonth,ll_newday) // calendar.dll中阳历转阴历的函数
if ll_newyear = ll_oldyear and ll_newmonth = ll_oldmonth and ll_newday = ll_oldday then
if lb_leap then
lb_leap = false
first_date = new_date
new_date = RelativeDate(new_date,29) //下个月,起码是29天后了
else
i = true
end if
else
//取得的农历日期大于传入的日期,说明传入的日期有误,直接返回
if string(ll_newyear) + string(ll_newmonth, '00') > string(ll_oldyear) + string(ll_oldmonth, '00') then return 1900-01-01//没有发现
if ll_newmonth <> ll_oldmonth then
if ll_newday < 29 then
new_date = RelativeDate(new_date,29 - ll_newday) //取得的农历日期还是上一个月的29号前的,直接跳到29号
else
new_date = RelativeDate(new_date,1) //取得的农历日期是上一个月的29号后的,跳到下一天(因为下一天可能是30号也可能是1号)
end if
else //取得的农历日期已经是这个月的,直接跳到传入的农历日期那一天
new_date = RelativeDate(new_date,ll_oldday - ll_newday)
end if
end if
loop
return new_date