Rule not calculating $PatternCode from $BirthDate — need help with Tinderbox date handling

Hi all,

I’m trying to calculate a custom $PatternCode from a note’s $BirthDate, using a Rule defined in a prototype (pPerson). The logic is:

  1. Sum the digits of the birth year and reduce to a single digit.
  2. Sum the digits of the birth month and day and reduce to a single digit.
  3. Sum (1) and (2) and reduce to a single digit.
  4. Combine all three digits into a string (e.g. 977).

Here’s the Rule I’m currently using:

if(!isEmpty($BirthDate)) {
  $YearString = year($BirthDate).format("0000");
  $Y1 = $YearString.at(0).asNumber;
  $Y2 = $YearString.at(1).asNumber;
  $Y3 = $YearString.at(2).asNumber;
  $Y4 = $YearString.at(3).asNumber;
  $YearSum = $Y1 + $Y2 + $Y3 + $Y4;
  if($YearSum > 9) {
    $YearSum = $YearSum.format("00").at(0).asNumber + $YearSum.format("00").at(1).asNumber;
  }
  $FirstDigit = $YearSum;

  $MonthString = month($BirthDate).format("00");
  $DayString = day($BirthDate).format("00");
  $MD1 = $MonthString.at(0).asNumber;
  $MD2 = $MonthString.at(1).asNumber;
  $DD1 = $DayString.at(0).asNumber;
  $DD2 = $DayString.at(1).asNumber;
  $MonthDaySum = $MD1 + $MD2 + $DD1 + $DD2;
  $SecondDigit = $MonthDaySum;

  $ThirdSum = $FirstDigit + $SecondDigit;
  if($ThirdSum > 9) {
    $ThirdSum = $ThirdSum.format("00").at(0).asNumber + $ThirdSum.format("00").at(1).asNumber;
  }
  $ThirdDigit = $ThirdSum;

  $PatternCode = $FirstDigit.format("0") + $SecondDigit.format("0") + $ThirdDigit.format("0");
}

Context:
This is for a pattern analysis system that will process thousands of public figures’ birth dates, so manual calculation isn’t feasible. I need the Rule to automatically calculate the PatternCode from any $BirthDate input.

Current status:

Simple string concatenation works fine (like setting $PatternCode = “999” or combining digits directly), but the digit extraction from date parts appears to fail silently — even though $BirthDate is correctly stored and formatted in the note as a date.

I suspect either .format() or .at().asNumber is behaving differently than expected, or possibly there’s a quirk in Rule execution order or data typing.

Has anyone successfully extracted and summed digits from parts of a date like this in Tinderbox? Any better method to do digit-level math from year/month/day values?

Oh, I am using Tinderbox Version 9.6.1 (b638)

Thanks for any help!
Eric

Some observations. Change “asNumber” to “toNumber”. I don´t think the at-operator works on Strings. If you instead use substr(x,y), where x is the position of the first wanted character and y is the number of characters to get , like this
$Y1 = $YearString.substr(0,1).toNumber;
This might work

It’s not % ccertain from the description, but the ‘pattern code’ is actually a String that happens to be composed of three digital characters. Each character in the code has a different meaning and the output is not intended for calculation use? If so, careful attention needs to be paid to explicit or implicit type conversions.

Also, is `isEmpty a custom function and what data type is $BirthDate? If $BirthDate is a Date type attribute, then checking if it has a value is simple:

if($Birthdate){...
// or in longer form, the same as
if($BirthDate!="never?){

The attributes $Y1, etc, seem to be place holders and general practice now would be to variables as there is then no cleaning up as saved process data at the end.

There is no .asNumber action code operator. Have part solution. But, meetings now. Should have a solution later today. As guessed, your data type parsing is off.

OK, I have a solution but I think but exposes a flaw in your design logic:

  $ThirdSum = $FirstDigit + $SecondDigit;
  if($ThirdSum > 9) {
    $ThirdSum = $ThirdSum.format("00").at(0).asNumber + $ThirdSum.format("00").at(1).asNumber;
  }

The aim seems to reduce a $ThirdSum of 10 or more to a one-digit figure. So, 15 is turned into 1 and 5 then added such that 15 becomes 61. But for a start value like 19gives1+9i.e.10` so two digits.

This is my current code’s results (note that I use dd/mm/yyyy dates), showing in test #3 that the pattern code has expanded to 4 characters:

FWIW, this is my current code below. There may be a simpler version later but for now I’ve tried to remove all the ‘interstitial’ attributes to stay close to your sample code.

if($BirthDate){
	var:string vYear = $BirthDate.year.format(0,4,"0"); // num -> str
	var:number vYr1 = vYear.substr(0,1); // var type auto-coerces str to num
	var:number vYr2 = vYear.substr(1,1);
	var:number vYr3 = vYear.substr(2,1);
	var:number vYr4 = vYear.substr(3,1);
	var:number vYearSum = (vYr1 + vYr2 + vYr3 + vYr4);

	if(vYearSum > 9) { // assumes a 2-digit number
  		var:number vYearSum2 = mod(vYearSum,10);
 		var:number vYearSum1 = (vYearSum - vYearSum2)/10;
		vYearSum = vYearSum1 + vYearSum2;
  }
  var vFirstDigit = vYearSum; // assumes a 1-digit number

  var:string vMonth = $BirthDate.month.format(0,2,"0"); // num -> str
  var:string vDay = $BirthDate.day.format(0,2,"0"); // num -> str
  var:number vM1 = vMonth.substr(0,1); // var type auto-coerces str to num
  var:number vM2 = vMonth.substr(1,1);
  var:number vD1 = vDay.substr(0,1);
  var:number vD2 = Dvay.substr(1,1);
  var:number vMDSum = (vM1 + vM2 + vD1 + vD2);
  var:number vSecondDigit = vMDSum;

  var:number vThirdSum = vFirstDigit + vSecondDigit;

  if(vThirdSum > 9) { // assumes a 2-digit number
  		var:number vThirdSum2 = mod(vThirdSum,10);
 		var:number vThirdSum1 = (vThirdSum - vThirdSum2)/10;
		vThirdSum = vThirdSum1 + vThirdSum2;
  }
  var:number vThirdDigit = vThirdSum; // assumes a 1-digit number

  $PatternCode = vFirstDigit.format(0,1) + vSecondDigit.format(0,1)+ vThirdDigit.format(0,1);
}

I think it’s worth getting the pattern code logic working consistently before delving further.

1 Like

Doing the above away from the office and missed noting a point re string->number. There is no String,asNumber but there is a String.toNumber. See String.toNumber(). This would have been more using in my code.

An action code point to be aware of is the result of adding/joining disparate data types and assumptions as the result. IOW is "6" + 7 the same as 6 + "7"? As users, we tend to assume the logic will accord with out intent and often thus fail to check first.

The .at(N) or [N] suffix notation is used for lists, so doing String.at(2) to get the second character for String involves an implicit String-type–to-List-type parsing. A String-native method would be String.substr(2,1).

This doesn’t fix the buggy logic for getting the year sum to be less than 10 (and for month/day sums) but includes us of .toNumber for more explicit type declaration and adds more comments:

if($BirthDate){
	// year returns a number so .format() to a string,
	// e.g. year 45 CE is saved as string '0045'
	var:string vYear = $BirthDate.year.format(0,4,"0"); // number -> string
	var:number vYr1 = vYear.substr(0,1).toNumber; // string -> number
	var:number vYr2 = vYear.substr(1,1).toNumber;
	var:number vYr3 = vYear.substr(2,1).toNumber;
	var:number vYr4 = vYear.substr(3,1).toNumber;
	var:number vYearSum = (vYr1 + vYr2 + vYr3 + vYr4); // sum up  numbers

	if(vYearSum > 9) { // assumes vYearSum is a 2-digit number
		var:number vYearSum2 = mod(vYearSum,10); //e.g. 13 -> 3
		var:number vYearSum1 = (vYearSum - vYearSum2)/10; // e.g. 13-3 > 10/10 > 1
		// above avoids going type number->string->number
		// add two numbers
		vYearSum = vYearSum1 + vYearSum2;
	};
	var vFirstDigit = vYearSum; // assumes now a 1-digit number

	var:string vMonth = $BirthDate.month.format(0,2,"0"); // number -> string
	var:string vDay = $BirthDate.day.format(0,2,"0"); // number -> string
	var:number vM1 = vMonth.substr(0,1).toNumber; // string -> number
	var:number vM2 = vMonth.substr(1,1).toNumber;
	var:number vD1 = vDay.substr(0,1).toNumber;
	var:number vD2 = Dvay.substr(1,1).toNumber;
	var:number vSecondDigit = (vM1 + vM2 + vD1 + vD2); // sum up  numbers

	var:number vThirdSum = vFirstDigit + vSecondDigit;

	if(vThirdSum > 9) { // assumes a 2-digit number
		var:number vThirdSum2 = mod(vThirdSum,10);
		var:number vThirdSum1 = (vThirdSum - vThirdSum2)/10;
		vThirdSum = vThirdSum1 + vThirdSum2;
	};
	var:number vThirdDigit = vThirdSum; // assumes now a 1-digit number
	// to make patter concatenate 3 1-char strinfs, not 3 numbers
	$PatternCode = vFirstDigit.format(0,1) + vSecondDigit.format(0,1)+ vThirdDigit.format(0,1);
}

Above in a TBX, code in stamp “Set PatternCode”: pattern-code.tbx (234.1 KB)

Thanks Gunnar — I really appreciate the hint. You’re absolutely right about toNumber, and I hadn’t realized substr() would be the better approach here. Your tip helps move things forward. Thanks again!

Thanks so much, Mark — that’s very kind of you. I’ll test it out and report back.

The logic behind this methodology is that every component of a date ultimately reduces to a fundamental digit between 1 and 9 — excluding zero, since it’s not considered a root digit in this framework (historically it came later). The result is a 3-digit pattern code that reflects hidden rhythms or archetypal traits derived from birth dates.

Manually, I’ve applied this to hundreds of public figures as part of a larger analysis system, and from that data I’m seeing some intriguing clusters and recurrences. For instance:
• Michael Jordan: 17 Feb 1963 → 112
• Oprah Winfrey: 29 Jan 1954 → 134
• Elon Musk: 28 June 1971 → 977
• Taylor Swift: 13 Dec 1989 → 977

I’m now trying to scale this up — hence the attempt to automate the calculation in Tinderbox based on any $BirthDate input. Your point about data type parsing makes total sense, and I’ll clean up the use of attributes vs variables as you suggested.

Thanks again for helping me troubleshoot this — and for all the care you put into your responses!

1 Like

Editing a typo in a previous post, I’d expand on this.

An action code point to be aware of is the result of adding/joining disparate data types and assumptions as the result. IOW is "6" + 7 the same as 6 + "7"?

My recollection is the result of the two context above is 67 for the first and 13 for the second. The reason is the type of the first item sets the output. So:

$MyNumber = "6" + 7;

$MyNumber is 67. Why? The number 7 is coerced to a string so it can be concatenated with a a string, so “6” + “7” gives strong "67". As the result only contains digits, it can be coerced into Number type and stored in $MyNumber as 67.

$MyNumber = "6".toNumber + 7;

gives a $MyNumber value of 13. Why? Now, the string "6" is coerced to a Number-type 6 by the chained .toNumber (see String.toNumber()). Now, the + is adding two Numbers, and 6 + 7 = 13.

You can save a number directly into a String attribute or variable or vice versa: MyNumber = "12" stores a Number value 12 whilst $MyString = 12 stored the string 12. But beware implicit type coercion in the evaluation of the expression to the right of the = , e.g. addition vs. concatenation, happening before the result is stored.

To explicitly turn a number onto a string, use Number.format(decimalsNum[, widthNum, padStr]|formatStr).

The logic bug is where the year or month/day data may result in a two digit output. Thus in my earlier test we see 24/09/1972 (24 Sep) gives 1067 from the results 10, 6, and 7. This is because whilst a number over 9 like 15 will sum down to 6, 19 sums down to 10. So you need to look out for results, like 19, 28, 29, 37, 38, 39.

Of these I think only 19 and 28 will occur (year 1999 in your logic sums down to 28) and most likely in the year element only. I think the largest day+month initial sum is 29 Sep (29/9 → 20). So for initial split-and-sum outcomes If think you need to check for two special results: 19 and 28. You then need to decide how to resolve those to one digit. a starting point might be to use zero for both, until you hit on a better fix.

Also spotted a glitch (typo in code by me) in my demo file and 24 Sep 1972 returns ‘10102’ - so that’s an overflow on year and day+month count.

Whilst I can’t fully address the flaw in the underlying logic, this update assumes that sums returning a value that re-sums to a value over 9 (e.g. 19 → 10) are set to an output of zero (0).

The code doing this is now factored out into 3 functions (fYearSum(),fDayMonthSum(), and fCheckSum()) such that the pattern-setting code (in stamp “Set PatternCode”) is:

if($BirthDate){

	var:number vYearSum = fYearSum($BirthDate.year);
	var:number vFirstDigit = fCheckSum(vYearSum); // assumes output now a 1-digit number
	var:number vDayMonth = fDayMonthSum($BirthDate.day,$BirthDate.month);
	var:number vSecondDigit = fCheckSum(vDayMonth); // assumes output now a 1-digit number
	var:number vThirdDigit = fCheckSum(vFirstDigit + vSecondDigit);
	// assumes all 'digits' vars are now  1-digit numbers
	// to make pattern concatenate 3 1-char strinfs, not 3 numbers
	$PatternCode = vFirstDigit.format(0) + vSecondDigit.format(0) + vThirdDigit.format(0);
}

N.B. Code in the TBX (below) includes logging calls to report digits 1/2/3 for debugging purposes)

I think you now have a cleaner model. It’s for you to resolve go to deal with sums that re-sum to >10. Here the inputs at least (mis)behave consistently. But, for instance you might want to differ the output put of re-summing 19 vs 28. Note though that zero is the only unused sum. The point being that by mapping two or more re-sums to a common value you arecreating a pattern that is artificial.

I’m not really sure the patterns here are meaningful, as birth is a random process, so co-incident birthdays are a false positive. But, that’s not my problem. i’ve just been trying to make this work for you in Tinderbox. :slight_smile:

The revised TBX: pattern-code-2.tbx (287.4 KB)

Thanks, Mark — I really appreciate the clarity here, especially around implicit coercion and those edge-case year/month sums like 19 or 28. This is something not common to a “human” like me. I need to learn the computer language to teach Tinderbox what I “sense.” Thank you!

That definitely explains why some of the early tests were returning unexpected PatternCodes like 1067. I’ve now implemented your fCheckSum() logic to ensure every component reduces cleanly to a single digit — and for now, mapping 19 or 28 to 0 helps contain the noise, until I refine the edge-case treatment further.

You’re right that the value of the PatternCode isn’t in precise mathematical meaning but in structuring patterns across large datasets — in this case, thousands of public figures’ birthdates — as a sort of proxy fingerprint. It’s not astrology, but it’s not purely statistical either. So having the system behave predictably and scalably in Tinderbox is already a huge win. Thanks again — this was incredibly helpful.

Happy to help. An interesting problem, especially as I doubt my sanity for a part until I discovered the ‘adds up to 10’ edge case. :slight_smile:

One other thought, I don’t think it matters whether your system uses day/month or month/day ordered date info as the code extracts the year/month/day from a date that is stored date-order-agnostically at source. I note this in case people wonder about that facet of the process.


FWIW, passing readers may wonder why I didn’t start immediately with functions. Two reasons: firstly, I wanted to most the in-process user attributes into variables; secondly, I needed to understand the logic of someone else’s idea. Using one’s own ideas, it would be easier to jump to functions. But, functions are new-ish to Tinderbox, and those less used to ‘code’ may well find the additional abstraction of functions makes it harder to under stand the process.

With the latter in mind, the nature of the task here means the relative speed of using functions or not isn’t an issue, so if you prefer to use a single script, then do so! Functions create shorter code but harder to read the logic top-to-bottom (especially without experience of use of sub-routines).

Thank you, Mark. I was initially rushing to get things done through functions and shortcuts, but you’ve slowed me down in the best possible way—not just to learn Tinderbox, but to think with Tinderbox. That shift alone has been profound.

I puzzled for days over what you said—“co-incident birthdays are a false positive”—because I realized I wasn’t even looking at birthdays the way a computer would. For me, dates aren’t raw data, they’re carriers of pattern and inherited structure. It’s like studying insects: they don’t teach survival to their offspring; they pass it down genetically. Humans don’t get that—we have to break things apart, learn, re-structure, and rebuild meaning from scratch. That’s what I’m doing here.

My Patternology system isn’t statistical; it’s symbolic. A shared birthdate doesn’t mean “same outcome,” it points to a shared internal timing code. So when the machine stops at “10,” it hasn’t miscalculated—it’s simply unaware of the symbolic layer I’m working with. I need to build that layer myself, piece by piece, and thanks to your guidance, I now see how.

1 Like

Kind words and useful reflection for others starting a project. I’m as prone as anyone to rushing ahead. I’m sorry, I could have phrased “co-incident birthdays are a false positive” better. The point was not about the symbolism of the dates but that dates that were intended to give a different pattern weren’t ding so. The ‘false positive’ is that two different patterns were merging giving the researcher a false group. As you note, this isn’t a particular problem here, especially if you’re aware of this. At the time I was worried you might not realise this coincidence was happening in an unexpected manner.

Your point about ‘thinking with Tinderbox’ is well made. The app is a toolbox of tools and features to help us explore our notes (information, data, whatever). Most apps are restrictive in ways not always apparent. IOW, successful use hinges on using the app in the right (i.e. designed/intended way). The same is actually the same for Tinderbox although the range of choice is larger (albeit not unlimited!). But using the tools to assist thinking via use of the app is how I observe deeper value being unlocked—but it is still OK to just write a few notes if that is all a task requires. :slight_smile:

Thank you again, Mark—your reply means a lot.

I wanted to share how this journey has opened up something deeper for me. Your explanation of the “false positive” wasn’t taken harshly at all—in fact, it helped me realize how the underlying logic of computers differs so profoundly from how we make meaning as humans. What you pointed out about the machine needing every step spelled out brought me to a completely new perspective. I’ve long operated with what I might call a “literature sensibility,” trying to intuit structure, read between the lines, and trust what feels coherent. But as you showed me, that sensibility hits a wall unless I learn how the machine thinks. That’s a perspective I honestly never had before, and now I see it not as a limitation, but as a new terrain to walk.

I also resonated with your point about tools and how most apps offer convenience but at the cost of flattening how we think. They promise ease, but sometimes that ease makes us forget what we were really trying to ask ourselves in the first place. How cynical—and how common! Tinderbox, in contrast, doesn’t lure you into a predetermined workflow. It waits. It asks back. It gives space. But it also requires us to show up not just as users, but as thinkers. I’ve been using Tinderbox for a long time—even before UTF-8 worked well with Japanese text, when I would get garbled characters trying to write in my now-almost-native fourth language. Strangely, my first impulse in Tinderbox then is still my impulse now: to write, to look, and to try to see what I can’t yet see. It never gave me clarity unless I thought harder. And maybe that’s the point.

I could have asked AI to optimize all of this, to suggest what’s efficient or futuristic. Sometimes I do, and it pushes me far—but often in directions that are far from myself. Coming back here, asking simple but real questions, and receiving honest human wisdom from someone who understands the terrain—I can’t express how meaningful that is. Thank you not just for solving a technical issue, but for reminding me that thinking deeply is the work itself. And that it’s worth it.

1 Like