-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #11 from Souler/refactor/drop-moment-dependency
Remove moment as a dependency
- Loading branch information
Showing
4 changed files
with
203 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
const util = require('util'); | ||
|
||
/** | ||
* Pads the provided number into a string of 2 digits; adding leading | ||
* zeros if needed. | ||
* Values resulting in more than 2 digits are left untouched. | ||
* | ||
* @param {string} number | ||
* @returns A two-digit number | ||
* @throws {TypeError} if the provided value is not a valid integer | ||
*/ | ||
function toTwoDigits(number) { | ||
if (!Number.isInteger(number)) { | ||
throw new TypeError(`Not a valid integer: ${number}`); | ||
} | ||
|
||
return number.toString().padStart(2, '0'); | ||
} | ||
|
||
/** | ||
* Look-up map of month number into month short name (in english). | ||
* A month number is the value returned by Date#getMonth() and a short month name | ||
* is a 3 letter representation of a month of the year. | ||
*/ | ||
const shortMonthByMonthNumber = { | ||
0: 'Jan', | ||
1: 'Feb', | ||
2: 'Mar', | ||
3: 'Apr', | ||
4: 'May', | ||
5: 'Jun', | ||
6: 'Jul', | ||
7: 'Aug', | ||
8: 'Sep', | ||
9: 'Oct', | ||
10: 'Nov', | ||
11: 'Dec' | ||
}; | ||
|
||
/** | ||
* Returns a [short](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat#month) | ||
* (3-letters) representation of the given month index **in English**. | ||
* | ||
* @param {number} month | ||
* @returns {string} | ||
* @throws {TypeError} if the provided value is not a valid month number | ||
*/ | ||
function toShortMonth(month) { | ||
if (!(month in shortMonthByMonthNumber)) { | ||
throw new TypeError(`Not a valid month value: ${month}`); | ||
} | ||
|
||
return shortMonthByMonthNumber[month]; | ||
} | ||
|
||
/** | ||
* Returns a [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) compliant | ||
* offset string. | ||
* | ||
* @returns {string} | ||
* @throws {TypeError} if the provided value is not a valid integer | ||
*/ | ||
function toOffset(offsetMinutes) { | ||
if (!Number.isInteger(offsetMinutes)) { | ||
throw new TypeError(`Not a valid integer: ${offsetMinutes}`); | ||
} | ||
|
||
const absoluteOffset = Math.abs(offsetMinutes); | ||
const hours = toTwoDigits(Math.floor(absoluteOffset / 60)); | ||
const minutes = toTwoDigits(absoluteOffset % 60); | ||
const sign = offsetMinutes >= 0 ? '-' : '+'; | ||
|
||
return `${sign}${hours}${minutes}`; | ||
} | ||
|
||
/** | ||
* Formats the provided date into a [Common Log Format](https://en.wikipedia.org/wiki/Common_Log_Format) | ||
* compliant string. | ||
* | ||
* @param {Date} date | ||
* @returns {string} | ||
*/ | ||
function toCommonAccessLogDateFormat(date) { | ||
if (!(date instanceof Date)) { | ||
throw new TypeError('Not a valid date'); | ||
} | ||
|
||
// e.g: 10/Oct/2000:13:55:36 -0700 | ||
return util.format( | ||
'%s/%s/%s:%s:%s:%s %s', | ||
toTwoDigits(date.getDate()), | ||
toShortMonth(date.getMonth()), | ||
date.getFullYear(), | ||
toTwoDigits(date.getHours()), | ||
toTwoDigits(date.getMinutes()), | ||
toTwoDigits(date.getSeconds()), | ||
toOffset(date.getTimezoneOffset()) | ||
); | ||
} | ||
|
||
module.exports = { | ||
toCommonAccessLogDateFormat, | ||
toOffset, | ||
toShortMonth, | ||
toTwoDigits | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,9 +30,6 @@ | |
"3imed-jaberi <[email protected]> (https://www.3imed-jaberi.com)" | ||
], | ||
"license": "MIT", | ||
"dependencies": { | ||
"moment": "^2.26.0" | ||
}, | ||
"devDependencies": { | ||
"eslint-config-xo-lass": "^1.0.3", | ||
"koa": "^2.12.1", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
const { | ||
toCommonAccessLogDateFormat, | ||
toOffset, | ||
toShortMonth, | ||
toTwoDigits, | ||
} = require('../format') | ||
|
||
describe('toTwoDigits', () => { | ||
test.each([ | ||
{ value: 0, expected: '00' }, | ||
{ value: 1, expected: '01' }, | ||
{ value: 11, expected: '11' }, | ||
{ value: 99, expected: '99' }, | ||
{ value: 123, expected: '123' }, | ||
])('returns $expected for $value', ({ value, expected }) => { | ||
expect(toTwoDigits(value)).toBe(expected) | ||
}) | ||
|
||
test('throws TypeError if provided value is Infinity', () => { | ||
expect(() => toTwoDigits(Infinity)).toThrow(TypeError) | ||
}) | ||
|
||
test('throws TypeError if provided value is NaN', () => { | ||
expect(() => toTwoDigits(NaN)).toThrow(TypeError) | ||
}) | ||
|
||
test('throws TypeError if provided value is a decimal number', () => { | ||
expect(() => toTwoDigits(0.99)).toThrow(TypeError) | ||
}) | ||
|
||
test('throws TypeError if provided value is a string', () => { | ||
expect(() => toTwoDigits('0')).toThrow(TypeError) | ||
}) | ||
}) | ||
|
||
describe('toOffset', () => { | ||
test.each([ | ||
{ value: 480, expected: '-0800' }, | ||
{ value: 0, expected: '+0000' }, | ||
{ value: -180, expected: '+0300' }, | ||
])('returns $expected for $value', ({ value, expected }) => { | ||
expect(toOffset(value)).toBe(expected) | ||
}) | ||
|
||
test('throws TypeError if provided value is Infinity', () => { | ||
expect(() => toOffset(Infinity)).toThrow(TypeError) | ||
}) | ||
|
||
test('throws TypeError if provided value is NaN', () => { | ||
expect(() => toOffset(NaN)).toThrow(TypeError) | ||
}) | ||
|
||
test('throws TypeError if provided value is a decimal number', () => { | ||
expect(() => toOffset(60.5)).toThrow(TypeError) | ||
}) | ||
|
||
test('throws TypeError if provided value is a string', () => { | ||
expect(() => toOffset('60')).toThrow(TypeError) | ||
}) | ||
}) | ||
|
||
describe('toShortMonth', () => { | ||
test.each([ | ||
{ value: 0, expected: 'Jan' }, | ||
{ value: 1, expected: 'Feb' }, | ||
{ value: 2, expected: 'Mar' }, | ||
{ value: 3, expected: 'Apr' }, | ||
{ value: 4, expected: 'May' }, | ||
{ value: 5, expected: 'Jun' }, | ||
{ value: 6, expected: 'Jul' }, | ||
{ value: 7, expected: 'Aug' }, | ||
{ value: 8, expected: 'Sep' }, | ||
{ value: 9, expected: 'Oct' }, | ||
{ value: 10, expected: 'Nov' }, | ||
{ value: 11, expected: 'Dec' }, | ||
])('returns $expected for $value', ({ value, expected }) => { | ||
expect(toShortMonth(value)).toBe(expected) | ||
}) | ||
|
||
test('throws TypeError for a non-valid month number', () => { | ||
expect(() => toShortMonth(13)).toThrow(TypeError) | ||
}) | ||
}) | ||
|
||
describe('toCommonAccessLogDateFormat', () => { | ||
test('correctly formats a date', () => { | ||
const date = new Date('2020-01-01T12:34:56Z') | ||
const expectedValue = '01/Jan/2020:10:34:56 +0200' | ||
expect(toCommonAccessLogDateFormat(date)).toBe(expectedValue) | ||
}) | ||
|
||
test('throws TypeError for non-Date value', () => { | ||
expect(() => toCommonAccessLogDateFormat({})).toThrow(TypeError) | ||
}) | ||
}) |