Task guide
Add a timestamp when a row changes
Change a status cell and the script stamps the same row with the current date and time. There is nothing to install beyond saving the script.
1 · Prepare
Put these pieces in place
- Open the spreadsheet you want to stamp and create a tab named Tasks.
- Put a task name in column A, a status in column B, and leave column E free for the timestamp.
- Keep your headers in row 1, because the script ignores that row on purpose.
Change these values
- Sheet name
TasksThe script reacts only to edits made on the tab with this name.
- Watched column
2Column B is column number 2. Change it to the column you want to watch.
- Timestamp column
5Column E is column number 5. The date and time goes here, in the row you edited.
- Date format
yyyy-MM-dd HH:mmThis is how the stamp is written, for example 2024-03-08 14:05.
2 · Review before running
What the script can access and change
Permissions you approve
- Nothing to approve. A simple trigger does not open the authorization dialog.
- The code reads the cell you edited and writes to the timestamp column in the same spreadsheet. It touches no other file.
Effects of one run
- Writes the current date and time into column E of the row you edited on the Tasks tab.
- Changes nothing else. Other rows, other tabs, and other files stay as they are.
- Does nothing when more than one cell is edited at once, when the edit is on another tab, or when the edit is in row 1.
3 · Copy
Paste this into the Apps Script editor
Create or open a script file, replace the example values marked above, then save the project.
const SHEET_NAME = 'Tasks'; // The script reacts only to edits made on the tab with this name.
const WATCHED_COLUMN = 2; // Column B is column number 2. Change it to the column you want to watch.
const TIMESTAMP_COLUMN = 5; // Column E is column number 5. The date and time goes here, in the row you edited.
const DATE_FORMAT = 'yyyy-MM-dd HH:mm'; // This is how the stamp is written, for example 2024-03-08 14:05.
/** @param {Events.SheetsOnEdit} e */
function onEdit(e) {
const range = e.range;
const sheet = range.getSheet();
if (sheet.getName() !== SHEET_NAME) return;
if (range.getNumRows() > 1 || range.getNumColumns() > 1) return;
if (range.getColumn() !== WATCHED_COLUMN) return;
const editedRow = range.getRow();
if (editedRow < 2) return;
const stamp = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), DATE_FORMAT);
sheet.getRange(editedRow, TIMESTAMP_COLUMN).setValue(stamp);
} 4 · Run safely
Follow these steps in order
Make one small test row
On the Tasks tab, put headers in row 1 and one test task in row 2. Leave column E empty so you can watch it fill in.
Paste the function and save
Open Extensions, choose Apps Script, replace the starter function with this one, and save. A function named onEdit is a simple trigger, so there is nothing else to set up and no need to press Run.
Test it by hand
Go back to the spreadsheet and type a word in cell B2 yourself. Within a second or two, cell E2 shows the current date and time.
Point it at your own columns
Change the tab name, the watched column number, and the timestamp column number to match your sheet. Save, then edit one cell again to confirm the stamp lands in the right place.
5 · Confirm
How to tell it worked
A second or two after you change a cell in column B, column E of that same row shows a stamp such as 2024-03-08 14:05.
If the result is different
Troubleshooting
- Nothing happens when I change a cell.
- Check that the tab name matches Tasks exactly, including capital letters and spaces, and that you edited column B in row 2 or below. A simple trigger runs only for edits a person makes in the browser, so a value written by another script or by an import does not start it.
- Pasting a block of cells adds no timestamps.
- That is on purpose. The getNumRows and getNumColumns check skips any edit that covers more than one cell. Remove those two lines only if you also write a stamp for every row in the block.
- The execution log shows "You do not have permission to call GmailApp.sendEmail".
- Simple triggers cannot use services that need approval, such as Gmail or UrlFetchApp. Move that work into a normal function you run yourself, or create an installable trigger from the Triggers panel in the left sidebar.
- The stamp shows as a plain number such as 45359.
- The cell is formatted as a number. Select column E, then choose Format, Number, and Date time or Plain text.
Technical details
Names used in this script
Open these details when you want to adapt the example or look up an exact service or method name.
Show Apps Script symbols and service pages
Apps Script symbols
Service pages
Keep going
Related tasks, basics, and apps
- Task guide Move finished rows to an Archive tab
Rows with the status Done are copied to the bottom of an Archive tab and then removed from Tasks. This one deletes rows, so test it on a copy of the file first.
- Task guide Add a custom menu to a Google Sheet
Use an onOpen() simple trigger and SpreadsheetApp.getUi().createMenu() to add a custom menu bar item that shows which cell is currently selected.
- Task guide Run an Apps Script function every day
Run the setup function once, then Apps Script calls the reminder function each day around your chosen hour.
- Object reference Sheet
A Sheet is one tab inside a Google spreadsheet. Reach it with getSheetByName() or getActiveSheet(), then inspect getLastRow() or read cells with getRange().
- Object reference Range
A Range represents one cell such as A2 or a rectangular block of rows and columns in a Sheet. Use getValue() for one cell and getValues() or setValues() for a 2D array of rows.
- Basics Google Apps Script triggers explained
A Google Apps Script trigger runs a function when an event happens, without pressing Run. Simple triggers like onEdit and onOpen need no setup, while installable triggers run on a schedule or form submit using the OAuth scopes approved by their owner.
- Basics Common Apps Script errors and what they mean
Apps Script error messages name the failure directly, from TypeError: Cannot read properties of null (reading 'getRange') to ReferenceError and quota limits. Find the exact message below for the cause and fix.
- Basics Dates, time zones, and Utilities.formatDate
A Date holds one instant in time. Use Utilities.formatDate(date, timeZone, pattern) and Session.getScriptTimeZone() to format it in the right time zone.
- Google app Google Sheets
SpreadsheetApp lets a Google Sheets script read a Sheet or Range, add a custom menu or formula, and run repeatable actions across rows.
- Google app Triggers
A trigger is a saved schedule or event rule in ScriptApp that runs your function on time, on edit, or on form submit.
- Diagnostic TypeError: e.range is not a function
Why e.range is a Range property rather than a method on the onEdit event object.
- Book chapter Validate, highlight, and react to Sheets edits
Add dropdowns, checkboxes, and conditional formatting to Google Sheets, and stamp edits with a guarded onEdit trigger.