{"id":13989984,"url":"https://github.com/kessler/tempus-fugit","last_synced_at":"2026-01-11T07:01:45.724Z","repository":{"id":11601465,"uuid":"14093814","full_name":"kessler/tempus-fugit","owner":"kessler","description":"A scheduling and time utilities module that doesn't waste your time","archived":true,"fork":false,"pushed_at":"2018-04-04T14:03:11.000Z","size":37,"stargazers_count":70,"open_issues_count":3,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-10-08T18:18:59.883Z","etag":null,"topics":["javascript","node-js","nodejs","scheduling"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kessler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-11-03T21:20:03.000Z","updated_at":"2023-01-28T16:14:59.000Z","dependencies_parsed_at":"2022-08-30T06:10:38.371Z","dependency_job_id":null,"html_url":"https://github.com/kessler/tempus-fugit","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/kessler/tempus-fugit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kessler%2Ftempus-fugit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kessler%2Ftempus-fugit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kessler%2Ftempus-fugit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kessler%2Ftempus-fugit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kessler","download_url":"https://codeload.github.com/kessler/tempus-fugit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kessler%2Ftempus-fugit/sbom","scorecard":{"id":556471,"data":{"date":"2025-08-11","repo":{"name":"github.com/kessler/tempus-fugit","commit":"bf7dc8042f87d38f8611b358f445da05bfbdb66a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.7,"checks":[{"name":"Code-Review","score":1,"reason":"Found 3/30 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 3 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T12:28:58.818Z","repository_id":11601465,"created_at":"2025-08-20T12:28:58.819Z","updated_at":"2025-08-20T12:28:58.819Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28296941,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T04:44:51.577Z","status":"ssl_error","status_checked_at":"2026-01-11T04:44:44.232Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","node-js","nodejs","scheduling"],"created_at":"2024-08-09T13:02:13.939Z","updated_at":"2026-01-11T07:01:45.458Z","avatar_url":"https://github.com/kessler.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Tempus Fugit [![Build Status](https://secure.travis-ci.org/kessler/tempus-fugit.png?branch=master)](http://travis-ci.org/kessler/tempus-fugit) [![Join the chat at https://gitter.im/kessler/tempus-fugit](https://badges.gitter.im/kessler/tempus-fugit.svg)](https://gitter.im/kessler/tempus-fugit?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n\n\u003e Tempus fugit is a Latin expression meaning \"time flees\", more commonly translated as \"time flies\". It is frequently used as an inscription on clocks.\n\nThis module contains high level api for scheduling jobs and also exposes utilities and classes to help build other more custom / complex scheduling code.\n\nInstall\n-------\n```\n\tnpm install tempus-fugit\n```\n\nUsage\n-----\n### Scheduling api\n\nThe scheduling api can be used to schedule single time or repeating jobs. Repeating jobs schedule is defined using the interval object (see below).\n\n##### schedule a one time job in the future:\n```js\nvar schedule = require('tempus-fugit').schedule;\n\nvar futureDate = new Date(....);\nfunction task() {}\n\nvar job = schedule(futureDate, task);\n\n// can cancel\njob.cancel();\n\njob = schedule(1000, task); // schedule in 1 second from now\n```\n\n##### schedule a repeating / recurring job:\n```js\nvar schedule = require('tempus-fugit').schedule;\n\nvar interval = { hour: 1, minute: 5 }; // every hour and 5 minutes\n\n// job.done() is not required when overlappingExecutions is true\nfunction task(job) { \n\t// this.done() also works\n\t// also job.callback() can be used to create a callback function instead, e.g fs.readFile('foo', job.callback())\n\tjob.done(); \n}\n\nvar job = schedule(interval, task /*, {.. options ..} */);\n\n// can cancel\njob.cancel();\n```\n##### scheduling options:\n\nunref: \\[boolean\\] (default false) setting this to true will issue automatic unref() on timers, which will allow the node process to exit when a task is run.\n\noverlappingExecutions: \\[boolean\\] (default false) setting this to true will cause tasks to overlap if\nthey dont finish before interval time elapses.\n\ncreateOnly: \\[boolean\\] (default false) if set to true execute() will not be called, this means you will have to call job.execute() after shceduling.schedule(...)\n\n##### the interval object:\n```js\nvar interval = {\n\tmillisecond: 1,\n\tsecond: 2,\n\tminute: 3,\n\thour: 4,\n\tday: 5,\n\tstart: Date.now() + 10000 || new Date('some date in the future') //optional\n}\n```\nThe interval object supports all the time units displayed above, those can also be used in combination to create more complex intervals (e.g day + hour + second). When scheduling a task using an interval object, tempus-fugit will sync the execution cycle to the next round occurance of the interval. \n\nFor example, look at the following code:\n```js\nschedule({ hour: 1 }, function task (job) { job.done() })\n```\nIf initially run at 20:31, will execute task at 21:00, then 22:00, then 23:00 etc...\n\nIf we want to start the cycle right away we can use the optional **start** property:\n```js\nschedule({ hour: 1, start: Date.now() }, function task (job) { job.done() })\n```\nIf initially run at 20:31, will execute task at 20:31, then 21:31, 22:31 etc...\n\n##### Creating new job \"classes\"\n```js\n\tvar AbstractJob = require('tempus-fugit').AbstractJob;\n\tvar $u = require('util');\n\n\t$u.inherits(MyJob, AbstractJob);\n\tfunction MyJob(task, options) {\n\t\tAbstractJob.call(this, task, options)\n\t}\n\n\t// must implement\n\tMyJob.prototype._executeImpl = function () {\n\t\treturn setInterval(this._task, 500);\n\t};\n\n\t// must implement\n\tMyJob.prototype._cancelImpl = function(token) {\n\t\treturn clearInterval(token);\n\t};\n\n\t// optionally implement, if so, do no pass task argument in constructor\n\tMyJob.prototype._task = function () {\n\t\tconsole.log('foo!');\n\t};\n\n```\n- - -\n### Interval util\n\n##### tu.intervalObjectToMillis():\n```js\nvar tu = require('tempus-fugit').temporalUtil;\n\nvar interval = { millisecond: 500, second: 2 };\n\nconsole.log(tu.intervalObjectToMillis(interval));\n```\nwill print:\n\n\u003e 2500\n\n##### tu.normalizeIntervalObject:\n```js\nvar tu = require('tempus-fugit').tu;\n\nvar interval = { millisecond: 1502, second: 2 };\n\nconsole.log(tu.normalizeIntervalObject(interval));\n```\nwill print:\n\n\u003e { millisecond: 502, second: 3 }\n\n_note: this will modify the original interval object_\n\n##### tu.intervalCountSinceEpoch:\n```js\nvar tu = require('tempus-fugit').tu;\n\nvar interval = { day: 1 };\n\nvar n = Date.UTC(2000, 0);\n\nvar millis = tu.intervalObjectToMillis(interval);\n\nconsole.log(tu.intervalCountSinceEpoch(millis, n));\n\n```\nwill print:\n\n\u003e 10957\n\nwhich is 30 years * 365 day + 7(.5) days from leap years\n\n_note: the n argument is optional, if omitted the function will use Date.now() internally_\n\n##### tu.nextIntervalEvent:\n```js\nvar tu = require('tempus-fugit').tu;\n\nvar interval = { day: 1 };\n\nvar n = Date.UTC(2000, 0, 1, 0, 30); // Sat Jan 01 2000 00:30:00 GMT\n\nvar millis = tu.intervalObjectToMillis(interval);\n\nvar nextInterval = tu.nextIntervalEvent(millis, n);\n\nconsole.log(new Date(nextInterval).toUTCString());\n\n```\nwill print:\n\n\u003e Sun, 02 Jan 2000 00:00:00 GMT\n\n_note: the n argument is optional, if omitted the function will use Date.now() internally_\n\n\n### Date related util\n\ntu.nextSecond(date);\n\ntu.nextMinute(date);\n\ntu.nextHour(date);\n\ntu.nextDate(date);\n\ntu.nextMonth(date);\n\ntu.nextYear(date);\n\n##### example\n```js\n\tvar tf = require('tempus-fugit');\n\n\tvar now = new Date(2013, 11, 25, 23, 23, 59, 123);\n\tvar actual = tf.tu.nextSecond(now);  // tf.tu === tf.temporalUtil\n\n\tconsole.log('closest second:');\n\tconsole.log(now);\n\tconsole.log(actual);\n\n```\nwill print:\n\n\u003e Wed Dec 25 2013 23:23:59 GMT+0200 (Jerusalem Standard Time)\n\n\u003e Wed Dec 25 2013 23:24:00 GMT+0200 (Jerusalem Standard Time)\n\n### A Pitfall\nI should probably find a solution for this, but for now, if you run a SeriallyRepeatingJob like this one:\n```js\nschedule({ /*some interval data*/ }, function (job) {})\n```\nCalling job.done() will created a timer. But in this case we don't, if there are no other pending tasks in the event loop the process will exit. \n\n### TODO\n----\n- support month and year intervals, calculated correctly\n- throw exception from jobs if error event is not handled or ignore errors flag is not set\n- add more events to job\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkessler%2Ftempus-fugit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkessler%2Ftempus-fugit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkessler%2Ftempus-fugit/lists"}