PHP万年历——突破mktime函数的时间限制 作者: 黑马
http://www.gxblk.com/art.php?ad=pc&ads=no11&idx=x7&page=0

做PHP万年历的最大困难有两个方面:一是mktime()函数的局限,二是Windows平台下Windows对合法时间的规定。

PHP函数mktime()用以获取UNIX时间戳记,即从UNIX时代创建以来到指定时间的总秒数,其意义是为date()等函数处理时间信息提供一个可解析的时间数据。UNIX时代从1970年1月1日开始,因此理论上mktime()能够返回的有效数据只适用于1970年以后的年份。

比起UNIX时间系统,Windows则显得小气了,它的合法时间系统更为狭小。如若PHP运行于Windows平台,mktime()函数还得看Windows的脸色。在UNIX和Linux操作环境下,date()通过mktime()读取1970年以前某个日期的时间戳记总是能够返回一个值(虽然不正确),而获取2038年以后的时间戳记,Windows平台下date()也会报错。

上述问题不解决,PHP万年历就名不副实。解决的思路之一是:绕开date()和mktime()函数,即,不使用它们参与处理日历的核心内容。

这需要对阳历进行必要的研究,深入掌握其内在规律。

阳历经历了三个时代:儒略历、奥古斯都历和现在仍然通行的格里历(又称格列高利历)。格里历由罗马教皇格列高利十三世于1582年10月15日(原奥古斯都历的10月5日,这一天不存在)倡导使用,直至今日。我们现在做万年历就是基于格里历的日历,即,做万年历要考虑的因素是格里历的规律。往下我们讨论的“阳历”指的就是格里历。

阳历有如下几个规则:

一. 一年的总天数:平年365天,闰年366天;

二. 闰年:非世纪年的年份(即不被100整除的年份)被4整除的为闰年,世纪年的年份(即被100整除的年份)被400整除的年份为闰年。闰年的二月份有29天;

三. 月份天数:

1. 大月(31天):一月、三月、五月、七月、八月、十月、十二月;
2. 小月(30天):四月、六月、九月、十一月;
3. 二月为28天,闰年为29天。

这些规律足以让我们抛开PHP的时间日期函数,取而代之的是使用我们根据格里历规律编写的自定义函数。

 

首先判断处理年(y年)是否为闰年,这将关系到处理年的总天数和二月的总天数:

function is_yleap($y) { //是否闰年: 返回1(代表闰年)或0(非闰年)
    return($y%100==0 ? $result=($y%400==0 ? 1 : 0) : $result=($y%4==0 ? 1 : 0));
}

能够判断任意年是否为闰年之后,我们就可以计算处理年的总天数:

function y_days($y) { //y年的总天数
    return($total=365+is_yleap($y));
}

以及处理年的各月的总天数:

function m_days($y,$m) { //y年m月的总天数
    switch($m) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            $total=31;
            break;
        case 4:
        case 6:
        case 9:
        case 11:
            $total=30;
            break;
        case 2:
            $total=28+is_yleap($y);
            break;
    }
    return $total;
}

现在还剩下一个写日历更为核心的问题:判断每个月的第一天是星期几。这个问题必须解决,否则,日历每月的一日写在哪里无从确定。

我们可以使用著名的蔡勒(Zeller)公式来求得y年m月n日是星期几。以下是经数模原理简化过的蔡勒公式:

W = [C/4]-2C+y+[y/4]+[13*(M+1)/5]+d-1 ... mod(7)

y为年份数(二位),C为世纪数(二位),m为月份数,d为日期数。求出W值后对7取模(即求余数)将得到从0到6的值,依次代表从周日到周六。这个公式不必考虑闰年因素,但要求:一、1月和2月视为13月和14月,同时年份减去1,减去后若世纪数受影响,则按影响后的变化取值;二、中括号里的算式仅取计算结果的整数部分,小数点后的值忽略不计。

根据蔡勒公式,我们可以很方便地判断处理年(y年)处理月(m月)第一天是星期几:

function wk1day($y,$m) { //当月第一天周几
    if($m<3) { //一月和二月按上一年的13月和14月取值
        $y-=1;
        if($m==1) $m=13;
        if($m==2) $m=14;
    }
    $c=substr($y,0,2);
    $yy=substr($y,2,2);
    $cal=floor($c/4) - 2*$c + $yy + floor($yy/4) + floor(13*($m+1)/5);
    return ($cal+700)%7;
}

至此,绕开UNIX和Windows时间限制制作万年历的条件已经具备,理论上它的有效性可以从1582年10月15日起到4049年。4049年以后,由于地球自转等因素,阳历日期与太阳回归年将出现较大的误差,届时格里历如果还使用的话将面临着调整。




php关于1970年前日期的处理
http://blog.csdn.net/fzhlee/archive/2010/07/27/5769049.aspx

我在处理一篇会员注册页面时,客户要求日期从1960年开始,这样就会产生以下问题:

Warning : date() [function.date ]: Windows does not support dates prior to midnight (00:00:00), January 1, 1970 in c:\program files\wamp\www\friends\admin\users_manager.php on line 320

Warning : date() [function.date ]: Windows does not support dates prior to midnight (00:00:00), January 1, 1970 in c:\program files\wamp\www\friends\admin\users_manager.php on line 321

Warning : date() [function.date ]: Windows does not support dates prior to midnight (00:00:00), January 1, 1970 in c:\program files\wamp\www\friends\admin\users_manager.php on line 322

在网上搜了半天,没找到合适的办法,于是千思万想,想到了以下的办法,解决了这个问题:

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/fzhlee/archive/2010/07/27/5769049.aspx