├── .editorconfig
├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __tests__
└── index.js
├── index.d.ts
├── index.js
├── package-lock.json
└── package.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | # default
4 | [*]
5 | charset = utf-8
6 | end_of_line = lf
7 | indent_size = 2
8 | indent_style = space
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | build:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | strategy:
13 | matrix:
14 | node-version:
15 | - 8.x
16 | - 10.x
17 |
18 | steps:
19 | - uses: actions/checkout@v3
20 | - name: Use Node.js ${{ matrix.node-version }}
21 | uses: actions/setup-node@v3
22 | with:
23 | node-version: ${{ matrix.node-version }}
24 | cache: npm
25 | - run: npm ci
26 | - run: npm test
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /node_modules/
3 | /coverage/
4 | /dist/
5 | npm-debug.log
6 | yarn-debug.log
7 | yarn-error.log
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 | package-lock.json
3 | yarn.lock
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "singleQuote": true
4 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ### [2.1.3](https://github.com/vutran/twas/compare/v2.1.2...v2.1.3) (2023-02-08)
6 |
7 | ### [2.1.2](https://github.com/vutran/twas/compare/v2.1.1...v2.1.2) (2020-12-01)
8 |
9 | ### [2.1.1](https://github.com/vutran/twas/compare/v2.1.0...v2.1.1) (2019-08-02)
10 |
11 |
12 | # [2.1.0](https://github.com/vutran/twas/compare/v2.0.3...v2.1.0) (2019-02-13)
13 |
14 |
15 | ### Bug Fixes
16 |
17 | * **ci:** replace node 7 -> 10 ([4246ecf](https://github.com/vutran/twas/commit/4246ecf))
18 |
19 |
20 | ### Features
21 |
22 | * Add standard-version ([ded4dc9](https://github.com/vutran/twas/commit/ded4dc9))
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Vu Tran
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # twas
2 |
3 | [](https://coveralls.io/github/vutran/twas) [](LICENSE)
4 |
5 | > Tiny (280B) relative time string function (eg: "3 seconds ago")
6 |
7 | ## Install
8 |
9 | ```bash
10 | $ npm i -S twas
11 | ```
12 |
13 | ## Usage
14 |
15 | ```js
16 | import twas from 'twas';
17 |
18 | console.log(twas(Date.now() - (5 * 1000));
19 |
20 | // -> 5 seconds ago
21 | ```
22 |
23 | ## License
24 |
25 | MIT © [Vu Tran](https://github.com/vutran)
26 |
--------------------------------------------------------------------------------
/__tests__/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @jest-environment node
3 | */
4 |
5 | const twas = require('../dist');
6 |
7 | describe('twas', () => {
8 | // now -- 2018-01-24T17:51:30+00:00 (ISO 8601)
9 | const NOW = 1516816290;
10 | const SECONDS = 1000;
11 | const MINUTES = SECONDS * 60;
12 | const HOURS = MINUTES * 60;
13 | const DAYS = HOURS * 24;
14 | const WEEKS = DAYS * 7;
15 | const MONTHS = DAYS * 30;
16 | const YEARS = MONTHS * 12;
17 |
18 | // overrides Date.now()
19 | Date.now = jest.fn(() => NOW);
20 |
21 | it('should mock Date.now()', () => {
22 | expect(Date.now()).toBe(NOW);
23 | });
24 |
25 | it('should be "just now"', () => {
26 | expect(twas(NOW)).toBe('just now');
27 | expect(twas(NOW - 1)).toBe('just now');
28 | });
29 |
30 | it('should be "5 seconds ago"', () => {
31 | expect(twas(NOW - 5 * SECONDS)).toBe('5 seconds ago');
32 | });
33 |
34 | it('should be "32 seconds ago"', () => {
35 | expect(twas(NOW - 32 * SECONDS)).toBe('32 seconds ago');
36 | });
37 |
38 | it('should be "a minute ago"', () => {
39 | expect(twas(NOW - 1 * MINUTES)).toBe('a minute ago');
40 | });
41 |
42 | it('should be "5 minutes ago"', () => {
43 | expect(twas(NOW - 5 * MINUTES)).toBe('5 minutes ago');
44 | });
45 |
46 | it('should be "15 minutes ago"', () => {
47 | expect(twas(NOW - 15 * MINUTES)).toBe('15 minutes ago');
48 | });
49 |
50 | it('should be "an hour ago"', () => {
51 | expect(twas(NOW - 1 * HOURS)).toBe('an hour ago');
52 | });
53 |
54 | it('should be "2 hours ago"', () => {
55 | expect(twas(NOW - 2 * HOURS)).toBe('2 hours ago');
56 | });
57 |
58 | it('should be "3 days ago"', () => {
59 | expect(twas(NOW - 3 * DAYS)).toBe('3 days ago');
60 | });
61 |
62 | it('should be "4 weeks ago"', () => {
63 | expect(twas(NOW - 4 * WEEKS)).toBe('4 weeks ago');
64 | });
65 |
66 | it('should be "5 months ago"', () => {
67 | expect(twas(NOW - 5 * MONTHS)).toBe('5 months ago');
68 | });
69 |
70 | it('should be "6 years ago"', () => {
71 | expect(twas(NOW - 6 * YEARS)).toBe('6 years ago');
72 | });
73 |
74 | it('should be "7 years ago"', () => {
75 | expect(twas(NOW - 6 * YEARS - 11 * MONTHS)).toBe('7 years ago');
76 | });
77 |
78 | it('should be 10 seconds ago', () => {
79 | const n = 1514764800; // Monday, January 1, 2018 12:00:00 AM
80 | expect(twas(n - 10 * SECONDS, n)).toBe('10 seconds ago');
81 | });
82 | });
83 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'twas' {
2 | export default function(time: number, now?: number): string;
3 | }
4 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const is = (interval, cycle) =>
2 | cycle >= interval ? Math.round(cycle / interval) : 0;
3 |
4 | export default function(time, now) {
5 | if (!now) {
6 | now = Date.now();
7 | }
8 | const secs = (now - time) / 1000;
9 | const mins = is(60, secs);
10 | const hours = is(60, mins);
11 | const days = is(24, hours);
12 | const weeks = is(7, days);
13 | const months = is(30, days);
14 | const years = is(12, months);
15 |
16 | let amt = years;
17 | let cycle = 'year';
18 |
19 | if (secs <= 1) {
20 | return 'just now';
21 | } else if (years > 0) {
22 | amt = years;
23 | cycle = 'year';
24 | } else if (months > 0) {
25 | amt = months;
26 | cycle = 'month';
27 | } else if (weeks > 0) {
28 | amt = weeks;
29 | cycle = 'week';
30 | } else if (days > 0) {
31 | amt = days;
32 | cycle = 'day';
33 | } else if (hours > 0) {
34 | amt = hours;
35 | cycle = 'hour';
36 | } else if (mins > 0) {
37 | amt = mins;
38 | cycle = 'minute';
39 | } else if (secs > 0) {
40 | amt = secs;
41 | cycle = 'second';
42 | }
43 |
44 | const v = Math.round(amt);
45 |
46 | return `${v === 1 ? (amt === hours ? 'an' : 'a') : v} ${cycle}${v > 1 ? 's' : ''} ago`;
47 | };
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "twas",
3 | "version": "2.1.3",
4 | "description": "Generate time ago string",
5 | "umd:main": "dist/index.min.js",
6 | "module": "dist/index.es.js",
7 | "main": "dist/index.js",
8 | "engines": {
9 | "node": ">=8.9.3"
10 | },
11 | "files": [
12 | "dist",
13 | "index.d.ts"
14 | ],
15 | "repository": {
16 | "type": "git",
17 | "url": "git@github.com:vutran/twas.git"
18 | },
19 | "dependencies": {},
20 | "devDependencies": {
21 | "coveralls": "^3.0.5",
22 | "jest": "^24.8.0",
23 | "microbundle": "^0.11.0",
24 | "standard-version": "^7.0.0"
25 | },
26 | "scripts": {
27 | "build": "microbundle",
28 | "jest": "jest --coverage",
29 | "prepublish": "npm run build",
30 | "release": "standard-version",
31 | "pretest": "npm run build",
32 | "test": "npm run jest"
33 | },
34 | "keywords": [
35 | "timeago",
36 | "time-ago",
37 | "time ago",
38 | "time",
39 | "date",
40 | "from-now",
41 | "fromnow",
42 | "from now",
43 | "relative",
44 | "moment"
45 | ],
46 | "author": "Vu Tran ",
47 | "license": "MIT"
48 | }
49 |
--------------------------------------------------------------------------------