├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── ms.ts └── test.ts /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .DS_Store 3 | *.swp 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: sh 2 | 3 | install: 4 | - curl -L https://deno.land/x/install/install.sh | sh 5 | - export PATH="$HOME/.deno/bin:$PATH" 6 | 7 | script: 8 | - deno test --allow-net 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2016 Zeit, Inc. 4 | Copyright (c) 2018 Kevin (Kun) "Kassimo" Qian 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 7 | and associated documentation files (the 'Software'), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 16 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 19 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `ms` 2 | [![Build Status](https://travis-ci.org/denolib/ms.svg?branch=master)](https://travis-ci.org/denolib/ms) 3 | 4 | Use this package to easily convert various time formats to milliseconds. 5 | It is ported from [https://github.com/zeit/ms](https://github.com/zeit/ms) to work with [Deno](https://deno.land). Ported mainly for use in porting `debug`. 6 | 7 | ## Examples 8 | 9 | ```js 10 | import { ms } from "https://raw.githubusercontent.com/denolib/ms/master/ms.ts"; 11 | 12 | ms('2 days') // 172800000 13 | ms('1d') // 86400000 14 | ms('10h') // 36000000 15 | ms('2.5 hrs') // 9000000 16 | ms('2h') // 7200000 17 | ms('1m') // 60000 18 | ms('5s') // 5000 19 | ms('1y') // 31557600000 20 | ms('100') // 100 21 | ms('-3 days') // -259200000 22 | ms('-1h') // -3600000 23 | ms('-200') // -200 24 | ``` 25 | 26 | ### Convert from Milliseconds 27 | 28 | ```js 29 | ms(60000) // "1m" 30 | ms(2 * 60000) // "2m" 31 | ms(-3 * 60000) // "-3m" 32 | ms(ms('10 hours')) // "10h" 33 | ``` 34 | 35 | ### Time Format Written-Out 36 | 37 | ```js 38 | ms(60000, { long: true }) // "1 minute" 39 | ms(2 * 60000, { long: true }) // "2 minutes" 40 | ms(-3 * 60000, { long: true }) // "-3 minutes" 41 | ms(ms('10 hours'), { long: true }) // "10 hours" 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /ms.ts: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/zeit/ms/blob/master/index.js 2 | // Copyright (c) 2016 Zeit, Inc. MIT License 3 | // Copyright (c) 2018 Kevin "Kun" Kassimo Qian. MIT License 4 | 5 | const s = 1000; 6 | const m = s * 60; 7 | const h = m * 60; 8 | const d = h * 24; 9 | const w = d * 7; 10 | const y = d * 365.25; 11 | 12 | /** Parse or format the given `val`. 13 | * 14 | * Options: 15 | * 16 | * - `long` verbose formatting [false] 17 | */ 18 | 19 | export function ms( 20 | val: string | number, 21 | options?: { [key: string]: any }, 22 | ): string | number | undefined { 23 | switch (typeof val) { 24 | case "string": 25 | if ((val).length > 0) { 26 | return parse(val); 27 | } 28 | break; 29 | case "number": 30 | if (!isNaN(val)) { 31 | return options && options!.long ? fmtLong(val) : fmtShort(val); 32 | } 33 | } 34 | throw new Error( 35 | "val is not a non-empty string or a valid number. val=" + 36 | JSON.stringify(val), 37 | ); 38 | } 39 | 40 | /** Parse the given `str` and return milliseconds. 41 | */ 42 | 43 | function parse(str: string): number | undefined { 44 | if (str.length > 100) { 45 | return; 46 | } 47 | const match = 48 | /^((?:\d+)?-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i 49 | .exec( 50 | str, 51 | ); 52 | if (!match) { 53 | return; 54 | } 55 | const n = parseFloat(match[1]); 56 | const type = (match[2] || "ms").toLowerCase(); 57 | switch (type) { 58 | case "years": 59 | case "year": 60 | case "yrs": 61 | case "yr": 62 | case "y": 63 | return n * y; 64 | case "weeks": 65 | case "week": 66 | case "w": 67 | return n * w; 68 | case "days": 69 | case "day": 70 | case "d": 71 | return n * d; 72 | case "hours": 73 | case "hour": 74 | case "hrs": 75 | case "hr": 76 | case "h": 77 | return n * h; 78 | case "minutes": 79 | case "minute": 80 | case "mins": 81 | case "min": 82 | case "m": 83 | return n * m; 84 | case "seconds": 85 | case "second": 86 | case "secs": 87 | case "sec": 88 | case "s": 89 | return n * s; 90 | case "milliseconds": 91 | case "millisecond": 92 | case "msecs": 93 | case "msec": 94 | case "ms": 95 | return n; 96 | default: 97 | return undefined; 98 | } 99 | } 100 | 101 | /** Short format for `ms`. 102 | */ 103 | 104 | function fmtShort(ms: number): string { 105 | const msAbs = Math.abs(ms); 106 | if (msAbs >= d) { 107 | return Math.round(ms / d) + "d"; 108 | } 109 | if (msAbs >= h) { 110 | return Math.round(ms / h) + "h"; 111 | } 112 | if (msAbs >= m) { 113 | return Math.round(ms / m) + "m"; 114 | } 115 | if (msAbs >= s) { 116 | return Math.round(ms / s) + "s"; 117 | } 118 | return ms + "ms"; 119 | } 120 | 121 | /** Long format for `ms`. 122 | */ 123 | 124 | function fmtLong(ms: number): string { 125 | const msAbs = Math.abs(ms); 126 | if (msAbs >= d) { 127 | return plural(ms, msAbs, d, "day"); 128 | } 129 | if (msAbs >= h) { 130 | return plural(ms, msAbs, h, "hour"); 131 | } 132 | if (msAbs >= m) { 133 | return plural(ms, msAbs, m, "minute"); 134 | } 135 | if (msAbs >= s) { 136 | return plural(ms, msAbs, s, "second"); 137 | } 138 | return ms + " ms"; 139 | } 140 | 141 | /** Pluralization helper. 142 | */ 143 | 144 | function plural(ms: number, msAbs: number, n: number, name: string) { 145 | const isPlural = msAbs >= n * 1.5; 146 | return Math.round(ms / n) + " " + name + (isPlural ? "s" : ""); 147 | } 148 | -------------------------------------------------------------------------------- /test.ts: -------------------------------------------------------------------------------- 1 | // Adapted from https://github.com/zeit/ms/blob/master/test.js 2 | // Copyright (c) 2016 Zeit, Inc. MIT License 3 | // Copyright (c) 2018 Kevin "Kun" Kassimo Qian. MIT License 4 | import { assertEquals } from "https://deno.land/std@v0.50.0/testing/asserts.ts"; 5 | import { ms } from "./ms.ts"; 6 | 7 | // ms(string) 8 | Deno.test("preserveMs", function () { 9 | assertEquals(ms("100"), 100); 10 | }); 11 | 12 | Deno.test("mToMs", function () { 13 | assertEquals(ms("1m"), 60000); 14 | }); 15 | 16 | Deno.test("hToMs", function () { 17 | assertEquals(ms("1h"), 3600000); 18 | }); 19 | 20 | Deno.test("dToMs", function () { 21 | assertEquals(ms("2d"), 172800000); 22 | }); 23 | 24 | Deno.test("wToMs", function () { 25 | assertEquals(ms("3w"), 1814400000); 26 | }); 27 | 28 | Deno.test("sToMs", function () { 29 | assertEquals(ms("1s"), 1000); 30 | }); 31 | 32 | Deno.test("msToMs", function () { 33 | assertEquals(ms("100ms"), 100); 34 | }); 35 | 36 | Deno.test("decimals", function () { 37 | assertEquals(ms("1.5h"), 5400000); 38 | }); 39 | 40 | Deno.test("multiSpaces", function () { 41 | assertEquals(ms("1 s"), 1000); 42 | }); 43 | 44 | Deno.test("invalidRetNaN", function () { 45 | assertEquals(isNaN(ms("☃") as number), true); 46 | }); 47 | 48 | Deno.test("caseInsensitive", function () { 49 | assertEquals(ms("1.5H"), 5400000); 50 | }); 51 | 52 | Deno.test("numbersStartWithDot", function () { 53 | assertEquals(ms(".5ms"), 0.5); 54 | }); 55 | 56 | Deno.test("negativeInts", function () { 57 | assertEquals(ms("-100ms"), -100); 58 | }); 59 | 60 | Deno.test("negativeDecimals", function () { 61 | assertEquals(ms("-1.5h"), -5400000); 62 | assertEquals(ms("-10.5h"), -37800000); 63 | }); 64 | 65 | Deno.test("negativeDecimalsStartWithDot", function () { 66 | assertEquals(ms("-.5h"), -1800000); 67 | }); 68 | 69 | // ms(long string) 70 | 71 | Deno.test("millisecondsToMs", function () { 72 | assertEquals(ms("53 milliseconds"), 53); 73 | }); 74 | 75 | Deno.test("msecsToMs", function () { 76 | assertEquals(ms("17 msecs"), 17); 77 | }); 78 | 79 | Deno.test("secToMs", function () { 80 | assertEquals(ms("1 sec"), 1000); 81 | }); 82 | 83 | Deno.test("minToMs", function () { 84 | assertEquals(ms("1 min"), 60000); 85 | }); 86 | 87 | Deno.test("hrToMs", function () { 88 | assertEquals(ms("1 hr"), 3600000); 89 | }); 90 | 91 | Deno.test("daysToMs", function () { 92 | assertEquals(ms("2 days"), 172800000); 93 | }); 94 | 95 | Deno.test("longDecimals", function () { 96 | assertEquals(ms("1.5 hours"), 5400000); 97 | }); 98 | 99 | Deno.test("longNegativeIntegers", function () { 100 | assertEquals(ms("-100 milliseconds"), -100); 101 | }); 102 | 103 | Deno.test("longNegativeDecimals", function () { 104 | assertEquals(ms("-1.5 hours"), -5400000); 105 | }); 106 | 107 | Deno.test("negativeDecimalsStartWithDot", function () { 108 | assertEquals(ms("-.5 hr"), -1800000); 109 | }); 110 | 111 | // ms(number, { long: true }) 112 | Deno.test("longSupportMilliseconds", function () { 113 | assertEquals(ms(500, { long: true }), "500 ms"); 114 | assertEquals(ms(-500, { long: true }), "-500 ms"); 115 | }); 116 | 117 | Deno.test("longSupportSeconds", function () { 118 | assertEquals(ms(1000, { long: true }), "1 second"); 119 | assertEquals(ms(1200, { long: true }), "1 second"); 120 | assertEquals(ms(10000, { long: true }), "10 seconds"); 121 | 122 | assertEquals(ms(-1000, { long: true }), "-1 second"); 123 | assertEquals(ms(-1200, { long: true }), "-1 second"); 124 | assertEquals(ms(-10000, { long: true }), "-10 seconds"); 125 | }); 126 | 127 | Deno.test("longSupportMinutes", function () { 128 | assertEquals(ms(60 * 1000, { long: true }), "1 minute"); 129 | assertEquals(ms(60 * 1200, { long: true }), "1 minute"); 130 | assertEquals(ms(60 * 10000, { long: true }), "10 minutes"); 131 | 132 | assertEquals(ms(-1 * 60 * 1000, { long: true }), "-1 minute"); 133 | assertEquals(ms(-1 * 60 * 1200, { long: true }), "-1 minute"); 134 | assertEquals(ms(-1 * 60 * 10000, { long: true }), "-10 minutes"); 135 | }); 136 | 137 | Deno.test("longSupportHours", function () { 138 | assertEquals(ms(60 * 60 * 1000, { long: true }), "1 hour"); 139 | assertEquals(ms(60 * 60 * 1200, { long: true }), "1 hour"); 140 | assertEquals(ms(60 * 60 * 10000, { long: true }), "10 hours"); 141 | 142 | assertEquals(ms(-1 * 60 * 60 * 1000, { long: true }), "-1 hour"); 143 | assertEquals(ms(-1 * 60 * 60 * 1200, { long: true }), "-1 hour"); 144 | assertEquals(ms(-1 * 60 * 60 * 10000, { long: true }), "-10 hours"); 145 | }); 146 | 147 | Deno.test("longSupportDays", function () { 148 | assertEquals(ms(24 * 60 * 60 * 1000, { long: true }), "1 day"); 149 | assertEquals(ms(24 * 60 * 60 * 1200, { long: true }), "1 day"); 150 | assertEquals(ms(24 * 60 * 60 * 10000, { long: true }), "10 days"); 151 | 152 | assertEquals(ms(-1 * 24 * 60 * 60 * 1000, { long: true }), "-1 day"); 153 | assertEquals(ms(-1 * 24 * 60 * 60 * 1200, { long: true }), "-1 day"); 154 | assertEquals(ms(-1 * 24 * 60 * 60 * 10000, { long: true }), "-10 days"); 155 | }); 156 | 157 | Deno.test("longShouldRound", function () { 158 | assertEquals(ms(234234234, { long: true }), "3 days"); 159 | 160 | assertEquals(ms(-234234234, { long: true }), "-3 days"); 161 | }); 162 | 163 | // ms(number) 164 | Deno.test("supportMilliseconds", function () { 165 | assertEquals(ms(500), "500ms"); 166 | 167 | assertEquals(ms(-500), "-500ms"); 168 | }); 169 | 170 | Deno.test("supportSeconds", function () { 171 | assertEquals(ms(1000), "1s"); 172 | assertEquals(ms(10000), "10s"); 173 | 174 | assertEquals(ms(-1000), "-1s"); 175 | assertEquals(ms(-10000), "-10s"); 176 | }); 177 | 178 | Deno.test("supportMinutes", function () { 179 | assertEquals(ms(60 * 1000), "1m"); 180 | assertEquals(ms(60 * 10000), "10m"); 181 | 182 | assertEquals(ms(-1 * 60 * 1000), "-1m"); 183 | assertEquals(ms(-1 * 60 * 10000), "-10m"); 184 | }); 185 | 186 | Deno.test("supportHours", function () { 187 | assertEquals(ms(60 * 60 * 1000), "1h"); 188 | assertEquals(ms(60 * 60 * 10000), "10h"); 189 | 190 | assertEquals(ms(-1 * 60 * 60 * 1000), "-1h"); 191 | assertEquals(ms(-1 * 60 * 60 * 10000), "-10h"); 192 | }); 193 | 194 | Deno.test("supportDays", function () { 195 | assertEquals(ms(24 * 60 * 60 * 1000), "1d"); 196 | assertEquals(ms(24 * 60 * 60 * 10000), "10d"); 197 | 198 | assertEquals(ms(-1 * 24 * 60 * 60 * 1000), "-1d"); 199 | assertEquals(ms(-1 * 24 * 60 * 60 * 10000), "-10d"); 200 | }); 201 | 202 | Deno.test("shouldRound", function () { 203 | assertEquals(ms(234234234), "3d"); 204 | 205 | assertEquals(ms(-234234234), "-3d"); 206 | }); 207 | 208 | // ms(invalid inputs) 209 | Deno.test("invalidInputs", function () { 210 | let errCount = 0; 211 | try { 212 | // @ts-ignore 213 | ms(undefined); 214 | } catch (e) { 215 | errCount++; 216 | } 217 | try { 218 | // @ts-ignore 219 | ms(null); 220 | } catch (e) { 221 | errCount++; 222 | } 223 | try { 224 | // @ts-ignore 225 | ms([]); 226 | } catch (e) { 227 | errCount++; 228 | } 229 | try { 230 | // @ts-ignore 231 | ms({}); 232 | } catch (e) { 233 | errCount++; 234 | } 235 | try { 236 | // @ts-ignore 237 | ms(NaN); 238 | } catch (e) { 239 | errCount++; 240 | } 241 | assertEquals(errCount, 5); 242 | }); 243 | --------------------------------------------------------------------------------