How to validate a date text string with flexible formats?

Hello.

I think the date delimiters for a non C locale gets the delimites from the C-like locale added, as “-” and “/” works fine for LC_ALL = no_NO.UTF-8.

Either non Us/English gets more, or some locales do! :slight_smile:

. Or was it “:” that worked?

Well, instead of date(DueDate), one could also check for ([0]1 to 31)(valid delimiter)([0]1 to 12)(valid delimiter)(YY or YYYY). That however leads to new problems:

  • local differences in the date syntax order (MM-DD vs. DD-MM) based on System Preferences > Language & Text > Formats (‘date(DueDate)’ captures that pretty well)
  • how many days a month has, incl. differences in leap years (again: ‘date(DueDate)’ captures that)

I’d stick with date(DueDate) for now, even if it is not 100% failsafe. (One could later further catch errors with a 5th test: MM-DD, where MM must not be > 12 and DD must not be > 31, whereas the question is how to ask the Mac for its local settings on the date format, MM-DD vs. DD-MM).

But, let’s return to my initial questions for now:

  • How to combine multiple try criterias in one try block? (I’m now considering nested try blocks)
  • How to try #2, #3 and #4? How to code that? Using AS’s ‘contains’? Using a Shell call?

Hello.

First of all. you know when the specified date is wrong, when setting the date by your datestring fails.

Nigel Garvey has put an univarsal date handler into code-echange that cures all for your part.

Nested try blocks and multiple try’s should both be avoided. You can however test for several condition under an “on error statement.” Not nested because a new stack is pulled up, and the code is executed within that one, before you start executing from your normal stack when you are don with the try catch, so any values you give variables there may be wiped out when you return to normal execution.

I normally code multiple tries sequentially. Setting success to false if something went wrong, then I pass by the remaining try blocks. 5 try blocks on a row, the 4 last of them embededd in if-tests.

Maybe you should read a little bit in the Apple Script Language Guide? Short and well written.

Where an error means “this test has failed”, subsequent tests have to be in the "on error’ part of the ‘try’ statement.

try
	date theDueDate
	set ValidDueDate to true
on error
	-- Other tests here, setting ValidDueDate to true or false as appropriate.
end try

Here’s an another approach I’ve been working on this afternoon:

set theDueDate to "2w"

if ((do shell script ("<<<" & quoted form of theDueDate & " sed -E '/^(0?[1-9]|[12][0-9]|3[01])[/-](0?[1-9]|1[0-2])[/-](20)?[0-9]{2}$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean) then
	-- If theDueDate is a valid date string in any of the allowed formats, test to see if it represents a valid date.
	set {d, m, y} to theDueDate's words
	set {d, m} to {d as integer, m as integer}
	set ValidDueDate to (d < 29) or (m is in {1, 3, 5, 7, 8, 10, 12}) or ((d < 31) and (m is in {4, 6, 9, 11})) or ((d is 29) and (m is 2) and (y mod 4 is 0) and (y mod 400 is not in {100, 200, 300}))
else
	-- Otherwise test for any of the alternative valid inputs.
	set ValidDueDate to (do shell script ("<<<" & quoted form of theDueDate & " sed -E '/^([-+]?[1-9][0-9]*[dw]|to(day|morrow))$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean
end if

A “valid date string” here contains slash and/or hyphen delimiters, the first part is a number between 1 and 31 (with/without a leading zero), the second part is a number between 1 and 12 (ditto), and the third is either a number between 0 and 99 or a number between 2000 and 2099. The number range thus limited, the rest of the applescript code above the ‘else’ line checks that the date itself is valid.

An “alternative valid input” optionally begins with “+” or “-”, then a number not equal to 0 and with no leading zeros,with any number of digits, then either “d” or “w”. Or it can be “today” or “tomorrow”.

:smiley:

I can’t wait to try that, there is only one more test to be had, and that is of course if duedate is not before today.

It is quite simple to perform a today (now()) as date object as seconds +86400 - due date as dateobject as seconds shouldn’t be negative.

You do amaze me Nigel.

Here we go … into the right direction! :stuck_out_tongue: I’ll give this a try. Perhaps you can describe the syntax of the individual lines a bit in depth. The shell script lines are a miracle to me, because I don’t know the commands and the syntax).

One more thing:

Does this script accept the various regional date formats? Probably not…

Here’s the problem: In Germany it is DD.MM.YY(YY) whereas the typical delimiter is “.”, but the Mac (including the AS date cmd) also accepts “-” and “/”. In the US it is MM-DD-YY(YY), whereas the delimiter is “-” or “/” (but what about “.”?).

Of course it is impossible to tell from “04-06-13” whether that is German format for June or US format for April. Again: this depends on the settings in System Preferences > Language & Text > Formats. See here. Note that my script in a later step uses the AS date cmd to parse the actual date from the tested string.

Two solutions:

a) We say it’s the users responsibility, to use the DD-MM or MM-DD order strictly according to that System Preference. The test really doesn’t have to take care of this. In that case however, we must accept any number up to [31], both for Days and Months, not knowing which is which.

b) The ultimate challenge: the test code snippet figures out the local System Prefs Formats setting and depending on that tests for either [±]MM[-/]DD[-/]YY(YY) OR [±]DD[.-/]MM[.-/]YY(YY).


--This is how to call the Pane (the responsible setting(s) is/are under the 3rd tab) 
-- Any idea how to read them out?
tell application "System Preferences"
	activate
	set the current pane to pane id "com.apple.Localization"
end tell

On the other hand: perhaps the current date format setting can be figured out through a shell cmd as well!?

Nope. Of course the DueDate can also be before today. That’s what is simply called ‘overdue’. That’s why I want the script to accept ‘-14d’ (so 14 days ago), and dates 2 months ago (e.g. 11/30/2012).

Uhm, well… It seems that Nigel’s code actually DOES take this into account. With my german setting (DD-MM-YYYY) the date “31/12/2013” returns true, whereas “12/31/2013” leads to false. Excellent!

The only things missing now is the acceptance of “.” as date delimiter. I’ve changed all occurrences of [/-] to [/-.], but that doesn’t work. Why?

The shell scripts use ‘sed’ to edit the input, which is assumed to be just one line. The first one tests for a date string of the form described in the paragraph beneath the script. If the copy of the line in that script doesn’t match the date string format, it’s changed to “false”. Then, if it hasn’t been changed to “false”, it’s changed to “true”. The “true” or “false” result is coerced to a boolean after the return from the shell script and the boolean is used to control the AppleScript ‘if’ statement.

If the boolean is ‘true’, the date string is further analysed to see if it represents a valid calendar date. The shell script has only allowed the date string if the first part represents a number between 1 and 31, the second a number between 1 and 12, and the third a number between (20)00 and (20)09. The date’s therefore valid if the day’s less than 29, or the month has 31 days, or the day is less than 31 and the month has 30 days, or the day is 29 and the month is February and it’s a leap year.

If the boolean from the first shell script is ‘false’, the second shell script is executed to see if the input matches any of your other allowed formats. It performs the same “true” or “false” substitution as the first shell script.

Only the formats you specified in post #1. It also assumes a day-month-year short-date order, which I deduced to be the one you use. The script can be expanded to allow other separators, but needs changes to both the sed and AppleScript codes. Testing the local short-date order can be done either with AppleScript or (I think) a shell script. However, it’s 01:00 where I am as I write…

It’s 02:12 over here (Frankfurt, Germany) :smiley:

Oops… Stupid me, didn’t notice that my german settings (DD-MM-YYYY) of course must lead to true and others to false with your script. But as I wrote in post #9: testing the syntax as text, rather than using the date cmd leads to the localization problem, which means that

a) the code snippet either has to figure out the System Preferences or
b) we have to establish a changeable date format preference within the script, depending on which the code snippet test either for one format (DD-MM-YYYY) or the other (MM-DD-YYYY).

Needless to say that I’ll mention you in the credits of the script (a pretty slick, full-featured Mail2Things script, which turns incoming emails into To Dos in Cultured Code’s Things). :wink:

On figuring out the System Preferences > Language & Text > Formats settings: Guess this should be the AppleScript relevant cmd, and this the one for the shell.

The cumbersome part now is to cover the various possible returned values… Anyone got a table showing all return values with their associated date formats at hand? Just saw that South Africa has YYYY-MM-DD … Holy cow! Shell> locale -a at least outputs a list with all possible locales.

OK. This version takes the local short-date order into account and can handle “.” separators (or any others you may wish to add) as well. I’ve changed the “year” regex to allow any four-digit year between 1000 and 9999 as well as two-digit years as before. Since this script uses simultaneous multiple delimiters, it needs to be run in Snow Leopard or later. (Or else the user must be forced to use the first of the allowed separators when entering a short date!)

on validateDateInput(theDueDate)
	-- A string of the short-date separators to be recognised. (Edit to taste.)
	-- These will be used in a regex class, so the hyphen must be first or last.
	set allowedSeparators to "-/."
	-- A list of short-date part regexes, in day, month, year order.
	set datePartRegices to {"(0?[1-9]|[12][0-9]|3[01])", "(0?[1-9]|1[0-2])", "([1-9][0-9])?[0-9]{2}"}
	
	-- Get the local short-date string for 1st February 4003, strip out everything except the "1", the "2", and the "3", and turn these into a 3-digit integer. Use the digits to index the short-date part regexes and to arrange them into the equivalent order in a full short-date regex.
	set order to (do shell script ("<<<" & quoted form of short date string of («data isot343030332D30322D3031» as date) & " sed -E 's/[^123]//g'")) as integer
	set separatorClass to "[" & allowedSeparators & "]"
	tell datePartRegices to set shortDateRegex to item (order div 100) & separatorClass & item (order mod 100 div 10) & separatorClass & item (order mod 10)
	
	if ((do shell script ("<<<" & quoted form of theDueDate & " sed -E '/^" & shortDateRegex & "$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean) then
		-- If theDueDate is a valid date string in any of the allowed formats, test to see if it represents a valid date.
		set astid to AppleScript's text item delimiters
		set AppleScript's text item delimiters to characters of allowedSeparators -- Requires Snow Leopard or later.
		tell theDueDate's text items
			set {item (order div 100), item (order mod 100 div 10), item (order mod 10)} to {beginning as integer, item 2 as integer, end as integer}
			set {d, m, y} to it
		end tell
		set AppleScript's text item delimiters to astid
		
		set ValidDueDate to ((d < 29) or (m is in {1, 3, 5, 7, 8, 10, 12}) or ((d < 31) and (m > 2)) or ((d is 29) and (y mod 4 is 0) and (y mod 400 is not in {100, 200, 300})))
	else
		-- Otherwise test for any of the alternative valid inputs.
		set ValidDueDate to (do shell script ("<<<" & quoted form of theDueDate & " sed -E 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ ; /^([-+]?[1-9][0-9]*[dw]|(to(day|morrow)|yesterday))$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean
	end if
	
	return ValidDueDate
end validateDateInput

validateDateInput("31.01.13")

Edits: Made the "alternative input form’ parsing effectively case-insensitive and added “yesterday” to the possibilities. Fixed a bug in the date-order handling and made it more efficient. Made a couple of optimisations in the date verification logic.

Freaking awesome!! :smiley: Kudos to you!

Final issue (I promise ;)):

The script will support any language which the Things.app itself does support. Based on a language variable (en, de, es etc.; which currently is set manually in the script, but which I’ll now simply retrieve from the system), the various text strings are set into the appropriate language, like shown here:


		if myLanguage is "de" then
			set theDueDateWords to {"Fällig: ", "vorgestern", "gestern", "Heute", "Morgen", "Übermorgen", "seit", "in", "Tagen"}
		else if myLanguage is "fr" then
			set theDueDateWords to {"Échéance: ", "avant-hier", "hier", "Aujourd'hui", "Demain", "Après-demain", "depuis", "dans", "jours"}
		else if myLanguage is "es" then
			set theDueDateWords to {"Vencimiento: ", "anteayer", "ayer", "Hoy", "Mañana", "Pasado mañana", "desde", "dentro de", "días"}
		else
			-- "en" for all other languages
			set theDueDateWords to {"Due: ", "the day before yesterday", "yesterday", "Today", "Tomorrow", "The day after tomorrow", "since", "in", "days"}
		end if

That means that the validDate handler should not check for “today” and “tomorrow”, but item 4 and item 5 of the list theDueDateWords.

Furthermore, I’d like to check only for the first 3 characters of those words. So instead of insisting on “tomorrow”, the validDate handler should be content with “tom”. Why is that? Simply to cover typos: “tomorrow” is a pretty long word when typed on a Smartphone keyboard, so there is a certain risk for typos like “tomorrwo” or “tomorow”. By checking only for the first 3 characters, this risk is minimized.

Today and tomorrow is what I currently support, yesterday is OK too. Absolutely no need to acknowledge “the day before yesterday” and “the day after tomorrow”.

We’ll actually… I’m brainstorming here… Why should’t the validDate handler accept item 3, item 4 and item 5 of the theDueDateWords list (see above) in any of the languages supported by the script (en, de, es, fr)? If a spanish co-worker sends you an email which ends to “Ven(cimiento): Mañ(ana)” instead of “Due: Tom(orrow)”, it should be accepted as well, independent from your “locale” setting…

Consequently, the day and week identifiers should support the 4 languages as well

{"d", "t", "j"} -- for day/día, Tag, jour
{"w", "s"} -- for week/Woche, semain/semana

Of course it’s the co-worker’s responsibility to use your “locale” format on full dates (DD-MM-YYYY etc.).

NOTE: There are no interferences between the foreign language translations of Yesterday, Tomorrow and Today, so any first 3-characters of those words are truly unique, no need to worry from that side.

NOTE: validDate still has to care only care about the dueDate text string after "Due: " etc., I’m separating the "Due: ", "Ven(cimiento): " from the actual DueDate in an earlier step.

From the point of view of slotting these things into my script, it would be easier to have just one list containing different-language plug-ins for the regex in the last shell script. For English, the “plug-in” would be “[dw]|(to(day|morrow)|yesterday)” and you’d slot it into the shell script thus:

set ValidDueDate to (do shell script ("<<<" & quoted form of theDueDate & " sed -E 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ ; /^([-+]?[1-9][0-9]*" & plugin & ")$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean

The German plug-in would be “[tw]|(heute|morgen|gestern)”, the French “[js]|(aujourd’\''hui|demain|hier)”, and so on. But you’d need to add more characters to the case-conversion alphabets in the shell script to cover possibilties like “mañana”.

For three-letter matches only: “[dw]|(to[dm]|yes)[[:alpha:]]", "[tw]|(heu|mor|ges)[[:alpha:]]”, “[js]|(auj|dem|hie)[[:alpha:]‘\’']*”, etc.

All languages should be accepted in PARALLEL, guess I didn’t make that clear. I combined all languages now in one plugin variable. Here’s the final script. Pretty awesome! Thanks a zillion times for your support, Nigel!

on validateDateInput(theDueDate)
	-- A string of the short-date separators to be recognised. (Edit to taste.)
	-- These will be used in a regex class, so the hyphen must be first or last.
	set allowedSeparators to "-/."
	-- A list of short-date part regexes, in day, month, year order.
	set datePartRegices to {"(0?[1-9]|[12][0-9]|3[01])", "(0?[1-9]|1[0-2])", "([1-9][0-9])?[0-9]{2}"}
	set plugin to "[dw|tw|js|ds]|(yes|to[dm]|ges|heu|mor|hie|auj|dem|aye|hoy|man|mañ)[[:alpha:]]*"
	
	-- Get the local short-date string for 1st February 4003, strip out everything except the "1", the "2", and the "3", and turn these into a 3-digit integer. Use the digits to index the short-date part regexes and to arrange them into the equivalent order in a full short-date regex.
	set order to (do shell script ("<<<" & quoted form of short date string of («data isot343030332D30322D3031» as date) & " sed -E 's/[^123]//g'")) as integer
	set separatorClass to "[" & allowedSeparators & "]"
	tell datePartRegices to set shortDateRegex to item (order div 100) & separatorClass & item (order mod 100 div 10) & separatorClass & item (order mod 10)
	
	if ((do shell script ("<<<" & quoted form of theDueDate & " sed -E '/^" & shortDateRegex & "$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean) then
		-- If theDueDate is a valid date string in any of the allowed formats, test to see if it represents a valid date.
		set astid to AppleScript's text item delimiters
		set AppleScript's text item delimiters to characters of allowedSeparators -- Requires Snow Leopard or later.
		tell theDueDate's text items
			set {item (order div 100), item (order mod 100 div 10), item (order mod 10)} to {beginning as integer, item 2 as integer, end as integer}
			set {d, m, y} to it
		end tell
		set AppleScript's text item delimiters to astid
		
		set ValidDueDate to ((d < 29) or (m is in {1, 3, 5, 7, 8, 10, 12}) or ((d < 31) and (m is in {4, 6, 9, 11})) or ((d is 29) and (m is 2) and (y mod 4 is 0) and (y mod 400 is not in {100, 200, 300})))
	else
		-- Otherwise test for any of the alternative valid inputs.
		set ValidDueDate to (do shell script ("<<<" & quoted form of theDueDate & " sed -E 'y/ABCDEFGHIJKLMNÑOPQRSTUVWXYZ/abcdefghijklmnñopqrstuvwxyz/ ; /^([-+]?[1-9][0-9]*" & plugin & ")$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean
	end if
	
	return ValidDueDate
end validateDateInput

validateDateInput("mañ")

Glad you’ve got what you needed. :slight_smile:

This optimisation of the above logic seems to work OK:

set ValidDueDate to ((d < 29) or (m is in {1, 3, 5, 7, 8, 10, 12}) or ((d < 31) and (m > 2)) or ((d is 29) and (y mod 4 is 0) and (y mod 400 is not in {100, 200, 300})))

PS.

Because you’ve got those bars in the class at the beginning of the regex, a bar will be accepted as valid in the input, as in “2|”. Although it’s not as clear, the class should probably be shortened to “[dwtjsd]”.

As I have multiple occurrences of those indicators within the script, my idea is to define your plugin string early in the script once, like shown here:


set theYesterdayIndicators to {"yes", "ges", "hie", "aye"} -- for yesterday, gestern, hier, ayer
set theTodayIndicators to {"tod", "heu", "auj", "hoy"} -- for today, heute, aujourd'hui, hoy
set theTomorrowIndicators to {"tom", "mor", "dem", "mañ", "man"} -- for tomorrow, morgen, demain, mañana, manana
set theDayIndicators to {"d", "t", "j"} -- for day/día, Tag, jour
set theWeekIndicators to {"w", "s"} -- for week/Woche, semain/semana

set text item delimiters to ""
set validateDayWeekIndicator to (theDayIndicators as string) & theWeekIndicators as string
set text item delimiters to "|"
set validateYesTodTomIndicator to ((theYesterdayIndicators as string) & "|" & theTodayIndicators as string) & "|" & theTomorrowIndicators as string
set plugin to "[" & validateDayWeekIndicator & "]|(" & validateYesTodTomIndicator & ")[[:alpha:]]*"
display dialog plugin buttons {"Looks exactly as it should"}

on validateDateInput(aDate)
	-- A string of the short-date separators to be recognised.
	-- These will be used in a regex class, so the hyphen must be first or last.
	set allowedSeparators to "-/."
	-- A list of short-date part regexes, in day, month, year order.
	set datePartRegices to {"(0?[1-9]|[12][0-9]|3[01])", "(0?[1-9]|1[0-2])", "([1-9][0-9])?[0-9]{2}"}
	
	-- Get the local short-date string for 1st February 4003, strip out everything except the "1", the "2", and the "3", and turn these into a 3-digit integer. Use the digits to index the short-date part regexes and to arrange them into the equivalent order in a full short-date regex.
	set order to (do shell script ("<<<" & quoted form of short date string of («data isot343030332D30322D3031» as date) & " sed -E 's/[^123]//g'")) as integer
	set separatorClass to "[" & allowedSeparators & "]"
	tell datePartRegices to set shortDateRegex to item (order div 100) & separatorClass & item (order mod 100 div 10) & separatorClass & item (order mod 10)
	
	if ((do shell script ("<<<" & quoted form of aDate & " sed -E '/^" & shortDateRegex & "$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean) then
		-- If aDate is a valid date string in any of the allowed formats, test to see if it represents a valid date.
		set astid to AppleScript's text item delimiters
		set AppleScript's text item delimiters to characters of allowedSeparators -- Requires Snow Leopard or later.
		tell aDate's text items
			set {item (order div 100), item (order mod 100 div 10), item (order mod 10)} to {beginning as integer, item 2 as integer, end as integer}
			set {d, m, y} to it
		end tell
		set AppleScript's text item delimiters to astid
		
		set ValidDate to ((d < 29) or (m is in {1, 3, 5, 7, 8, 10, 12}) or ((d < 31) and (m > 2)) or ((d is 29) and (y mod 4 is 0) and (y mod 400 is not in {100, 200, 300})))
	else
		-- Otherwise test for any of the alternative valid inputs. (It might be necessary to add foreign characters here - both lower and upper case - when adding further languages)
		set ValidDate to (do shell script ("<<<" & quoted form of aDate & " sed -E 'y/ABCDEFGHIJKLMNÑOPQRSTUVWXYZ/abcdefghijklmnñopqrstuvwxyz/ ; /^([-+]?[1-9][0-9]*" & plugin & ")$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean
	end if
	
	return ValidDate
end validateDateInput

set theDueDate to "2d"
if validateDateInput(theDueDate) of me is true then
	say "true"
else
	say "false"
end if

set plugin returns a string which is absolutely identical to “[dwtjs]|(yes|to[dm]|ges|heu|mor|hie|auj|dem|aye|hoy|man|mañ)[[:alpha:]]*”, the script however ends with an error, claiming plugin hasn’t been defined (number -2753 from “plugin”). What am I doing wrong?

UPDATE: Guess I just solved the problem: → of me was missing:

set ValidDate to (do shell script ("<<<" & quoted form of aDate & " sed -E 'y/ABCDEFGHIJKLMNÑOPQRSTUVWXYZ/abcdefghijklmnñopqrstuvwxyz/ ; /^([-+]?[1-9][0-9]*" & plugin of me & ")$/ !s/.*/false/; /false/ !s/.+/true/ ;'")) as boolean

Yes. It’s a “scoping” issue. Variables in handlers are local unless declared otherwise, so the ‘plugin’ in the ‘validateDateInput’ handler isn’t the same ‘plugin’ as the one at the top of the script in the “implicit run handler”.

Locals are temporary, exist only while the handler in which they occur is executing, and can only be accessed within that handler.

Globals, properties, and run-handler variables are permanent and “belong” to the script. If the script’s run directly from a file, the values of its globals, properties, and/or run-handler variables are saved back to the file when it exits

If you declare a variable global at the top of the script, it’ll be the same variable throughout the script (except in handlers where you’ve explicitly declared it local).

global plugin

set theYesterdayIndicators to .
etc.

If you instead make the declaration at the top of a handler, it’ll be the same variable as the one in the run handler (if there is such a variable there) and in any other handlers where it’s explicitly declared global.

Your ‘of me’ solution sets up a reference to the ‘plugin’ belonging to the script, which in this case is the one set in the implicit run handler.

The polite way to do things is to pass the string to the handler as an additional parameter.

validateDateInput(theDueDate, plugin)

on validateDateInput(aDate, plugin)

	-- Use 'plugin', not 'my plugin' here.

end validateDateInput

The two ‘plugin’ variables here contain the same value, but are different variables.

It’s considered very bad form to intersperse run-handler code with handler definitions, so your last six lines should be with the code at the top of the script (or it with them). However, I imagine they’ve just been added temporarily to test the rest of the script.

Pardon me, if I ever use the wrong terminology (I’m a Kraut, not a native speaker :P)…

IMO the use of parameters with handlers are only meaningful, if the handler is used in multiple instances within the script, and/or with changing/variable parameters. In my case the plugin parameter doesn’t change throughout the script (and couldn’t be replaced within anything else in the handler), which is why of me simply looks better. Do you agree? Or does it make any difference in performance?

Indeed only for testing purposes…

My script contains a bunch of handlers, which are all placed in a HANDLERS section at the very bottom of the script. What’s your opinion: In terms of script performance, should the order of the various handlers within that section match the order in which the handlers are called, or doesn’t this matter?

Hi.

I’ve posted a combined validation and interpretation script in your other thread.