fix(employees): preserve age across birthday boundaries

This commit is contained in:
caoxiaozhu
2026-07-20 10:29:42 +08:00
parent c59990dc35
commit 044a5669fe
3 changed files with 120 additions and 20 deletions

View File

@@ -303,21 +303,34 @@ export function resolveOrganizationOptions(metaOrganizations) {
.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'))
}
export function calculateAgeFromDate(dateString) {
if (!dateString) {
export function calculateAgeFromDate(dateString, referenceDate = new Date()) {
if (!isValidIsoDate(dateString) || !isValidReferenceDate(referenceDate)) {
return ''
}
const birthDate = new Date(`${dateString}T00:00:00`)
if (Number.isNaN(birthDate.getTime())) {
const [birthYearText, birthMonthText, birthDayText] = dateString.split('-')
const birthYear = Number.parseInt(birthYearText, 10)
const birthMonth = Number.parseInt(birthMonthText, 10)
const birthDay = Number.parseInt(birthDayText, 10)
const referenceYear = referenceDate.getFullYear()
const referenceMonth = referenceDate.getMonth() + 1
const referenceDay = referenceDate.getDate()
const referenceDateNumber = referenceYear * 10000 + referenceMonth * 100 + referenceDay
const birthDateNumber = birthYear * 10000 + birthMonth * 100 + birthDay
if (birthDateNumber > referenceDateNumber) {
return ''
}
const today = new Date()
let age = today.getFullYear() - birthDate.getFullYear()
const [birthdayMonth, birthdayDay] = normalizeBirthdayForYear(
birthMonth,
birthDay,
referenceYear
)
let age = referenceYear - birthYear
const hasBirthdayPassed =
today.getMonth() > birthDate.getMonth() ||
(today.getMonth() === birthDate.getMonth() && today.getDate() >= birthDate.getDate())
referenceMonth > birthdayMonth ||
(referenceMonth === birthdayMonth && referenceDay >= birthdayDay)
if (!hasBirthdayPassed) {
age -= 1
@@ -326,31 +339,56 @@ export function calculateAgeFromDate(dateString) {
return age >= 0 ? String(age) : ''
}
export function calculateBirthDateFromAge(ageValue, existingBirthDate = '') {
export function calculateBirthDateFromAge(
ageValue,
existingBirthDate = '',
referenceDate = new Date()
) {
const age = Number.parseInt(String(ageValue ?? '').trim(), 10)
if (Number.isNaN(age) || age < 0 || age > 120) {
return existingBirthDate || ''
}
const today = new Date()
let month = '01'
let day = '01'
if (!isValidReferenceDate(referenceDate)) {
return existingBirthDate || ''
}
let month = 1
let day = 1
if (existingBirthDate && isValidIsoDate(existingBirthDate)) {
const [, monthText, dayText] = existingBirthDate.split('-')
month = monthText
day = dayText
month = Number.parseInt(monthText, 10)
day = Number.parseInt(dayText, 10)
}
let birthYear = today.getFullYear() - age
let candidate = `${birthYear}-${month}-${day}`
const referenceYear = referenceDate.getFullYear()
const referenceMonth = referenceDate.getMonth() + 1
const referenceDay = referenceDate.getDate()
const [birthdayMonth, birthdayDay] = normalizeBirthdayForYear(month, day, referenceYear)
const hasBirthdayPassed =
referenceMonth > birthdayMonth ||
(referenceMonth === birthdayMonth && referenceDay >= birthdayDay)
const birthYear = referenceYear - age - (hasBirthdayPassed ? 0 : 1)
const [birthMonth, birthDay] = normalizeBirthdayForYear(month, day, birthYear)
if (Number(calculateAgeFromDate(candidate)) > age) {
birthYear -= 1
candidate = `${birthYear}-${month}-${day}`
return `${birthYear}-${padDatePart(birthMonth)}-${padDatePart(birthDay)}`
}
function isLeapYear(year) {
return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
}
function isValidReferenceDate(value) {
return typeof value?.getTime === 'function' && !Number.isNaN(value.getTime())
}
function normalizeBirthdayForYear(month, day, year) {
if (month === 2 && day === 29 && !isLeapYear(year)) {
return [2, 28]
}
return candidate
return [month, day]
}
export function matchKeyword(employee, keyword) {

View File

@@ -0,0 +1,44 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import {
calculateAgeFromDate,
calculateBirthDateFromAge,
isValidIsoDate
} from '../src/views/scripts/employeeManagementModel.js'
test('根据年龄反算未到生日的出生年份不会少一年', () => {
const referenceDate = new Date('2026-07-18T00:00:00')
const birthDate = calculateBirthDateFromAge('30', '1990-12-31', referenceDate)
assert.equal(birthDate, '1995-12-31')
assert.equal(calculateAgeFromDate(birthDate, referenceDate), '30')
})
test('根据年龄反算已过生日的出生年份保持当年差值', () => {
const referenceDate = new Date('2026-07-18T00:00:00')
const birthDate = calculateBirthDateFromAge('30', '1990-01-01', referenceDate)
assert.equal(birthDate, '1996-01-01')
assert.equal(calculateAgeFromDate(birthDate, referenceDate), '30')
})
test('零岁且生日月日尚未到来时不会生成未来出生日期', () => {
const referenceDate = new Date('2026-07-18T00:00:00')
const birthDate = calculateBirthDateFromAge('0', '2000-12-31', referenceDate)
assert.equal(birthDate, '2025-12-31')
assert.equal(isValidIsoDate(birthDate), true)
assert.equal(new Date(`${birthDate}T00:00:00`) <= referenceDate, true)
assert.equal(calculateAgeFromDate(birthDate, referenceDate), '0')
})
test('闰日生日跨非闰年时使用二月二十八日并保持年龄回算一致', () => {
const referenceDate = new Date('2024-03-01T00:00:00')
const birthDate = calculateBirthDateFromAge('23', '2000-02-29', referenceDate)
assert.equal(birthDate, '2001-02-28')
assert.equal(isValidIsoDate(birthDate), true)
assert.equal(calculateAgeFromDate(birthDate, referenceDate), '23')
assert.equal(calculateAgeFromDate('2000-02-29', new Date('2023-02-28T00:00:00')), '23')
})