3
\$\begingroup\$

Here is method to calculate age given the birth date in the format YYYYMMDD.

function getAge(dateString) {
    var today = new Date();
    var birthDate = new Date(dateString);
    var age = today.getFullYear() - birthDate.getFullYear();
    var m = today.getMonth() - birthDate.getMonth();
    if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
        age--;
    }
    return age;
}

Is it possible to make it more readable and easy to understand? I don't want moment.js

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Looks quite reasonable to me. \$\endgroup\$ – CertainPerformance Oct 29 '20 at 14:42
  • \$\begingroup\$ The current question title, which states your concerns about the code, applies to too many questions on this site to be useful. The site standard is for the title to simply state the task accomplished by the code. Please see How do I ask a good question?. \$\endgroup\$ – BCdotWEB Oct 29 '20 at 15:16
  • \$\begingroup\$ stackoverflow.com/a/21984136/648075 \$\endgroup\$ – BCdotWEB Oct 29 '20 at 15:17
  • \$\begingroup\$ The shorter, accepted answer in the linked question has precision problems, as described. This is probably the best alternative, or near to it, though I'm not enthusiastic about the age-- \$\endgroup\$ – CertainPerformance Oct 29 '20 at 15:32
  • \$\begingroup\$ This solution does not consider two special cases I can think of. 1. Some people were born under the Julian calendar, so the date must be converted to Gregorian first. 2. Under common law, a person is considered to reach a given age one day sooner than you would expect. A person born 10 Jan. 1999 became 21 under common law at the beginning of the day 9 Jan. 2020. Look it up: lexology.com/library/… \$\endgroup\$ – Gerard Ashton Oct 29 '20 at 19:58
1
\$\begingroup\$

Non standard date string YYYYMMDD

As pointed out in the comments "Looks quite reasonable..." and as such there is not too much to review...

Well apart from the fact that new Date("19671031") is not a valid date The date string as parsed by Date must be a valid date strings. (I did not spot this until halfway through the review)

Ignoring the fact your code does not work as a simple oversight and continuing on as initially started...

...I will therefor nit pick, and I do stress the point this is just nit picking for sake of a review.

Constants

Constants as const The named vars today, birthDate, m can all be const. In fact the whole thing can be rewritten to only use constants.

Names

Names are too verbose. The general rule is to use the scope to infer meaning, for names to preference 1 word over 2, 2 words over 3, etc... Short words rather than long. Common abbreviations instead of full words.

  • birthDate can be birth as date is inferred
  • dateString can be dateStr using the common abbreviation for string
  • today could be now. In context today is the most accurate however nothing is lost if now is used.
  • m does not work as month. The value represents a quantitative query (How many months?) and as such months or better yet the abbreviated mths... eww ... drop the s for mth

Rewrite A

And then I realize it does not work

function getAge(dateStr) {
    const birth = new Date(dateStr);
    const now = new Date();
    const mth = now.getMonth() - birth.getMonth();
    const adjust = mth < 0 || (mth === 0 && now.getDate() < birth.getDate()) ? 1 : 0;
    return now.getFullYear() - birth.getFullYear() - adjust;
}

Responsibility

A general rule for functions is that they should only do one thing. Your function has two tasks, parse the date string and calculate the age.

As the date string is likely an un-trusted source (eg user input) and as such requires vetting which is outside the functions responsibility (inferred by the name getAge) the function should accept a Date rather than a string to parse.

This solves the problem of incorrectly formatted date string.

Rewrite B

As the birth date is now an argument the simple name birth does not hold enough meaning to be clear so it is changed back to birthDate

function getAge(birthDate) {
    const now = new Date();
    const mth = now.getMonth() - birthDate.getMonth();
    const adjust = mth < 0 || (mth === 0 && now.getDate() < birthDate.getDate()) ? 1 : 0;
    return now.getFullYear() - birthDate.getFullYear() - adjust;
}

Parsing and vetting

The task of vetting and parsing the date string can now be handed to a more appropriate function.

The function assumes that the format is YYYYMMDD and returns a date only if the Date accepts the year, month, and day extracted from the string, that the date is earlier than now. It will return an age that fits within the Date object range (age of historical births)

If the date is invalid it can be said to be undefined and as such the function returns a Date or undefined.

function parseBirthDate(dateStr) {
    const birth = new Date(dateStr.slice(0,4), dateStr.slice(4,6), dateStr.slice(6,8)););
    const since = Date.now() - birth.getTime();
    return isNaN(since) || since < 0 ? undefined : birth;
}

Using the two functions to get an age.

const birth = parseBirthDate("19671031");
const age = birth ? getAge(birth) : undefined;

Testing

A simple manual test snippet accepts a string input

function getAge(birthDate) {
    const now = new Date();
    const mth = now.getMonth() - birthDate.getMonth();
    const adjust = mth < 0 || (mth === 0 && now.getDate() < birthDate.getDate()) ? 1 : 0;
    return now.getFullYear() - birthDate.getFullYear() - adjust;
}
function parseBirthDate(dateStr) {
    const birth = new Date(dateStr.slice(0,4), dateStr.slice(4,6), dateStr.slice(6,8));
    const since = Date.now() - birth.getTime();
    return isNaN(since) || since < 0 ? undefined : birth;
}
birthDate.addEventListener("change", () => {
    const birth = parseBirthDate(birthDate.value);
    const age = birth ? getAge(birth) : undefined;
    ageDisplay.textContent = age !== undefined ? 
        "Age: " + age + " years old" : 
        "Invalid birth date!";
})
    
<label for="birthDate">Enter birth date:</label>
<input type="text" id="birthDate"  maxlength="8" placeholder = "YYYYMMDD" size="8">
<div id="ageDisplay"></div>

\$\endgroup\$
0
\$\begingroup\$

I've removed the use of m and added comments for clarity:

function getAge(dateString) {
    var today = new Date();
    var birthDate = new Date(dateString);
    
    /* year difference */
    var age = today.getFullYear() - birthDate.getFullYear();
    
    /* check today: */
    if(
          /* is before birth month */
          (today.getMonth() < birthDate.getMonth())
        
        /* or */
        ||
          (
               /* is birth month */
              (today.getMonth() == birthDate.getMonth())
            
            /* AND */
            &&
              /* is before birth day of month */
              (today.getDate()  <  birthDate.getDate())      
          ) 
    ) {
        /* => no birthday yet this year */
        /* so, one year less */
        age--;
    }
    return age;
}

Hope this clarify what's going on in the original function.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.