基于年、月值双向更新儿童出生日期与年龄的技术咨询
Alright, let's tackle this two-way sync between a child's date of birth (DoB) and their age in years/months. I'll walk through the exact logic you described, including the specific examples you provided, and share working code to make it happen.
Core Logic Breakdown
First, let's align on the two key workflows you need:
- Calculate age from DoB: When the DoB is entered/updated, compute the corresponding years and months relative to a fixed current date (or the real current date, if preferred).
- Update DoB from age: When the years or months input is changed, adjust the DoB relative to its previous value (matching your example where changing years to 5 shifts the DoB year back by 2, and changing months to 9 shifts it back by 8 months).
Step 1: Calculate Age from DoB
This function takes a DoB string (MM/dd/YYYY) and a current date, then returns the precise years and months. It handles edge cases where the current day is before the DoB day (e.g., if today is March 2 and the DoB is March 3, we haven't hit the birthday yet, so we adjust the month count).
function calculateAge(dobStr, currentDate = new Date()) { // Parse MM/dd/YYYY into a Date object (note: JS months are 0-indexed) const [dobMonth, dobDay, dobYear] = dobStr.split('/').map(Number); const dob = new Date(dobYear, dobMonth - 1, dobDay); let years = currentDate.getFullYear() - dob.getFullYear(); let months = currentDate.getMonth() - dob.getMonth(); // Adjust if current day is before the DoB day (haven't hit this month's birthday) if (currentDate.getDate() < dob.getDate()) { months--; } // Fix negative month count by reducing the year and adding 12 months if (months < 0) { years--; months += 12; } return { years, months }; }
Testing your example:
- DoB:
02/03/2015(February 3, 2015) - Current date:
03/07/2018(July 3, 2018) - The function returns
{ years: 3, months: 5 }—if your example's 3 years and 1 month is intentional (e.g., counting only full months since the last birthday), we can tweak the logic, but this is the standard precise age calculation.
Step 2: Update DoB from Age (Relative to Original)
This function adjusts the DoB based on changes to the years/months inputs, matching your exact example. It uses the original age to calculate how much to shift the DoB (e.g., changing years from 3 to 5 means shifting the DoB back 2 years; changing months from 1 to 9 means shifting back 8 months).
function updateDobFromAgeRelativeToOriginal(originalDobStr, newYears, newMonths, originalAge) { const [originalMonth, originalDay, originalYear] = originalDobStr.split('/').map(Number); const dob = new Date(originalYear, originalMonth - 1, originalDay); // Shift the year by the difference between new and original years const yearDiff = newYears - originalAge.years; dob.setFullYear(dob.getFullYear() - yearDiff); // Shift the month by the difference between new and original months const monthDiff = newMonths - originalAge.months; dob.setMonth(dob.getMonth() - monthDiff); // Format back to MM/dd/YYYY const formattedMonth = String(dob.getMonth() + 1).padStart(2, '0'); const formattedDay = String(dob.getDate()).padStart(2, '0'); const formattedYear = dob.getFullYear(); return `${formattedMonth}/${formattedDay}/${formattedYear}`; }
Testing your examples:
- Original DoB:
02/03/2015, original age:{ years:3, months:1 } - Change years to 5:
yearDiff =5-3=2→ DoB becomes02/03/2013(matches your example) - Change months to9:
monthDiff=9-1=8→ DoB shifts back 8 months from02/03/2013to05/03/2012(exactly what you described—JS automatically handles negative month values by adjusting the year!)
Step 3: Wire Up the UI
Now we need to connect these functions to your input fields with event listeners. Here's how to do it with vanilla JavaScript:
// Grab DOM elements const dobInput = document.getElementById('dob'); const yearsInput = document.getElementById('years'); const monthsInput = document.getElementById('months'); // Store the current age to use for relative updates let currentAge = { years: 0, months: 0 }; // Use your fixed current date, or new Date() for real-time current date const fixedCurrentDate = new Date(2018, 6, 3); // July 3, 2018 (0-indexed month) // Initialize the inputs when the page loads if (dobInput.value) { currentAge = calculateAge(dobInput.value, fixedCurrentDate); yearsInput.value = currentAge.years; monthsInput.value = currentAge.months; } // Update age when DoB changes dobInput.addEventListener('change', () => { if (!dobInput.value) return; currentAge = calculateAge(dobInput.value, fixedCurrentDate); yearsInput.value = currentAge.years; monthsInput.value = currentAge.months; }); // Update DoB when years or months change function handleAgeInputChange() { const newYears = parseInt(yearsInput.value) || 0; const newMonths = parseInt(monthsInput.value) || 0; if (!dobInput.value) return; // Calculate new DoB const newDob = updateDobFromAgeRelativeToOriginal(dobInput.value, newYears, newMonths, currentAge); dobInput.value = newDob; // Update currentAge to match the new input values currentAge = { years: newYears, months: newMonths }; } yearsInput.addEventListener('change', handleAgeInputChange); monthsInput.addEventListener('change', handleAgeInputChange);
Bonus: Edge Case Handling
To make this more robust, add these checks:
- Validate DoB format: Ensure the input matches MM/dd/YYYY before processing (e.g., using a regex like
^(0[1-9]|1[012])[-/.](0[1-9]|[12][0-9]|3[01])[-/.](19|20)\d\d$). - Limit month input: Prevent values over 11 (since 12 months = 1 year) by auto-converting (e.g., 13 months → 1 year and 1 month).
- Prevent future DoBs: Add a check to ensure the calculated DoB isn't after the current date, and show an error if it is.
内容的提问来源于stack exchange,提问作者Ryan Taite

