{
  "ship_ready": true,
  "identity": {
    "id": "ClsfRWx5C7E",
    "url": "https://www.youtube.com/watch?v=ClsfRWx5C7E",
    "title": "Run Apps Script at Certain Specific Times: How to Automatically Manage Timed Triggers",
    "channel": "Sheets Ninja",
    "category": "google-enterprise-automation-and-operations",
    "type": "video",
    "status": "accepted",
    "model": "gemini-3.6-flash",
    "summary_source": "gemini",
    "accepted_at": "2026-08-25T19:39:25Z"
  },
  "outputs": {
    "short_summary": {
      "status": "ok",
      "value": "Teaches Outcome 4 (Zero-Subscription Scheduling) by demonstrating programmatic creation and auto-cleanup of precise time-driven clock triggers in Google Apps Script without third-party SaaS fees."
    },
    "summary_md": {
      "status": "ok",
      "value": "# Run Apps Script at Certain Specific Times: How to Automatically Manage Timed Triggers\n\n- **Channel:** Sheets Ninja\n- **URL:** https://www.youtube.com/watch?v=ClsfRWx5C7E\n- **Source:** gemini-transcript\n\nHere is a practical summary of the video.\n\n### What the Video Shows\nThe video demonstrates how to write a Google Apps Script that automatically schedules its own next run at an exact time or interval (such as every 5 minutes or precisely at 9:00 AM) while automatically deleting its previous trigger to keep the project clean.\n\n### Who It Is For\nSmall-business owners and operators who use Google Workspace/Sheets automation and need scripts to execute at exact times, rather than relying on Google's default hourly time-driven windows (e.g., running anytime between 9:00 AM and 10:00 AM).\n\n---\n\n### Concrete Tools & Tactics\n\n* **Exact-Time Trigger Creation:** Uses `ScriptApp.newTrigger(functionName).timeBased().at(nextRuntime).create()` to schedule execution at a specific `Date` object, bypassing standard hourly windows.\n* **State Tracking:** Uses `PropertiesService.getScriptProperties()` to store the unique trigger ID (`scriptID`) after creating a new trigger so it can be retrieved on the next run.\n* **Trigger Cleanup:** Uses `ScriptApp.getTriggers()` and `ScriptApp.deleteTrigger()` to iterate through existing project triggers, find the matching saved ID, and remove the spent trigger before running main tasks.\n* **Precise Date Calculations:** \n  * Strips seconds/milliseconds off `Date` objects using `setHours()` to prevent time drift across repeating intervals.\n  * Uses helper functions to calculate exact future runtimes by converting time into milliseconds (`minutes * 60 * 1000` or `days * 24 * 60 * 60 * 1000`).\n\n---\n\n### Key Takeaways\n\n1. **Bypass Imprecise Google Time Slots:** Default time-driven triggers execute within a broad one-hour window. Programmatically creating triggers with `.at(date)` enforces fixed execution times down to the minute.\n2. **Auto-Delete Expired Triggers:** Without a cleanup step, self-scheduling scripts fill your Google Apps Script project with dead triggers. Deleting the previous trigger on every execution keeps the workspace clean.\n3. **Store Trigger IDs in Script Properties:** Use `PropertiesService` to save the newly generated trigger\u2019s unique ID (`getUniqueId()`). The script reads this value during its next run to know exactly which trigger to remove.\n4. **Strip Seconds to Prevent Execution Drift:** When calculating relative run times (like \"5 minutes from now\"), zero out the seconds and milliseconds on the current `Date` object so the schedule doesn't slowly slide forward over time.\n5. **Flexible Daily or Minute Recurrence:** You can reuse the exact same trigger-management logic whether you are scheduling fixed daily runs (e.g., setting hours to `9, 0, 0, 0`) or continuous short-interval loops.\n"
    },
    "timestamps": {
      "status": "ok",
      "value": [
        {"t": "00:00", "label": "do you ever need to run a script at a"},
        {"t": "01:00", "label": "trigger and so we'll just call this"},
        {"t": "02:05", "label": "triggers unique"},
        {"t": "03:06", "label": "want to add a day so for example you"},
        {"t": "04:09", "label": "our variables that we can set here or"},
        {"t": "05:10", "label": "to take is right now and"},
        {"t": "06:12", "label": "date so what this helper function is"},
        {"t": "07:14", "label": "perfectly so we have a 5 minute next"},
        {"t": "08:15", "label": "service again get scri properties set"},
        {"t": "09:15", "label": "ahead and run this and so here is when"},
        {"t": "10:16", "label": "quick and so this one we're going to"},
        {"t": "11:16", "label": "math on it and then we'll turn it back"},
        {"t": "12:16", "label": "days so uh we else I need to change this"},
        {"t": "13:17", "label": "and see what it comes up with so we have"}
      ]
    },
    "transcript": {
      "status": "ok",
      "value": "[00:00] do you ever need to run a script at a\n[00:04] particular time each day or at\n[00:06] particular time intervals in today's\n[00:08] video I'm going to show you how to write\n[00:11] a script that you can use to add a\n[00:12] certain amount of time every time the\n[00:15] script is run or that you can run at a\n[00:17] specific time like 9:00 a.m. and it's\n[00:19] going to run at 9: not at anywhere\n[00:21] between 9 and\n[00:21] [Music]\n[00:25] 10: all right so let's go ahead and jump\n[00:29] right in so we're doing two primary\n[00:31] things and so the first thing is the\n[00:34] major thing right which is scheduling\n[00:36] the script to run again every time it\n[00:38] runs based on the time period that you\n[00:39] want it to run at so whether that's\n[00:43] every day at a particular time or if you\n[00:46] R want it to run multiple times a day\n[00:48] and so the other part of it is that\n[00:50] we're going to do a little cleanup as we\n[00:52] go and so every time we run we're going\n[00:54] to delete the trigger that it just ran\n[00:56] under so let's go ahead and Jump Right\n[00:58] In let's start by deleting our old\n[01:00] trigger and so we'll just call this\n[01:02] delete old\n[01:04] trigger and then what we're going to do\n[01:05] is we're going to use the property\n[01:08] service to track this and so I'm going\n[01:11] to check Property Service to see if\n[01:12] there's anything existing which the\n[01:14] first time we run it there is not going\n[01:17] to be we're just going to get the script\n[01:18] properties and then we're going to get a\n[01:20] property so the property that we're\n[01:23] going to call is script\n[01:26] ID and so if this does not exist it's\n[01:30] going to be null and so we're going to\n[01:33] say if it's not null then we're going to\n[01:36] delete that trigger and so to delete\n[01:37] that trigger we're going to get the list\n[01:37] of\n[01:40] triggers and that is script app get\n[01:46] triggers and then we're just going to\n[01:50] quickly iterate through the triggers we\n[01:57] here and then inside this we're going to\n[02:05] triggers unique\n[02:12] ID is equal to the script ID if we've\n[02:14] saved it then what we're going to do is\n[02:18] we're actually going to delete that\n[02:21] trigger and then we can just use that\n[02:24] iteration right there and so that will\n[02:27] delete our old\n[02:30] trigger so this is just a little clean\n[02:31] because otherwise what happens in your\n[02:33] triggers here as you add more and more\n[02:35] triggers you're going to have all the\n[02:37] old ones here and it gets a little bit\n[02:39] tiresome to have a bunch of them there\n[02:41] so let's jump into the main part here\n[02:44] and that is determining our new Trigger\n[02:46] Time and so what we're doing here is\n[02:49] every time the script runs we want to\n[02:50] automatically set up to run the next\n[02:52] time we want it to\n[02:54] run and so let's go ahead and just set\n[02:56] some variables like for example let's\n[02:59] just do let add minutes and so we'll\n[03:02] start start with adding minutes and then\n[03:04] I'll show you how to change it if you\n[03:06] want to add a day so for example you\n[03:08] want it want it to run at 9:00 a.m.\n[03:11] every day and so next let's figure out\n[03:14] the current time and so let's just do\n[03:17] let right now equals to new\n[03:19] date and so that will get the current\n[03:21] date and\n[03:25] time and then let's go ahead and say let\n[03:29] this hour equal to right now and then\n[03:31] we're going to modif by this so This\n[03:35] Hour set hours and we're going to set\n[03:38] this because what happens is Google run\n[03:40] within a time slot that you've given it\n[03:43] and so if you set the script to run at a\n[03:45] certain minute for example it's going to\n[03:48] run within that minute and so if we\n[03:51] don't adjust this then it's going to\n[03:52] keep sliding and you're going to kind of\n[03:56] have the same effect as when you just do\n[03:58] run at certain\n[04:00] hours and so what we're going to do here\n[04:03] is get right now get\n[04:06] hours and if you can see up here here's\n[04:09] our variables that we can set here or\n[04:11] the arguments so hours minutes seconds\n[04:14] and milliseconds so this determines what\n[04:16] we want to do here so if for example you\n[04:17] were setting it for a certain time each\n[04:19] day this is where you'd set like 9 or\n[04:21] something like that but let's go ahead\n[04:26] and just do this right now get\n[04:30] minutes 0 0\n[04:32] so what this is doing is it's getting\n[04:34] the hours and the minutes from the\n[04:36] current time but it's disregarding the\n[04:38] seconds and then setting them to\n[04:40] zero and so at this point we're going to\n[04:45] do let next runtime we're going to do a\n[04:46] function we're going to write a new\n[04:48] function here we're going to call it\n[04:50] minutes later and then we're going to\n[04:53] use this hour and add\n[04:56] minutes so let's go ahead and write that\n[04:58] function real quick so this is going to\n[05:00] be a function outside of this one it's\n[05:01] going to be just a little helper\n[05:04] function and so we're going to call this\n[05:06] minutes later and then what we're going\n[05:10] to take is right now and\n[05:13] minutes and so inside this function what\n[05:15] we're going to do is let's start with\n[05:17] let this time equals\n[05:20] to right now get\n[05:23] time and so what that's going to do is\n[05:27] get time in milliseconds since was\n[05:28] January 1st\n[05:30] 1970 and then we're going to do some\n[05:32] math on it and that's why we're getting\n[05:34] the time so we're going to do let our\n[05:39] add and that's going to be minutes\n[05:41] time 60\n[05:45] seconds time 1,000 milliseconds so this\n[05:50] minutes\n[05:56] time and so our future date\n[06:03] our future time is going to be a new\n[06:06] date and then we'll use that new time\n[06:08] and then we can return to our\n[06:12] date so what this helper function is\n[06:18] going to do is take our current time and\n[06:21] then add a specific number of minutes\n[06:23] and so we can go\n[06:25] ahead and take a look at what this will\n[06:28] look like let's do a console log and\n[06:30] we'll do right\n[06:34] now and next run\n[06:37] time so let's run this real quick to see\n[06:38] what this looks like we'll have to\n[06:42] quick and then here we go so current\n[06:49] time so\n[06:53] 1712 and 1717 and if you notice there's\n[06:55] no second on here it's stripping those\n[06:58] off so it's taking the current time and\n[07:02] adding 5 minutes and so this is the next\n[07:06] runtime right here and then this is the\n[07:09] right now so it looks like it's working\n[07:14] perfectly so we have a 5 minute next\n[07:17] runtime and so now all we need to do is\n[07:21] create our trigger and then we'll save\n[07:24] that trigger ID back to this script ID\n[07:26] so we can delete it the next time we run\n[07:30] this so let's do let new trigger\n[07:33] and this is going to be script app new\n[07:35] trigger and then inside the new trigger\n[07:37] you reference the function name which is\n[07:41] in and then this will be a time based\n[07:48] trigger and then what we're going to use\n[07:51] the method is going to be at and we're\n[07:52] not going to use that hour we're not\n[07:53] going to use every whatever we're going\n[07:55] to use at and so this is where we can\n[08:00] specify a specific time and this will be\n[08:03] our next runtime that we came up with so\n[08:05] now we can go ahead and create and then\n[08:08] we want to get that unique\n[08:11] ID and so now we can go to the property\n[08:15] service again get scri properties set\n[08:17] property and we're going to do that\n[08:17] script\n[08:24] trigger so at this point we got\n[08:30] everything in place to do a 5 minute\n[08:32] increment and so if you ran this right\n[08:35] now it would set a new trigger for 5\n[08:38] minutes from now and then it would run\n[08:41] every finite 5 minutes from then on\n[08:44] out and so at this point obviously just\n[08:45] running the script isn't going to be\n[08:45] super\n[08:49] useful but we could add our rest of\n[08:54] here and so what it'll do is it'll clean\n[08:59] up our old triggers it'll process figure\n[09:02] out when the next trigger needs to run\n[09:03] and then you can run what you're\n[09:05] actually running in your script and this\n[09:07] will all run automatically so let's go\n[09:09] ahead and run it and just see if it\n[09:10] creates a trigger so let's check our\n[09:13] triggers nothing here right now let's go\n[09:15] ahead and run this and so here is when\n[09:18] our next trigger should be so\n[09:21] 1719 and so let's jump into our\n[09:23] triggers up and here we go there is one\n[09:25] so let's go ahead and\n[09:28] edit and there is our trigger time so\n[09:31] that is where working perfectly so I'm\n[09:32] going to go ahead and delete this just\n[09:32] for\n[09:36] now and then let's go back to the editor\n[09:38] and so the other thing I want to show\n[09:41] you is what if you want to run this at a\n[09:44] specific time each day and so often what\n[09:46] happens is maybe you want it to run at 9\n[09:49] 9:00 a.m. or 8:00 a.m. and when you've\n[09:52] done the run at hour it runs anywhere\n[09:56] between you know 9 and and 10 so it's\n[09:58] it's not working quite the way you're\n[10:00] wanting it to work so let's go ahead and\n[10:04] look at what it takes to do a specific\n[10:07] time every day so we're going to\n[10:09] refactor some of this and so one thing\n[10:12] we're going to do is let's add another\n[10:14] helper function real\n[10:16] quick and so this one we're going to\n[10:16] call\n[10:20] function add\n[10:24] days and and this one we're going to do\n[10:27] selected date and days so this going to\n[10:29] be similar to this minutes later which\n[10:31] be slightly different so I'm going to\n[10:35] actually steal\n[10:38] this and what we're going to do\n[10:42] here is in this one let's go ahead and\n[10:45] flip this around real quick so 1,000\n[10:48] milliseconds and then we need there's 60\n[10:49] Su in a\n[10:53] minute and 60 minutes in an hour and 2\n[10:56] 24 hours in a day and that time is days\n[10:59] so if we say one day we need\n[11:03] it to be one times the sum of those and\n[11:05] so when you do this get time it's the\n[11:08] mill sunant since um\n[11:12] 1970 and so we just need to turn the\n[11:15] date into a number so we can do some\n[11:16] math on it and then we'll turn it back\n[11:20] into a date so here's our add and then\n[11:23] new time add plus this time and then\n[11:25] return future dat so we only had to do a\n[11:27] little bit of tweaking to get this to\n[11:30] work um me actually\n[11:32] make sure I finish this here select a\n[11:35] date there we go I didn't have that\n[11:37] right now in there all right so now we\n[11:40] have our ad days so let's look at what\n[11:42] it takes to modify this to run on days\n[11:46] instead of minutes so we don't need this\n[11:48] add minutes we can change this to add\n[11:52] days and let's just say\n[11:54] one and this is where we can change our\n[11:54] set\n[12:00] hours so we we can\n[12:04] do um this day for example instead of\n[12:07] right now and then this day we're going\n[12:08] to set\n[12:11] hours and then we're going to do this\n[12:16] days so uh we else I need to change this\n[12:22] one to add\n[12:25] days oh speaking of rich I can't have\n[12:29] this the same name as that so let's do\n[12:33] add there we go all right so last thing\n[12:40] here so this is where you can just set\n[12:42] what you want the time to be so let's\n[12:45] say you want to run at 9:00 a.m. you can\n[12:47] just set just like that and you can\n[12:48] actually set it if you want to run at\n[12:50] 9915 you can set it like that or if you\n[12:54] want to run at 7 or at uh\n[12:58] 2:00 so you can just determine what time\n[13:01] you want it to run here every day and\n[13:03] this is how you would set\n[13:06] that and then you can just add that day\n[13:08] and so let's go ahead and\n[13:11] just put these off like\n[13:14] that let's go ahead and just run this\n[13:17] and see what it comes up with so we have\n[13:21] July 9 700 a.m. and so our next run time\n[13:24] is going to be tomorrow at 700\n[13:31] a.m. and so let's go ahead and do this\n[13:32] and then actually run\n[13:35] this and so we should have a trigger for\n[13:39] a.m. so let's check this\n[13:48] out so July 10 7 a.m. all right Perfect\n[13:49] all right so that is it for today's\n[13:52] video make sure to check out our other\n[13:55] videos for more tutorials on Google\n[13:59] Sheets and appscript so I'll put a link\n[14:02] to a Google Sheets project with this\n[14:04] script in it so that way you can get to\n[14:08] it and I'll have both copies in there of\n[14:10] the adding minutes or the adding days\n[14:13] that where you guys can jump in here and\n[14:15] get started so as always thanks for\n"
    },
    "audio": {"status": "missing", "value": ""},
    "summary_video": {"status": "missing", "value": ""},
    "quiz": {"status": "missing", "value": ""},
    "lesson_html": {"status": "missing", "value": ""},
    "semkhor_title": {"status": "missing", "value": ""},
    "semkhor_html": {"status": "missing", "value": ""}
  }
}
