New calculation field cheat sheet - Part 3 dates, markdown & recipes

Quick reference for Tape calculation fields: syntax, functions, and recipes for the new editor.

We built a demo workspace to try things out. βž” Duplicate it


Part 3 of 4. Dates & time, Markdown output, and drop-in recipes. For syntax basics see Part 1; for strings, arrays, and libraries see Part 2; for best practices and troubleshooting see Part 4.

:pushpin: This cheat sheet covers the new calculation editor in the new record experience. For details on the editor itself (autocomplete, live preview, fullscreen, Find & Replace, multi-cursor, classic vs new, migration), see the announcement post in the Tape Community.

Two key rules in the new editor:

  1. No @ in saved code. @ is just a search trigger; the saved field reference is the bare name.
  2. Spaces become underscores. First name is written as First_name.

Each section and recipe shows the fields it uses with their type. Replace the field names with your own; spaces in your field name become underscores in the code (e.g. Hourly Rate β†’ Hourly_Rate).


Recipe library

Drop-in calculations for the most common asks. Copy the snippet, swap field names, done.

Money

Financial math: VAT, currency formatting, discounts, late fees.

Field types: Amount [Number], Revenue [Number], Balance [Number], Deadline [Date]
Output type: Number

// Add VAT (19%)
Amount * 1.19
// Cap at zero
Math.max(0, Balance)
// Total: base Γ— rate + fixed fee
// (Amount = net, Revenue = tax rate as decimal, Balance = shipping)
((Amount * (1 + Revenue)) + Balance).toFixed(2)
// Discount if order over threshold
Amount >= 1000 ? Amount * 0.9 : Amount

Output type: Text

// Localized currency
Amount.toLocaleString("de-DE", { style: "currency", currency: "EUR" })
// Late fee: 2% per started week overdue
const weeksLate = Math.max(0, Math.ceil(moment(new Date()).diff(moment(Deadline), 'days') / 7))
Amount * (1 + 0.02 * weeksLate)

Status & indicators

Visual output driven by field values: emoji flags, traffic lights, mood scores, workload badges.

Field types: Score [Number], Priority [Single Select with options β€œLow”, β€œMedium”, β€œHigh”], Status [Single Select], Deadline [Date], Hours [Number]
Output type: Text

// Pass / Fail
Score >= 80 ? "βœ… Pass" : "❌ Fail"
// Traffic light
Priority === "High" ? "πŸ”΄" : Priority === "Medium" ? "🟑" : "🟒"
// Priority from lookup table
({ Low: "🟒", Medium: "🟑", High: "πŸ”΄" })[Priority] ?? "βšͺ"
// Overdue flag
moment(new Date()).diff(moment(Deadline), 'days') > 0 && Status !== "Done"
  ? "⏰ Overdue"
  : "🟒 On track"
// Mood from score
Score >= 90 ? "πŸ”₯" :
Score >= 75 ? "πŸ˜€" :
Score >= 50 ? "πŸ™‚" :
Score >= 25 ? "😐" : "😟"
// Project weather (deadline + status combined)
const days = moment(Deadline).diff(moment(), 'days')
Status === "Done" ? "βœ…" :
days < 0          ? "πŸ”΄" :
days <= 3         ? "🟠" :
days <= 14        ? "🟑" : "🟒"
// Workload badge
Hours >= 40 ? "πŸ”΄ Overloaded" :
Hours >= 30 ? "🟠 Busy" :
Hours >= 10 ? "🟒 Active" : "βšͺ Light"

Relations & aggregations

Roll-ups and counts across linked records. Always go through .field.aggregation. .all returns a JS array β€” chain any array method on it.

Field types: Tasks [Relation β†’ Title (Text), Status (Single Select with options β€œDone”, β€œBlocked”)], Invoices [Relation β†’ Amount (Number), Status (Single Select), Owner (Text)]
Output type: Number or Text (varies per recipe)

// Average / max / min
Invoices.Amount.avg
Invoices.Amount.max
Invoices.Amount.min
// Comma-separated titles (max 3, then "…")
const titles = Tasks.Title.all
titles.length <= 3
  ? titles.join(", ")
  : titles.slice(0, 3).join(", ") + " (+ " + (titles.length - 3) + " more)"
// All tasks done?
Tasks.Status.all.every(s => s === "Done") ? "βœ… All done" : "⏳ In progress"
// Any blocked task?
Tasks.Status.all.includes("Blocked") ? "🚧 Has blockers" : "🟒 Clear"
// Count "Done" tasks
Tasks.Status.all.filter(s => s === "Done").length
// Completion percentage
const statuses = Tasks.Status.all
const done = statuses.filter(s => s === "Done").length
statuses.length === 0 ? "β€”" : Math.round(done / statuses.length * 100) + "%"
// Sum only invoices over 1000
Invoices.Amount.all.filter(n => n > 1000).reduce((s, n) => s + n, 0)
// Distinct values
[...new Set(Invoices.Owner.all)].join(", ")

Progress visualization

Emoji bars, dot bars, star ratings, sparklines. Unicode only β€” works on mobile and in all views.

Field types: Done [Number], Total [Number], Score [Number], Invoices [Relation β†’ Amount (Number)]
Output type: Text

// Emoji bar
const pct = Math.round((Done / Total) * 100)
"🟩".repeat(Math.round(pct / 10)) +
"⬜".repeat(10 - Math.round(pct / 10)) +
" " + pct + "%"
// Padded percentage badge: "  73%"
String(Math.round(Done / Total * 100)).padStart(3, " ") + "%"
// 20-char dot bar
const pct = Total === 0 ? 0 : Done / Total
"●".repeat(Math.round(pct * 20)) + "β—‹".repeat(20 - Math.round(pct * 20))
// β†’ "●●●●●●●●●●●●●●○○○○○○"
// Star rating (Score 1–5)
"β˜…".repeat(Score) + "β˜†".repeat(5 - Score)
// Sparkline of invoice amounts (scale: max β‰ˆ 1000)
Invoices.Amount.all.map(n => "β–β–‚β–ƒβ–„β–…β–†β–‡β–ˆ"[Math.min(7, Math.floor(n / 125))]).join("")

Text & identity

Normalize text, format IDs, inspect field values. For full string methods and regex, see Part 2.

Field types: Title [Text], Owner [Contact], Tasks [Relation β†’ Title (Text), Status (Single Select)], Record_ID [built-in Number], Start date [Date], Full name [Text]
Output type: Text

// Normalize for comparisons (lowercase + trim)
Title.toLowerCase().trim()
// Inspect a contact field β€” returns display names, e.g. ["Juliet Adams"]
JSON.stringify(Owner, null, 2)
// Inspect related task titles + statuses
const titles = Tasks.Title.all
const statuses = Tasks.Status.all
JSON.stringify(titles.map((title, i) => ({ title, status: statuses[i] })), null, 2)
// Year-prefixed ID: "2026-042"
Start_date.getFullYear() + "-" + String(Record_ID).padStart(3, "0")
// Random suffix ID (regenerates on each formula save)
"REC-" + String(Record_ID).padStart(4, "0") + "-" + uuid.v4().slice(0, 4).toUpperCase()
// Initials with dots: "J. F. K."
Full_name.split(/\s+/).map(p => p[0].toUpperCase() + ".").join(" ")

Validation

Flag missing or inconsistent data inline. Surface problems without leaving the record view.

Field types: Title [Text], Amount [Number], Balance [Number], Total [Number], Full name [Text], Deadline [Date]
Output type: Text

// Flag inconsistent totals
Math.abs((Amount + Balance) - Total) < 0.01 ? "" : "⚠ Total mismatch"
// Required-fields checklist
const missing = []
if (!Full_name) missing.push("Full name")
if (!Title) missing.push("Title")
if (!Deadline) missing.push("Deadline")
missing.length === 0 ? "βœ… Complete" : "⚠ Missing: " + missing.join(", ")

Greetings & polite output

Time-of-day greetings, plurals, Oxford-comma lists. Small details that make output feel handcrafted.

Field types: First name [Text], Tasks [Relation β†’ Title (Text)]
Output type: Text

// Time-of-day greeting
const hour = new Date().getHours();
const greeting = hour < 12 ? "Good morning" :
                 hour < 18 ? "Good afternoon" : "Good evening";
greeting + ", " + First_name + " πŸ‘‹"
// Pluralize cleanly
const n = Tasks.Title.all.length;
`${n} task${n === 1 ? "" : "s"} open`
// Oxford-comma list
const titles = Tasks.Title.all
titles.length === 0 ? "β€”" :
titles.length === 1 ? titles[0] :
titles.length === 2 ? titles.join(" and ") :
titles.slice(0, -1).join(", ") + ", and " + titles.at(-1)

Dates & time

Dates are the most common reason to reach for a library. Tape ships with moment for date formatting, arithmetic, and comparisons.

β€œNow” and date snapping

When you need the current date or time inside a calculation, use new Date(). Snap to start-of-day with moment to avoid time-zone surprises in date comparisons.

What you want Code
Right now (with time) new Date()
Today at 00:00 (no time) moment().startOf('day').toDate()
Today at 23:59:59 moment().endOf('day').toDate()
Start of this week moment().startOf('isoWeek').toDate()
Start of this month moment().startOf('month').toDate()
Start of this year moment().startOf('year').toDate()

Calculation fields recompute on record write, not continuously. If you need β€œlive” date calculations (age, days remaining), the value updates only when something on the record changes. This is by design, same as Notion’s now() and Podio’s date math.

moment: the basics

The core date functions you reach for daily: formatting, parsing, adding/subtracting time, comparing dates.

Function Purpose Example
format(d, fmt) Format to string moment(Signed_date).format("MMMM D, YYYY")
parse(s, fmt, ref) Parse a string moment("2026-04-30", "YYYY-MM-DD").toDate()
parseISO(s) Parse ISO string new Date("2026-04-30")
addDays(d, n) Add days moment(Start_date).add(7, 'days').toDate()
addMonths(d, n) Add months moment(Start_date).add(3, 'months').toDate()
addYears(d, n) Add years moment(Start_date).add(1, 'years').toDate()
addHours(d, n) Add hours moment(Start_date).add(2, 'hours').toDate()
subDays(d, n) Subtract days moment(Start_date).subtract(7, 'days').toDate()
differenceInDays(a, b) Days between moment(End_date).diff(moment(Start_date), 'days')
differenceInBusinessDays(a, b) Working days moment(End_date).diff(moment(Start_date), 'days')
differenceInHours(a, b) Hours between β€”
differenceInMinutes(a, b) Minutes between β€”
isAfter(a, b) a > b Deadline > new Date()
isBefore(a, b) a < b β€”
isToday(d) Same day as today moment(Start_date).isSame(new Date(), 'day')
isWeekend(d) Saturday or Sunday [0, 6].includes(Start_date.getDay())
startOfDay(d) / endOfDay(d) Snap to boundaries β€”
startOfMonth(d) / endOfMonth(d) Snap to boundaries β€”
startOfWeek(d) / endOfWeek(d) Snap to boundaries β€”
getDay(d) 0–6 (Sun–Sat) Start_date.getDay()
getMonth(d) 0–11 β€”
getYear(d) Calendar year Start_date.getFullYear()
getQuarter(d) 1–4 β€”
getWeek(d) ISO week number β€”

Format tokens (most-used)

Building blocks for moment().format() strings. Mix and match to print any date layout you need.

Token Example Notes
YYYY 2026 4-digit year
YY 26 2-digit year
MMMM April Full month
MMM Apr Short month
MM 04 Zero-padded month
DD 08 Zero-padded day
D 8 Day without padding
dddd Friday Full weekday
ddd Fri Short weekday
HH 14 24-hour
hh 02 12-hour
mm 05 Minutes
a pm am/pm (lowercase)
A PM AM/PM (uppercase)

Full token reference

Examples

Field types: Deadline [Date], Start date [Date], End date [Date], Birthday [Date], Invoice date [Date], Sprint [Date Range], Submitted on [Date], Logged at [Date]
Output type: Text (labels) or Date (when returning a Date object for Date output type)

// Today as long date
moment(new Date()).format("dddd, MMMM D, YYYY")
// β†’ "Thursday, April 30, 2026"
// Days remaining until deadline
moment(Deadline).diff(moment(), 'days')
// Working days between two dates
moment(End_date).diff(moment(Start_date), 'days')
// Age in years
moment(new Date()).diff(moment(Birthday), 'years')
// Project status from deadline
const days = moment(Deadline).diff(moment(), 'days')
days < 0 ? "Overdue" :
days <= 3 ? "Due soon" :
days <= 14 ? "On track" : "Comfortable"
// Fiscal quarter label
"Q" + moment(Invoice_date).quarter() + " " + Invoice_date.getFullYear()
// β†’ "Q2 2026"
// ISO week label
"Week " + moment(Invoice_date).isoWeek() + ", " + Invoice_date.getFullYear()
// β†’ "Week 18, 2026"
// End of current month
moment().endOf('month').toDate()
// Pretty range from a Date Range field, e.g. "Apr 30 – May 14, 2026"
moment(Sprint.start).format("MMM D") +
" – " +
moment(Sprint.end).format("MMM D, YYYY")

Date parts (year, month, day, hour, minute, weekday)

Pull out a single number from a date. Useful for grouping, filtering, or building custom labels.

Function Returns Note
d.getFullYear() Calendar year (e.g. 2026) β€”
d.getMonth() 0–11 January = 0
d.getDate() 1–31 Day of month
d.getDay() 0–6 Sunday = 0
d.getHours() 0–23 β€”
d.getMinutes() 0–59 β€”
moment(d).quarter() 1–4 β€”
moment(d).isoWeek() ISO week β€”
// Group records by month name
moment(Submitted_on).format("MMMM YYYY")
// β†’ "April 2026"
// Hour bucket (e.g. "14:00")
moment(Logged_at).format("HH:00")

Unix timestamps

Convert between JavaScript dates and millisecond timestamps. Useful when integrating with external APIs or data exports.

// Date β†’ Unix timestamp (ms)
Start_date.getTime()
// Unix timestamp β†’ Date
new Date(1745000000000)
// Seconds β†’ milliseconds (when an external system gives you seconds)
new Date(1745000000 * 1000)

Time zones

:warning: Advanced timezone support has not been verified in the current editor. Use native Date methods or test before shipping timezone-dependent calculations.

Moment.js formatting reference

moment(Start_date).format("YYYY-MM-DD")
moment(Start_date).add(7, "days").toDate()
moment(End_date).diff(Start_date, "days")

Recipes

Relative labels, SLA detection, anniversary checks, weekend logic.

Field types: Deadline [Date], Start date [Date], Status [Single Select]
Output type: Text

// "in 3 days" / "5 days ago"
const days = moment(Deadline).diff(moment(), 'days')
days === 0 ? "today" :
days > 0 ? `in ${days} day${days === 1 ? "" : "s"}` :
`${Math.abs(days)} day${Math.abs(days) === 1 ? "" : "s"} ago`
// SLA breach (24h)
moment(new Date()).diff(moment(Start_date), 'hours') > 24 && Status !== "Done"
  ? "⚠ SLA breached"
  : "OK"
// Anniversary this month?
Start_date.getMonth() === new Date().getMonth() ? "πŸ—“ Anniversary this month" : ""
// Is today a weekend?
[0, 6].includes(new Date().getDay()) ? "πŸ–" : "πŸ’Ό"

Output type: Date

// Add 5 days to today
moment(new Date()).add(5, 'days').toDate()

Markdown

Format text output with bold, lists, links, tables, checkboxes. Same syntax as in the rest of Tape. Works automatically when the output type is Text (GitHub-Flavored Markdown spec).

Markdown Renders as
**bold** bold
*italic* or _italic_ italic
~~strike~~ strike
`code` code
# H1 … ###### H6 Headings
- item / * item Bullet list
1. item Numbered list
[label](url) Link
![alt](url) Image
> quote Blockquote
```lang block ``` Code block
| col | col | Table
--- Horizontal rule
- [x] task Checkbox

Examples

Field types: Full name [Text], Status [Single Select], Tags [Multi Select], Priority [Single Select], Hours [Number], Done [Number], Total [Number]
Output type: Text

// Bold name + status line
`**${Full_name}**\n${Status}`
// Status badge
Status === "Done" ? "βœ… Done" : "🟑 In progress"
// Bullet list of tags
Tags.map(t => `- ${t}`).join("\n")
// Mini table
`| Field | Value |
|---|---|
| Status | ${Status} |
| Priority | ${Priority} |
| Hours | ${Hours} |`
// Visual progress with emoji blocks
const pct = Math.round((Done / Total) * 100)
const filled = Math.round(pct / 10)
"🟩".repeat(filled) + "⬜".repeat(10 - filled) + " " + pct + "%"
// β†’ "🟩🟩🟩🟩🟩🟩⬜⬜⬜⬜ 60%"

Cards

Mini-cards and structured blocks. Great for summaries shown on dashboards.

Field types: Title [Text], Status [Single Select], Owner [Contact], Revenue [Number], Deadline [Date], Done [Number], Total [Number]
Output type: Text

// Project card
`**${Title}**
${Status}

**Owner:** ${Owner?.[0] ?? "β€”"}
**Revenue:** ${Revenue.toLocaleString("en-US", { style: "currency", currency: "USD" })}
**Due:** ${moment(Deadline).format("MMMM D, YYYY")}
`
// Task summary block
`### ${Title}
- Status: ${Status}
- Owner: ${Owner?.[0] ?? "β€”"}
- Due: ${moment(Deadline).format("MMMM D, YYYY")}
- Progress: ${Done}/${Total}
`

Resources


Have a recipe worth adding, or hit a case this page does not cover? Reply below.


1 Like