Calculate Age within an AppleScript

I have a script to calculate age that I got from this forum and modified it to work for me. The thing is, it isn’t responding to the change in days or month now. I believe that if the set M then D have past you should be at your higher age. The script currently only works if the year difference is higher or lower. Can someone help me?

--SET CURRENT YEAR, MONTH, AND DAY TO VARIABLES Y, M, D
set {year:y, month:m, day:d} to (current date)

--QUERY USER TO ENTER THEIR BIRTHDAY INFORMATION
set BirthdayDay to (17)

set BirthMonth to ("May")

set BirthYear to (1982)

--TURN USER INFORMATION INTO DATE FORMAT

set UserBirthDay to BirthMonth & " " & BirthdayDay & "," & BirthYear as string
set Birthdate to date UserBirthDay

--CONVERT MONTH INTO A NUMBER (INTEGER)
set theMonthNumber to ((month of Birthdate as integer) as string)

--CALCULATE USERS AGE AND WHEN THE USERS NEXT BIRTHDAY IS
set UserAge to y - (year of Birthdate)
set daysleft to (day of Birthdate) - d
set monthleft to (theMonthNumber) - m

--CHECK IF DAYS LEFT ARE NEGATIVE NUMBER AND CONVERT INTO WHOLE NUMBER
if daysleft < 0 then
	set daysleft to daysleft / -1 as integer
end if

--CHECK IF MONTH HAS ALREADY PAST
if monthleft < 0 then
	set monthleft to monthleft + 12
	
else
	set UserAge to UserAge - 1
end if

--SET TEXT TO SHOW USER WHEN BIRTHDAY IS

set BirtdayInfo to UserAge

--CHECK TO SEE IF TODAY IS THE USERS BIRTHDAY
if monthleft = 0 and daysleft = 0 then
	set BirtdayInfo to UserAge & " Happy birthday "
	
end if

BirtdayInfo

Model: 2009 iMac
AppleScript: 2.10
Browser: Chrome 101.0.4951.64
Operating System: macOS 10.13

FWIW, you could strip out a number of lines from your script as all you really have to compare is the birth month/day of the birth year to current month/day, e.g. May 18 of 1982 against May 17 of this year. Then, if the current year’s birthday is in the future, subtract 1 from the difference in years.

-- Current date variables
set ct to current date
set currentYear to year of ct

-- QUERY USER TO ENTER THEIR BIRTHDAY INFORMATION
set BirthDay to (18)
set BirthMonth to ("May")
set BirthYear to (1982)
set birthDate to date (BirthMonth & " " & BirthDay & "," & BirthYear as text)
--> date "Tuesday, May 18, 1982 at 00:00"

-- this year's birthday
set birthdayThisYear to date (BirthMonth & space & BirthDay & ", " & currentYear as text)
--> date "Wednesday, May 18, 2022 at 00:00"

-- birthday is still pending (then subtract 1)
set bdPending to ((ct is greater than birthdayThisYear) as integer) - 1
set ageYears to currentYear - BirthYear + bdPending

p.s. Was a bit concerned about running it on the actual birthday, as the current date’s time wouldn’t match the 00:00 of the birth day —and hence mess up the comparison— but it seems to handle it properly.

I do not know for what additional purposes the script you copied from some forum was intended. But from what you have done, it follows that you have only one goal: to get information about whether the current date is someone’s birthday.

Given the fact that the birthday is either there or not (today), and converting current date to birthday data instead of converting the birthday data into a date, your script can be extremely simplified:


set BirthDay to 18
set BirthMonth to "May"
set BirthYear to 1982

tell (current date) to if {BirthDay, BirthMonth} = {(its day) as integer, (its month) as string} then ¬
	return (((its year) - BirthYear) as text) & " Happy birthday "

My suggestion, which returns age and “happy birthday” on birthday. It breaks if the individual is less than 4 years old and leap year does not fall within that period.

set birthDate to "5/18/1982" -- format must conform to computer setting
set birthDate to date birthDate -- defaults to 12:00 midnight
set todayDate to (current date)

set theYears to (todayDate - birthDate) div days div 365.2425
set theDays to (todayDate - birthDate) div days mod 365.2425 div 1
if theDays < 1 then
	display alert "You are " & theYears & " years old. Happy Birthday!"
else
	display alert "You are " & theYears & " years old."
end if

Nigel is the recognized forum expert on dates, and I look forward to his suggestion :wink:

EDIT: My script also breaks during any leap year–it shouldn’t be used :frowning:

I’d probably use logic similar to Mockman’s: :slight_smile:

--ASSIGN CURRENT YEAR, MONTH, AND DAY TO VARIABLES Y, M, D
set cd to (current date)
set {year:y, month:m, day:d} to cd

--QUERY USER TO ENTER THEIR BIRTHDAY INFORMATION
set BirthdayDay to ("18")
set BirthMonth to ("May")
set BirthYear to ("1982")

--WORK OUT THE USER'S AGE AND WHETHER OR NOT IT'S THEIR BIRTHDAY.
set greeting to ""
set UserAge to y - BirthYear
set BirthMonth to (run script BirthMonth)
if (BirthMonth comes after m) then -- Month enum comparisons actually work!
	set UserAge to UserAge - 1
else if (BirthMonth is m) then
	set BirthdayDay to BirthdayDay as integer
	if (BirthdayDay comes after d) then
		set UserAge to UserAge - 1
	else if (BirthdayDay is d) then
		set greeting to " Happy Birthday!"
	end if
end if
set BirthdayInfo to (UserAge as text) & greeting

Or:

--ASSIGN CURRENT YEAR, MONTH, AND DAY TO VARIABLES Y, M, D
set cd to (current date)
set {year:y, month:m, day:d} to cd

--QUERY USER TO ENTER THEIR BIRTHDAY INFORMATION
set BirthdayDay to ("18")
set BirthMonth to ("May")
set BirthYear to ("1982")

--WORK OUT THE USER'S AGE AND WHETHER OR NOT IT'S THEIR BIRTHDAY.
set greeting to ""
set UserAge to y - BirthYear
-- Get today's date in the birth year.
set cd's year to BirthYear
-- Derive a date object for the birth date.
copy cd to BirthDate
set BirthMonth to (run script BirthMonth)
tell BirthDate to set {its day, its month, its day} to {1, BirthMonth, BirthdayDay}
-- Compare the two dates.
if (BirthDate comes after cd) then
	set UserAge to UserAge - 1
else if (BirthDate = cd) then
	set greeting to " Happy Birthday!"
end if
set BirthdayInfo to (UserAge as text) & greeting

Thanks Nigel. I tested your script from 1 day old to 101 years old and it worked great in every instance. BTW, I was curious why the first line below is necessary, when the second line seems to work as expected.

tell BirthDate to set {its day, its month, its day} to {1, BirthMonth, BirthdayDay}
tell BirthDate to set {its month, its day} to {BirthMonth, BirthdayDay}

Hi peavine. BirthDate’s being reset from a copy of cd, so setting its day to 1 before changing its month ensures there’s no overflow during that process. However, I must admit that the vulnerability also exists three lines earlier when cd’s year’s changed to the birth year. If the script’s run on 29th February in a leap year and the user’s birthday’s 1st March in a non-leap year, it’ll wish them a happy birthday anyway. :rolleyes:

Thanks Nigel–that makes sense.

BTW, Nigel’s suggestion appears to be the only one that will work regardless of the user’s date settings in System Preferences (e.g. MM-DD-YY versus DD-MM-YY). This might be important to the OP if the script is to be widely used.

What about KniazidisR’s script ? He offered a short, simple script that I think does what is needed, so I wonder if I missed some finer points that make the other, lengthier scripts more appropriate for the task somehow ?

CK. Thanks for the correction, and my apologies to KniazidisR. His script works fine regardless of the user’s date setting.

Nigel’s scripts also return the correct result regardless of the user’s date setting, plus they return the user’s age on days other than their birthdate. I don’t know if this is something the OP needs, but it will almost certainly be helpful to other forum members and Googler’s who consult this thread in the future.

The OP has several great scripts to choose from, and, FWIW, I thought I’d attempt to provide an ASObjC solution. I wanted the script to return the user’s current age and to include a “Happy Birthday” salutation when applicable. It seems like this could be done with NSDate alone or paired with NSCalendar, but I just couldn’t think where to start. My final script works, but it pretty much takes the same approach as others above and has nothing new to offer.

BTW, the script potentially breaks if the user was born on leap day, and, in this case, some error correction is needed. My inclination would be to ask the user the month and day they celebrate their birthday (i.e. February 28th or March 1st).

use framework "Foundation"
use scripting additions

set birthYear to 1982
set birthDay to 23
set birthMonth to "May"
set birthMonth to (run script birthMonth) as integer -- from Nigel

set currentDate to current application's NSDate's |date|()
set dateFormatter to current application's NSDateFormatter's new()
dateFormatter's setDateFormat:"d.M.yyyy"
set currentDate to (dateFormatter's stringFromDate:currentDate)
set currentDate to ((currentDate's componentsSeparatedByString:".")'s valueForKey:"integerValue") as list

if birthDay = item 1 of currentDate and birthMonth = item 2 of currentDate then
	set userAge to (item 3 of currentDate) - birthYear
	display alert "Happy Birthday! Your age is " & userAge & " years."
	error number -128
else if birthMonth < item 2 of currentDate then
	set userAge to (item 3 of currentDate) - birthYear
else if birthMonth > item 2 of currentDate then
	set userAge to (item 3 of currentDate) - birthYear - 1
else
	if birthDay < item 1 of currentDate then
		set userAge to (item 3 of currentDate) - birthYear
	else
		set userAge to (item 3 of currentDate) - birthYear - 1
	end if
end if

display alert "Your age is " & userAge & " years."

There are probably several approaches, but here’s one. (I’ve cheated by having the month as a number to keep it simple.)

use framework "Foundation"
use scripting additions

set birthYear to 1982
set birthDay to 24
set birthMonth to 5

set theCal to current application's NSCalendar's currentCalendar()
set birthDate to theCal's dateWithEra:1 |year|:birthYear |month|:birthMonth |day|:birthDay hour:0 minute:0 |second|:0 nanosecond:0
set componentsRequired to (current application's NSCalendarUnitYear) + (get current application's NSCalendarUnitMonth) + (get current application's NSCalendarUnitDay) -- could just use 28
set theComponents to theCal's components:componentsRequired fromDate:birthDate toDate:(current date) options:0
set ageInYears to theComponents's |year|()
if theComponents's |month|() = 0 and theComponents's |day|() = 0 then
	display alert "Happy Birthday! Your age is " & ageInYears & " years."
else
	display alert "Your age is " & ageInYears & " years."
end if

Thanks Shane–that’s excellent. The script works precisely as I envisioned, but I had no idea how to accomplish that. Having a good example to work with always makes the learning process so much easier.