有没有研究过农历的
tyc611
有没有研究过农历的
在网上找了下,发现农历没有固定的算法,是日月星辰观测结果,可以小范围推算
有没有人做过这方面的,想找点资料
tyc611
找了些资料,先搁这儿,慢慢研究
[url]http://www.nongli.com/Doc/0503/0316574.htm[/url]
[url]http://www.nongli.com/Doc/0409/19162522.htm[/url]
[url]http://www.chinesefortunecalendar.com/clc/clcBig5.htm[/url]
[url]http://www.chinesefortunecalendar.com/clc/Default.htm[/url]
[url]http://z.baidu.com/question/42505559.html?fr=qrl[/url]
[url]http://zhidao.baidu.com/question/1919817.html?md=3[/url]
tyc611
本来想写个日历算法,主要实现公历/农历转换,查找资料发现网上实现的转换都是使用查表方法
据说网上的数据表有很多是不准确的,真假难辨,暂时就不实现转换功能了,多研究下
这里提供一个半成品吧,主要实现了公历中的算法,如计算时期对应的星期、某日期在全年中的天数(及相反计算)、计算日期的日期差(及相反计算)
使用示例:
[code]
/**
* DateCN_example.cpp
* @Author Tu Yongce <yongce (at) 126 (dot) com>
* @Created 2008-6-28
* @Modified 2008-6-28
* @Version 0.1
*/
#include "DateCN.h"
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
int main()
{
const string weeks[7] = {"日", "一", "二", "三", "四", "五", "六"};
time_t nowTime = time(0);
struct tm *nowDate = localtime(&nowTime);
DateCN today(nowDate->tm_year + 1900, nowDate->tm_mon + 1, nowDate->tm_mday);
cout << "今天是:/n" << today;
cout << ", 星期" << weeks[today.ToWeek()];
cout << ", 今年的第" << today.CalcYearDay() << "天/n/n";
DateCN firstday(1, 1 ,1);
int days = firstday.Diff(today);
cout << firstday << "距今已有" << days << "天/n";
cout << firstday << "之后的第" << days << "天是";
cout << firstday.AdjustDays(days) << "/n";
cout << firstday << "之前的第" << days << "天是";
cout << firstday.AdjustDays(-days) << "/n/n";
days = 1000*1000*100; // 1亿
cout << today << "之后的第" << days << "天是";
DateCN oneday(today);
cout << oneday.AdjustDays(days) << "/n";
cout << oneday << "是距今" << oneday.Diff(today) << "天/n";
cout << oneday << "之前的第" << days << "天是";
cout << oneday.AdjustDays(-days) << "/n/n";
DateCN birthday(1949, 10, 1);
days = today.Diff(birthday);
cout << "中华人民共和国:/n" << birthday;
cout << ", 已经成立" << days << "天/n";
cout << birthday << "之后的第" << days << "天是";
cout << birthday.AdjustDays(days) << "/n";
cout << birthday << "之前的第" << days << "天是";
cout << birthday.AdjustDays(-days) << "/n/n";
birthday = DateCN(2006, 2, 14);
days = today.Diff(birthday);
cout << "tyc611的生日:/n" << birthday;
cout << ", 已经注册" << days << "天/n";
cout << birthday << "之后的第" << days << "天是";
cout << birthday.AdjustDays(days) << "/n";
cout << birthday << "之前的第" << days << "天是";
cout << birthday.AdjustDays(-days) << "/n/n";
return 0;
}
[/code]
运行结果:
[quote]
今天是:
2008年6月29日, 星期日, 今年的第181天
1年1月1日距今已有733221天
1年1月1日之后的第733221天是2008年6月29日
2008年6月29日之前的第733221天是1年1月1日
2008年6月29日之后的第100000000天是275799年3月12日
275799年3月12日是距今100000000天
275799年3月12日之前的第100000000天是2008年6月29日
中华人民共和国:
1949年10月1日, 已经成立21456天
1949年10月1日之后的第21456天是2008年6月29日
2008年6月29日之前的第21456天是1949年10月1日
tyc611的生日:
2006年2月14日, 已经注册866天
2006年2月14日之后的第866天是2008年6月29日
2008年6月29日之前的第866天是2006年2月14日
[/quote]
tyc611
类的头文件:
[code]
/**
* DateCN.h
* @Author Tu Yongce <yongce (at) 126 (dot) com>
* @Created 2008-6-28
* @Modified 2008-6-28
* @Version 0.1
*/
#ifndef DATE_CN_H_INCLUDED
#define DATE_CN_H_INCLUDED
#include <cassert>
#include <ostream>
class DateCN
{
size_t year_; // 1~?
size_t month_; // 1~12
size_t day_; // 1~[28|29|30|31]
// 平年中每一个月的天数
static const size_t monthDays_[12];
// 平年中每一个月之前所有月的天数
static const size_t yearDays_[12];
public:
DateCN(size_t year = 1, size_t month = 1, size_t day = 1);
size_t GetYear() const;
size_t GetMonth() const;
size_t GetDay() const;
// 判断是否为闰年(阳历)
bool IsLeapYear() const;
// 判断是否为有效日期(阳历)
bool IsValidDate() const;
// 判断是否在指定日期之前
bool IsPrior(const DateCN &ref) const;
// 计算某年的全年天数(阳历)
size_t CountYearDays() const;
// 计算是该年中的第几天(阳历)
size_t CalcYearDay() const;
// 从全年的某天计算日期(阳历)
DateCN& FromYearDay(size_t year, size_t days);
// 转换为星期(阳历),返回0~6,分别代表星期日~星期六
size_t ToWeek() const;
// 转换为阴历(todo)
DateCN& ToLunarDate();
// 转换为阳历(todo)
DateCN& ToSolarDate();
// 计算和指定日期相差的天数(阳历)
size_t Diff(const DateCN &ref) const;
// 向前或者向后调整指定天数(阳历),不处理溢出(主要是指下溢)
// 如果days大于0,则向前调整(未来);否则向后调整(过去)
DateCN& AdjustDays(int days);
// 输出日期,方便调试
friend std::ostream& operator<< (std::ostream &os, const DateCN &date) {
return os << date.year_ << "年" << date.month_ << "月" << date.day_ << "日";
}
private:
// 判断某年是否为闰年(阳历)
static bool DoIsLeapYear(size_t year);
// 计算某年之前的闰年数(阳历)
static size_t DoCountLeapYear(size_t year);
// 计算某年的全年天数(阳历)
static size_t DoCountYearDays(size_t year);
// 计算*this和ref之间相差的天数,且*this >= ref(阳历)
size_t DoDiff(const DateCN &ref) const;
// 向前(未来)调整指定天数(阳历)
DateCN& DoAdjustForward(size_t days);
// 向后(过去)调整指定天数(阳历)
DateCN& DoAdjustBackward(size_t days);
};
#endif // DATE_CN_H_INCLUDED
[/code]
类的源文件:
[code]
/**
* DateCN.cpp
* @Author Tu Yongce <yongce (at) 126 (dot) com>
* @Created 2008-6-28
* @Modified 2008-6-28
* @Version 0.1
*/
#include "DateCN.h"
// 平年中每一个月的天数
const size_t DateCN::monthDays_[12] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
// 平年中每一个月之前所有月的天数
const size_t DateCN::yearDays_[12] = {
0, 31, 59 , 90, 120, 151, 181, 212, 243, 273, 304, 334
};
DateCN::DateCN(size_t year, size_t month, size_t day):
year_(year), month_(month), day_(day)
{
assert(IsValidDate());
}
size_t DateCN::GetYear() const
{
return year_;
}
size_t DateCN::GetMonth() const
{
return month_;
}
size_t DateCN::GetDay() const
{
return day_;
}
// 判断某年是否为闰年(阳历)
bool DateCN::DoIsLeapYear(size_t year)
{
// 四年一闰,百年不闰,四百年再闰
return (!(year % 4) && (year % 100)) || !(year % 400);
}
// 判断是否为闰年(阳历)
bool DateCN::IsLeapYear() const
{
return DoIsLeapYear(year_);
}
// 判断是否为有效日期(阳历)
bool DateCN::IsValidDate() const
{
if (year_ < 1 || month_ < 1 || month_ > 12 || day_ < 1)
return false;
if (month_ != 2 && day_ > monthDays_[month_ - 1])
return false;
size_t feb = (IsLeapYear() ? 29 : 28);
if (month_ == 2 && day_ > feb)
return false;
return true;
}
// 判断是否在指定日期之前
bool DateCN::IsPrior(const DateCN &ref) const
{
return year_ < ref.year_ ||
year_ == ref.year_ && (month_ < ref.month_ || month_ == ref.month_ && day_ < ref.day_);
}
// 计算某年的全年天数(阳历)
size_t DateCN::DoCountYearDays(size_t year)
{
return (DoIsLeapYear(year) ? 366 : 365);
}
// 计算某年的全年天数(阳历)
size_t DateCN::CountYearDays() const
{
return DoCountYearDays(year_);
}
// 计算是该年中的第几天(阳历)
size_t DateCN::CalcYearDay() const
{
size_t days = yearDays_[month_ - 1] + day_;
if (month_ > 2 && IsLeapYear())
++days;
return days;
}
// 从全年的某天计算日期(阳历)
DateCN& DateCN::FromYearDay(size_t year, size_t days)
{
size_t m = 11;
for (; m > 0; --m) {
if (yearDays_[m] < days)
break;
}
year_ = year;
month_ = m + 1;
day_ = days - yearDays_[m];
if (month_ > 2 && IsLeapYear()) {
// 闰年
if (day_ > 1)
--day_;
else {
--month_;
day_ = monthDays_[month_ - 1];
}
}
assert(IsValidDate());
return *this;
}
// 计算某年之前的闰年数(阳历)
size_t DateCN::DoCountLeapYear(size_t year)
{
if (year == 0)
return 0;
size_t y = year - 1;
return y / 4 - y / 100 + y / 400;
}
// 转换为星期(阳历),返回0~6,分别代表星期日~星期六
size_t DateCN::ToWeek() const
{
// 公式:W = [Y-1] + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D
// Y是年份数,D是这一天在这一年中是第几天。
// 算出来的W除以7,余数是几就是星期几。如果余数是0,则为星期日。
// 注:365 = 52 * 7 + 1,公元元年1月1日为星期一
return (year_ - 1 + DoCountLeapYear(year_) + CalcYearDay()) % 7;
}
// 转换为阴历
DateCN& DateCN::ToLunarDate()
{
// todo ...
return *this;
}
// 转换为阳历
DateCN& DateCN::ToSolarDate()
{
// todo ...
return *this;
}
// 计算*this和ref之间相差的天数,且*this >= ref(阳历)
size_t DateCN::DoDiff(const DateCN &ref) const
{
// 首先计算两个年份的1月1日相差的天数
size_t days = (year_ - ref.year_) * 365;
// 算上闰年的额外天数
days += DoCountLeapYear(year_);
days -= DoCountLeapYear(ref.year_);
// 再处理月和日
days += CalcYearDay();
days -= ref.CalcYearDay();
return days;
}
// 计算和指定日期相差的天数(阳历)
size_t DateCN::Diff(const DateCN &ref) const
{
return IsPrior(ref) ? ref.DoDiff(*this) : this->DoDiff(ref);
}
// 向前(未来)调整指定天数(阳历)
DateCN& DateCN::DoAdjustForward(size_t days)
{
// 400年一个轮回
const size_t t1 = 400 * 365 + 100 - 3;
year_ += days / t1 * 400;
days -= days / t1 * t1;
// 从该年1月1日算起
days += CalcYearDay();
size_t y = days / 365;
size_t leapYears = DoCountLeapYear(year_ + y) - DoCountLeapYear(year_);
days -= y * 365;
if (days > leapYears) {
year_ += y;
return FromYearDay(year_, days - leapYears);
} else {
year_ += y - 1;
return FromYearDay(year_, days + 365 - leapYears);
}
}
// 向后(过去)调整指定天数(阳历)
DateCN& DateCN::DoAdjustBackward(size_t days)
{
// 400年一个轮回
const size_t t1 = 400 * 365 + 100 - 3;
year_ -= days / t1 * 400; // 不处理溢出问题
days -= days / t1 * t1;
// 从该年12月31日算起
days += DoCountYearDays(year_) - CalcYearDay();
size_t y = days / 365;
size_t leapYears = DoCountLeapYear(year_ + 1) - DoCountLeapYear(year_ - y + 1);
days -= y * 365;
if (days >= leapYears) {
year_ -= y;
days -= leapYears;
return FromYearDay(year_, DoCountYearDays(year_) - days);
} else {
year_ -= y - 1;
return FromYearDay(year_, DoCountYearDays(year_) - (days + 365 - leapYears));
}
}
// 向前或者向后调整指定天数(阳历),不处理溢出(主要是指下溢)
// 如果days大于0,则向前调整(未来);否则向后调整(过去)
DateCN& DateCN::AdjustDays(int days)
{
if (days > 0)
return DoAdjustForward(days);
else if (days < 0)
return DoAdjustBackward(-days);
return *this;
}
[/code]
tyc611
算法没详细测试,so可能会有bug,有问题还望告知下
tyc611
[quote]原帖由 [i]小公猫[/i] 于 2008-6-29 14:58 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8696677&ptid=1183717][img]http://bbs.chinaunix.net/images/common/back.gif[/img][/url]
没有固定算法?
那手机上的农历是怎么来的? [/quote]
只有公历有固定算法,农历是没有的,它主要根据天文测试来调整的,由于天文数据在现在可以做一段时间内的预测,所以农历可以向前估算N年
这些数据都可以存储起来供查询
我在想农历每年由谁负责发布?
cugb_cat
我以前也找过,发现网上找到的算法都是预测一两百年的,没有万年历
ruoyisiyu
[quote]原帖由 [i]cugb_cat[/i] 于 2008-6-29 18:33 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=8697593&ptid=1183717][img]http://bbs.chinaunix.net/images/common/back.gif[/img][/url]
我以前也找过,发现网上找到的算法都是预测一两百年的,没有万年历 [/quote]
呵呵,毕竟农历只适用于中国,通用性不大的
safedead
农历是阴阳历
月份按照月亮盈亏计算,是阴历
而节气是根据太阳运行轨迹计算,是阳历
由于节气是按照太阳年(不是恒星年)划分,
因此产生的闰月问题
中国古代天文学家就是观测天体,确定节气,计算每个节气在阴历月份中的日子
伊斯兰教历法是纯阴历
Godbach
农历的规律确实不好找,可以看看万年历,找一下规律。:mrgreen:
DraculaW
现在一般的算法大概都是一个table吧 然后在里面查之类的