/**
* 截取身份证年、月、日,通过与当前时间戳的比较计算年龄
* @param string $idcard 身份证
* @return int
*/
function getAgeByID($idcard) {
// 过了这年的生日才算多了一周岁
if(empty($idcard)) return 0;
// 获得出生年月日的时间戳
$date = strtotime(substr($idcard, 6, 8));
// 获得今日的时间戳
$today = strtotime('today');
// 得到两个日期相差的大体年数
$diff = floor(($today-$date)/ 86400/ 365);
// strtotime 加上这个年数后得到那日的时间戳后与今日的时间戳相比
$age = strtotime(substr($idcard, 6, 8).' +'.$diff.'years') > $today? ($diff+1): $diff;
return $age;
}
/**
* 通过截取身份证年、月、日来进行比较计算年龄
* @param string $idcard 身份证
* @return int
*/
function getAgeByIdcard($idcard) {
if(!$idcard) return 0;
$year = substr($idcard, 6, 4);
$monthDay = substr($idcard, 10, 4);
$age = date('Y') - $year;
if($monthDay > date('md')) {
$age--;
}
return $age;
}
/**
* 根据身份证计算年月龄
* @param string $idcard
* @param string $date
* @return string
*/
function getAgeYMDByIdcard($idcard, $date='') {
if(!$idcard) return 0;
$year = substr($idcard, 6, 4);
$month = substr($idcard, 10, 2);
$day = substr($idcard, 12, 2);
$date = $date ?: date('Y-m-d');
$diffDate = diffDate($year.'-'.$month.'-'.$day, $date);
return $diffDate['year'].'周岁'.$diffDate['month'].'月龄'.$diffDate['day'].'天';
}
/**
* 通过身份证获取出生年月日
* @param string $idcard
* @return string
*/
function getBirthdayByIdcard($idcard) {
if(!$idcard) return '';
$year = substr($idcard, 6, 4);
$month = substr($idcard, 10, 2);
$day = substr($idcard, 12, 2);
return $year.'-'.$month.'-'.$day;
}
/**
* 功能:把15位身份证转换成18位
*
* @param string $idCard
* @return newid or id
*/
function getIDCard($idCard) {
// 若是15位,则转换成18位;否则直接返回ID
if (15 == strlen($idCard)) {
$W = array(7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2,1 );
$A = array("1","0","X","9","8","7","6","5","4","3","2" );
$s = 0;
$idCard18 = substr($idCard, 0, 6)."19".substr($idCard, 6);
$idCard18_len = strlen($idCard18);
for($i = 0; $i < $idCard18_len; $i++) {
$s = $s + substr($idCard18, $i, 1) * $W[$i];
}
$idCard18 .= $A[$s % 11];
return $idCard18;
} else {
return $idCard;
}
}
/**
* 已测试,百度很多写法不行的
* 1就是男性 2就是女性
* 通过身份证获取性别类型
* @param type $card
* @return int
*/
function getCardSex($idcard) {
if(empty($idcard)) return null;
$sexint = (int)substr($idcard, 16, 1);
return $sexint % 2 === 0 ? '2' : '1';
}
/**
*function:计算两个日期相隔多少年,多少月,多少天
*param string $date1[格式如:2011-11-5]
*param string $date2[格式如:2012-12-01]
*return array array('年','月','日');
*@author Donlyn
*<li>created at 2018-03-05</li>
*/
function diffDate($date1, $date2)
{
if (strtotime($date1) > strtotime($date2)) {
$tmp = $date2;
$date2 = $date1;
$date1 = $tmp;
}
list ($Y1, $m1, $d1) = explode('-', $date1);
list ($Y2, $m2, $d2) = explode('-', $date2);
$Y = $Y2 - $Y1;
$m = $m2 - $m1;
$d = $d2 - $d1;
if ($d < 0) {
$d += (int)date('t', strtotime("-1 month $date2"));
$m--;
}
if ($m < 0) {
$m += 12;
$Y--;
}
return array(
'year' => $Y,
'month' => $m,
'day' => $d
);
}
class IdentityCard
{
/**
* 校验身份证号是否合法
* @param string $num 待校验的身份证号
* @return bool
*/
public static function isValid($num)
{
//老身份证长度15位,新身份证长度18位
$length = strlen($num);
if ($length == 15) { //如果是15位身份证
//15位身份证没有字母
if (!is_numeric($num)) {
return false;
}
// 省市县(6位)
$areaNum = substr($num, 0, 6);
// 出生年月(6位)
$dateNum = substr($num, 6, 6);
} else if ($length == 18) { //如果是18位身份证
//基本格式校验
if (!preg_match('/^\d{17}[0-9xX]$/', $num)) {
return false;
}
// 省市县(6位)
$areaNum = substr($num, 0, 6);
// 出生年月日(8位)
$dateNum = substr($num, 6, 8);
} else { //假身份证
return false;
}
//验证地区
if (!self::isAreaCodeValid($areaNum)) {
return false;
}
//验证日期
if (!self::isDateValid($dateNum)) {
return false;
}
//验证最后一位
if (!self::isVerifyCodeValid($num)) {
return false;
}
return true;
}
/**
* 获取身份证上的出生年月日
* @author donlyn
* @date 211029
*/
public static function getBirthDay($idcard) {
if(self::isValid($idcard)) {
$birth_day = strlen($idcard)==15 ? ('19' . substr($idcard, 6, 6)) : substr($idcard, 6, 8);
return substr($birth_day,0,4).'-'.substr($birth_day,4,2).'-'.substr($birth_day,6,2);
}
return '';
}
/**
* 省市自治区校验
* @param string $area 省、直辖市代码
* @return bool
*/
private static function isAreaCodeValid($area) {
$provinceCode = substr($area, 0, 2);
// 根据GB/T2260—999,省市代码11到65
if (11 <= $provinceCode && $provinceCode <= 65) {
return true;
} else {
return false;
}
}
/**
* 验证出生日期合法性
* @param string $date 日期
* @return bool
*/
private static function isDateValid($date) {
if (strlen($date) == 6) { //15位身份证号没有年份,这里拼上年份
$date = '19'.$date;
}
$year = intval(substr($date, 0, 4));
$month = intval(substr($date, 4, 2));
$day = intval(substr($date, 6, 2));
//日期基本格式校验
if (!checkdate($month, $day, $year)) {
return false;
}
//日期格式正确,但是逻辑存在问题(如:年份大于当前年)
$currYear = date('Y');
if ($year > $currYear) {
return false;
}
return true;
}
/**
* 根据身份证号码计算年龄
*/
public static function getAgeByID($id){
//过了这年的生日才算多了1周岁
if(empty($id)) return '';
$date=strtotime(substr($id,6,8));
//获得出生年月日的时间戳
$today=strtotime('today');
//获得今日的时间戳 111cn.net
$diff=floor(($today-$date)/86400/365);
//得到两个日期相差的大体年数
//strtotime加上这个年数后得到那日的时间戳后与今日的时间戳相比
$age=strtotime(substr($id,6,8).' +'.$diff.'years')>$today?($diff+1):$diff;
return $age;
}
/**
* 验证18位身份证最后一位
* @param string $num 待校验的身份证号
* @return bool
*/
private static function isVerifyCodeValid($num)
{
if (strlen($num) == 18) {
$factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
$tokens = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
$checkSum = 0;
for ($i = 0; $i < 17; $i++) {
$checkSum += intval($num{$i}) * $factor[$i];
}
$mod = $checkSum % 11;
$token = $tokens[$mod];
$lastChar = strtoupper($num{17});
if ($lastChar != $token) {
return false;
}
}
return true;
}
}
/*
// 测试
$num = '33071919610920021X';
if (IdentityCard::isValid($num)) {
echo '身份证格式正确';
} else {
echo '身份证格式不正确';
}
*/
//=============新的18位身份证号码各位的含义:=======================
//1-2位省、自治区、直辖市代码;11-65
//3-4位地级市、盟、自治州代码;
//5-6位县、县级市、区代码;
//7-14位出生年月日,比如19670401代表1967年4月1日;
//15-17位为顺序号,其中17位男为单数,女为双数;
//18位为校验码,0-9和X,由公式随机产生。
//举例:
//130503 19670401 0012这个身份证号的含义: 13为河北,05为邢台,03为桥西区,出生日期为1967年4月1日,顺序号为001,2为验证码
//===========15位身份证号码各位的含义:=======================
//1-2位省、自治区、直辖市代码;
//3-4位地级市、盟、自治州代码;
//5-6位县、县级市、区代码;
//7-12位出生年月日,比如670401代表1967年4月1日,这是和18位号码的第一个区别;
//13-15位为顺序号,其中15位男为单数,女为双数;
//与18位身份证号的第二个区别:没有最后一位的验证码。
//举例:
//130503 670401 001的含义; 13为河北,05为邢台,03为桥西区,出生日期为1967年4月1日,顺序号为001。