Now I have to check if it’s valid.
Even here at home if I switch from MacBook to iMac there can be some issues
(not only but also because of date format {yy/mm/dd, dd/mm/yyy, and so on})
I had a look at a lot of Applesripts and didn’t find a routine good enough that
is checking the input of valid year, month and day …
That’s also very difficult because o the different date formats that may occur.
So I came at the end to totally other solution,
that is saving me a plausibility check:
AppleScript calls the App “Calendar” or “iCal” and the should be returned by the app.
Kalle_Ger. I don’t know an answer to your first question. As to your second question, it seems that you might want to perform two checks, the first being whether the user input is seen as a valid date. For example,
try
set theDate to "2/22"
set enteredDate to date theDate --> error "Invalid date and time date 2/22 of «script»." number -30720
on error
display alert "The date entered is not recognized as a valid date"
end try
Then you should probably check to see if the user input is within a valid range. The following demonstrates how this can go wrong (you already know this):
set theDate to "6/11/22" -- correct format on my computer
date theDate --> date "Saturday, June 11, 2022 at 12:00:00 AM"
set theDate to "11/6/22" -- incorrect format on my computer
date theDate --> date "Sunday, November 6, 2022 at 12:00:00 AM"
The manner in which you check if the date is within a valid range depends on what you consider a valid range. For example, the following checks to see if the entered date is within the next 90 days:
set theDate to "6/11/22"
set enteredDate to date theDate
if enteredDate < (current date) or enteredDate > ((current date) + 90 * days) then
display alert "The entered date must be within the next 90 days"
end if
As regards time, a similar check is possible, but you need to provide a bit more information as to what you want to check.
but sorry, the check (script #1) is not of great help.
It doesn’t check if a valid date is entered.
I had several tests and didn’t get with your script en error message:
here the results:
(a) --set theDate to “2/22” – Result: Error Message
(b) --set theDate to “1/3/22”. – Result: NO ERROR MESSAGE, first of March 2022
(c) --set theDate to “1/13/22” – Result: NO ERROR MESSAGE, 1st of January 2023
(d) --set theDate to “1/23/22” – Result: NO ERROR MESSAGE, 1st of November 2023
(e) --set theDate to “1/83/22” – Result: NO ERROR MESSAGE, 1st of November 2028
(f) --set theDate to “18/3/22” – Result: NO ERROR MESSAGE, 18th of March 2022
(g) --set theDate to “78/3/22” – Result: NO ERROR MESSAGE, 17th of May 2022
in examples d, e and g I want an error message of a number in a date that is not expect for such data type…
--prepare a list of leap years:
set leapYears to {}
repeat with eachleapyear from 1980 to 2080 by 4
set end of leapYears to eachleapyear as text
end repeat
--prepare a range of years:
set allYears to {}
repeat with thisYear from 1980 to 2080
set end of allYears to thisYear as text
end repeat
-- user chooses a year:
try
set chosenYear to item 1 of (choose from list allYears with prompt "Choose a year:")
on error --dialog was cancelled, stop the script
return
end try
set leapYearChosen to false
if chosenYear is in leapYears then set leapYearChosen to true
--prepare a list of months:
set allmonths to {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
--define months with 30 or 31 days (omitting February):
set monthsof30 to {"April", "June", "September", "November"}
set monthsof31 to {"January", "March", "May", "July", "August", "October", "December"}
-- user chooses a month:
try
set chosenMonth to item 1 of (choose from list allmonths with prompt "Choose a month:")
on error --dialog was cancelled, stop the script
return
end try
--determine number of days in the chosen month:
set lastDay to 28 -- February by default
if chosenMonth is in monthsof31 then set lastDay to 31
if chosenMonth is in monthsof30 then set lastDay to 30
--allow for leap years:
if chosenMonth is "February" and leapYearChosen is true then set lastDay to 29
--prepare list of days for selected month and year:
set allDays to {}
repeat with x from 1 to lastDay
set end of allDays to x as text
end repeat
--user chooses a day:
try
set chosenDay to item 1 of (choose from list allDays with prompt "Choose a day:")
on error --dialog was cancelled, stop the script
return
end try
--return date from user's choices
set chosenDate to date (chosenDay & " " & chosenMonth & " " & chosenYear)
This offers a choice of years from 1980 to 2080. Depending on the date range you expect, you would need to modify the logic to allow for the fact that 1900 and 2100 are (were) not leap years, while 2000 is (was).
Kalle_Ger. Thanks for the explanation and examples, which are helpful.
I tested hubert0’s script, and it seems a reasonable approach which hopefully will do the job.
As regards the examples in your post, I don’t know a foolproof method to do what you want and let me explain by using your example (g), which is “78/3/22”. This is a perfectly valid date if the user’s date preference is set to year/month/date. Thus, on my computer:
set theDate to "78/3/22"
set enteredDate to date theDate --> date "Wednesday, March 22, 1978 at 12:00:00 AM"
The script could check to see if the month and day are within an expected range (less than 32 for the day and less than 13 for the month), although that would not help with the date discussed above.
hubert0. I tested your script and selected 1980 for the year, January for the month, and 1 for the date, and the returned result was:
I tested other dates and the returned results were also not as expected. I think the unexpected result arises from the following line, which assumes a certain user date preference:
set chosenDate to date (chosenDay & " " & chosenMonth & " " & chosenYear)
If I change this line as follows the script works as expected on my computer (but not on computers with a different date preference):
set chosenDate to date (chosenYear & " " & chosenMonth & " " & chosenDay)
Thanks, Peavine. I think that’s down to different date and time preferences on our different systems. Mine are set to a standard UK date format and when I run my original script and choose 1980 - January - I get the correct date:
Here a development of a script originally written as an exercise to return any valid short date strings (ie. valid to the current user) that may be present in the input text. Any good to you?
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
on validShortDatesForMachine(theText)
-- Divine the short-date order set for the current user.
tell (current date) to set {its day, its month, (its year), testDate} to {1, 2, 2003, it}
set testString to testDate's short date string
set dmy to "dmy"
set shortDateOrder to {missing value, missing value, missing value}
repeat with i from 1 to 3
set item i of shortDateOrder to character ((testString's word i) mod 100) of dmy
end repeat
-- Use it to select an appropriate regex pattern.
if (shortDateOrder is {"d", "m", "y"}) then
-- Regex for valid (d)d/(m)m/(y)(y)(y)y short dates in years 1-9999 AD of the proleptic Gregorian calendar.
set regexPattern to "(?<![^\\s(–-])(?:(?:(?:0?[1-9]|1\\d|2[0-8])/(?:0?[1-9]|1[0-2])|(?:29|30)/(?:0?[13-9]|1[0-2])|31/(?:0?[13578]|1[02]))/\\d{0,3}(?:[1-9]|(?<!/0{2,3})0)|29/0?2/\\d{0,2}(?:[13579][26]|[02468]?(?:[48]|(?<!(?:[13579][048]?|/[02468]?[26])0)0)))\\b(?!/)"
else if (shortDateOrder is {"m", "d", "y"}) then
-- Regex for valid (m)m/(d)d/(y)(y)(y)y short dates ditto.
set regexPattern to "(?<![^\\s(–-])(?:(?:(?:0?[13-9]|1[0-2])/(?:0?[1-9]|[12]\\d|30)|0?2/(?:0?[1-9]|1\\d|2[0-8])|(?:0?[13578]|1[02])/31)/\\d{0,3}(?:[1-9]|(?<!/0{2,3})0)|0?2/29/\\d{0,2}(?:[13579][26]|[02468]?(?:[48]|(?<!(?:[13579][048]?|/[02468]?[26])0)0)))\\b(?!/)"
else -- if (shortDateOrder is {"y", "m", "d"}) then
-- Regex for valid (y)(y)(y)y/(m)m/(d)d short dates ditto.
set regexPattern to "(?<![^\\s(–-])(?:\\d{0,3}(?:[1-9]|(?<!\\b0{2,3})0)/(?:(?:0?[13-9]|1[0-2])/(?:0?[1-9]|[12]\\d|30)|0?2/(?:0?[1-9]|1\\d|2[0-8])|(?:0?[13578]|1[02])/31)|\\d{0,2}(?:[13579][26]|[02468]?(?:[48]|(?<!(?:[13579][048]?|\\b[02468]?[26])0)0))/0?2/29)\\b(?!/)"
end if
set shortDateRegex to current application's class "NSRegularExpression"'s regularExpressionWithPattern:(regexPattern) options:(0) |error|:(missing value)
-- Locate any and all substrings of the text which match the pattern (are valid short-date strings for the user).
set theText to current application's class "NSString"'s stringWithString:theText
set shortDateMatches to shortDateRegex's matchesInString:(theText) options:(0) range:({0, theText's |length|()})
-- Extract them from the text as an array of NSStrings.
set dateStrings to current application's class "NSMutableArray"'s new()
repeat with thisMatch in shortDateMatches
tell dateStrings to addObject:(shortDateRegex's replacementStringForResult:(thisMatch) inString:(theText) |offset|:(0) template:("$0"))
end repeat
-- Return as a list of AS texts.
return dateStrings as list
end validShortDatesForMachine
-- Original test text. All valid substrings returned as a list.
-- set testText to "4079/31/12/2018/X, 31/12/2018, 1/1/19, 31/11/18 (only 30 days in November), 29/02/2200 (2200 not a leap year), (29/02/1952-29/2/9996), 4/3/1, 17/4/541-01/01/00, 25/12/0000 (no year 0)."
-- Single short-date string parameter.
set testText to "1/3/22"
set testResult to validShortDatesForMachine(testText)
if (testResult = {}) then error testText & " is not a valid date on this machine."
return date string of date testText -- or date string of date (item 1 of testResult)
hubert0. I wonder if your script could be made universal by adding the following midpoint in the script:
repeat with i from 1 to (count allmonths) -- convert month to number
if item i of allmonths = chosenMonth then
set chosenMonthInteger to i as text
exit repeat
end if
end repeat
Then replace the last line of the script with:
set chosenDate to current date -- create date object
set year of chosenDate to chosenYear
set month of chosenDate to chosenMonthInteger
set day of chosenDate to chosenDay
return chosenDate
There’s probably a more elegant way of doing this, but In limited testing it does appear to work.
Your script works well on my machine, although it appends the time the script is run, rather than 00:00:00, to the returned date.
One could change this if required:
set chosenDate to current date -- create date object
set year of chosenDate to chosenYear
set month of chosenDate to chosenMonthInteger
set day of chosenDate to chosenDay
set time of chosenDate to 0
return chosenDate
The tried & tested method for setting date properties is to use an actual date object. This will automagically conform to the user’s settings:
set d to current date
set chosenYear to 1954
set chosenMonth to 5 -- or ENGLISH AppleScript constant 'May'
set chosenDay to 13
tell d
set its day to 1 -- overflow precaution
set its year to chosenYear
set its month to chosenMonth
set its day to chosenDay
end tell
But, imho, the real problem (which hasn’t been discussed at all) is the way Kalle_Ger gets the data from the user. Users are notoriously unreliable…
In post 1 Kalle_Ger appears to suggest two possible alternatives. One is to check the user input to insure it contains a valid year, month, and day. As Hubert0’s notes, his script accomplishes this by only allowing the user to select valid dates. I tested Nigel’s suggestion and it doesn’t appear to accomplish what Kalle_Ger wants. For example, I have my computer’s date format set to m/d/y, and with testText set to “78/3/1”, Nigels script returns “Friday, June 3, 7”. However, Nigel’s script could be modified to insure that the user input date falls within a valid range (a month of 78 would obviously fail).
As an alternative Kalle_Ger suggests calling the Calendar app to obtain the desired date information, which would avoid what he/she terms a “plausibility check”. This alternative hasn’t been discussed, although in a broad sense this is what hubert0’s script does (i.e. avoid the plausibility check). Apparently Shane wrote a date-picker script which works with the Calendar app, but I haven’t found a copy of Shane’s actual script or attempted to write one myself.
Ah. My apologies. [Blush.] The first two sections of the if … else if statement were both testing for d/m/y. The second should be for m/d/y. I’ve now corrected it.
I tested Nigel’s script against dates (b) thru (g) shown in post 3 and the results were all as expected. The test results demonstrate the impact of the user’s date System Preference on the results:
System Preference date set to m/d/y - e, f, g not valid
System Preference date set to d/m/y - c, d, e, g not valid
System Preference date set to y/m/d - c, d, e not valid
BTW, the script can return an incorrect result if the user’s date System Preference is not supported (e.g. d/y/m). In this unlikely event, the if statement can be modified to support that date format or, more easily, to report an error.
So, Kalle_Ger has two great options to select from.