有没有研究过农历的

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,有问题还望告知下

小公猫
没有固定算法?
那手机上的农历是怎么来的?

gaocheng
LZ的意思是查表

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吧 然后在里面查之类的