Quick reference for Tape calculation fields: syntax, functions, and recipes for the new editor.
- Part 1: Syntax & field references
- Part 2: Functions & HTML output
- Part 3: Dates, Markdown & recipes (this post)
- Part 4: Best practices & troubleshooting
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.
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:
- No
@in saved code.@is just a search trigger; the saved field reference is the bare name.- Spaces become underscores.
First nameis written asFirst_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) |
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
Advanced timezone support has not been verified in the current editor. Use native
Datemethods 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~~ |
|
`code` |
code |
# H1 β¦ ###### H6 |
Headings |
- item / * item |
Bullet list |
1. item |
Numbered list |
[label](url) |
Link |
 |
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.
- Part 1: Syntax & field references
- Part 2: Functions & HTML output
- Part 3: Dates, Markdown & recipes (this post)
- Part 4: Best practices & troubleshooting