Skip to content
This repository has been archived by the owner on Jun 21, 2024. It is now read-only.

Commit

Permalink
feat: time - add support for 'l' and 'D' formatting characters
Browse files Browse the repository at this point in the history
  • Loading branch information
customcommander committed Feb 15, 2020
1 parent 4296df1 commit 26a3af3
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 18 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ DIST_FILES = $(SRC_FILES:src/%=dist/%)
all: README.md
clean:; rm -rfv build dist

dist: $(DIST_FILES)
dist: $(DIST_FILES) dist/utils/l10n-data.json

dist/%.js: src/%.js
mkdir -p $(@D)
Expand All @@ -15,6 +15,10 @@ dist/%.js: src/%.js
--js=$^ \
--js_output_file=$@

dist/utils/l10n-data.json: src/utils/l10n-data.json
mkdir -p $(@D)
cp $^ $@

build/doc-data.json: $(filter-out src/index.js src/utils/%.js src/types/%.js,$(SRC_FILES))
mkdir -p $(@D)
yarn -s jsdoc -X $^ | jq -f doc/process-doc.jq >$@
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ The `@` sign links a date with a format and the format is made of formatting cha
as seen in [PHP's date function](https://www.php.net/manual/en/function.date.php).
The format is removed from the string after processing.

Only a subset of these options is supported at the moment:
Only a subset of these options is supported at the moment and English is the only supported locale.

| Character | Description |
|:----------|:------------------------------------------------|
Expand All @@ -219,6 +219,8 @@ Only a subset of these options is supported at the moment:
| n | Month. No leading zeros. e.g. "1" |
| d | Day. Two digits; leading zeros. e.g. "01" |
| j | Day. No leading zeros. e.g. "1" |
| l | Day of the week e.g. "Monday" |
| D | Day of the week, short e.g. "Mon" |

Anything that isn't a formatting character is rendered as is.

Expand Down
40 changes: 40 additions & 0 deletions l10n-data-schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"$id": "l10n-data",
"defs": {
"l10n-str": {
"type": "string",
"minLength": "1"
},
"l10n-obj": {
"type": "object",
"additionalProperties": false,
"required": [
"en"
],
"properties": {
"en": {
"$ref": "l10n-data#/defs/l10n-str"
}
}
}
},
"type": "object",
"additionalProperties": false,
"required": [
"days",
"days_short"
],
"properties": {
"days": {
"type": "array",
"minItems": 7,
"maxItems": 7,
"items": {
"$ref": "l10n-data#/defs/l10n-obj"
}
},
"days_short": {
"$ref": "l10n-data#/properties/days"
}
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
"text processing"
],
"scripts": {
"test": "make dist && tape test.js",
"test": "make dist && tape test-l10n-data.js test.js",
"doc": "make README.md"
},
"devDependencies": {
"ajv": "^6.11.0",
"jsdoc": "^3.6.3",
"mustache": "^3.2.0",
"semantic-release": "^15.14.0",
Expand Down
65 changes: 51 additions & 14 deletions src/time.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@

const tag_function = require('./utils/tag-function');

const
{ day
, day_short
} = require('./utils/l10n');

const
{ compose
, constant
Expand All @@ -38,37 +43,57 @@ const
const INVALID_DATE = String(new Date('All those moments will be lost in time, like tears in the rain.'));
const RE = /^\s*@\S+/;

// Date#getDay consider that Sunday is the start of the week.
// Maps to indexes where Monday is the start of the week.
const weekday_map =
{ 0: 6
, 1: 0
, 2: 1
, 3: 2
, 4: 3
, 5: 4
, 6: 5
};

/** @enum {string} */
const formatOptions =
{ DAY_NUM_LEADING_ZEROS: 'd'
, DAY_NUM: 'j'
, DAY_TEXT_LONG: 'l'
, DAY_TEXT_SHORT: 'D'
, MONTH_NUM_LEADING_ZEROS: 'm'
, MONTH_NUM: 'n'
, YEAR_NUM_LONG: 'Y'
, YEAR_NUM: 'y'
};

/**
* @type {Object<string, function(Date): string>}
* @type {Object<formatOptions, function(lang, Date): string>}
*/
const formatFunctions =
{ [formatOptions.DAY_NUM_LEADING_ZEROS]:
d => String(d.getDate()).padStart(2, '0')
(l, d) => String(d.getDate()).padStart(2, '0')

, [formatOptions.DAY_NUM]:
d => String(d.getDate())
(l, d) => String(d.getDate())

, [formatOptions.DAY_TEXT_LONG]:
(l, d) => day(l, weekday_map[d.getDay()])

, [formatOptions.DAY_TEXT_SHORT]:
(l, d) => day_short(l, weekday_map[d.getDay()])

, [formatOptions.MONTH_NUM_LEADING_ZEROS]:
d => String(d.getMonth() + 1).padStart(2, '0')
(l, d) => String(d.getMonth() + 1).padStart(2, '0')

, [formatOptions.MONTH_NUM]:
d => String(d.getMonth() + 1)
(l, d) => String(d.getMonth() + 1)

, [formatOptions.YEAR_NUM_LONG]:
d => String(d.getFullYear())
(l, d) => String(d.getFullYear())

, [formatOptions.YEAR_NUM]:
d => String(d.getFullYear()).slice(-2)
(l, d) => String(d.getFullYear()).slice(-2)
};

const is_date =
Expand Down Expand Up @@ -97,18 +122,28 @@ const get_format =

const clear_format = str => str.replace(RE, '');

/**
* @param {lang} l
* @param {Date} d
* @return {function(string): string}
*/
const formatter =
d =>
(l, d) =>
f =>
(formatFunctions[f] || constant(f))
(d);
(l, d);

/**
* @param {string} f formatting characters
* @param {lang} l language code
* @param {Date} d
*/
const format =
(f, d) =>
(f, l, d) =>
compose
( join('')
, map(formatter(d))
, split(/([^Yymndj]+)/)
, map(formatter(l, d))
, split(/([^YymndjlD]+)/)
)(f);

/**
Expand All @@ -129,7 +164,7 @@ const format =
* as seen in [PHP's date function](https://www.php.net/manual/en/function.date.php).
* The format is removed from the string after processing.
*
* Only a subset of these options is supported at the moment:
* Only a subset of these options is supported at the moment and English is the only supported locale.
*
* | Character | Description |
* |:----------|:------------------------------------------------|
Expand All @@ -139,6 +174,8 @@ const format =
* | n | Month. No leading zeros. e.g. "1" |
* | d | Day. Two digits; leading zeros. e.g. "01" |
* | j | Day. No leading zeros. e.g. "1" |
* | l | Day of the week e.g. "Monday" |
* | D | Day of the week, short e.g. "Mon" |
*
* Anything that isn't a formatting character is rendered as is.
*
Expand All @@ -155,7 +192,7 @@ module.exports =
[ l
, !is_date(x) || !get_format(r)
? x
: format(get_format(r), x)
: format(get_format(r), 'en', x)
, clear_format(r)
]
);
23 changes: 23 additions & 0 deletions src/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,26 @@
* Tag function.
* @typedef {function(string, ?, string, TagOptions): Array} TagFunction
*/

/**
* Position of the day in the week.
* 0..6 (Mon..Sun)
* @enum {number}
*/
const weekday =
{ 0: 0
, 1: 1
, 2: 2
, 3: 3
, 4: 4
, 5: 5
, 6: 6
};

/**
* Language code (e.g. 'en')
* @enum {string}
*/
const lang =
{ en: 'en'
};
45 changes: 45 additions & 0 deletions src/utils/l10n-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{ "days":
[ { "en": "Monday"
}
,
{ "en": "Tuesday"
}
,
{ "en": "Wednesday"
}
,
{ "en": "Thursday"
}
,
{ "en": "Friday"
}
,
{ "en": "Saturday"
}
,
{ "en": "Sunday"
}
]
, "days_short":
[ { "en": "Mon"
}
,
{ "en": "Tue"
}
,
{ "en": "Wed"
}
,
{ "en": "Thu"
}
,
{ "en": "Fri"
}
,
{ "en": "Sat"
}
,
{ "en": "Sun"
}
]
}
22 changes: 22 additions & 0 deletions src/utils/l10n.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const data = require('./l10n-data.json');

/**
* Day of the week (e.g. 'Monday')
* @param {lang} l
* @param {weekday} n
* @return {string}
*/
const day = (l, n) => data.days[n][l];

/**
* Day of the week abbreviated (e.g. 'Mon')
* @param {lang} l
* @param {weekday} n
* @return {string}
*/
const day_short = (l, n) => data.days_short[n][l];

module.exports =
{ day
, day_short
};
15 changes: 15 additions & 0 deletions test-l10n-data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const Ajv = require('ajv');
const test = require('tape');

const validate = (new Ajv).compile(require('./l10n-data-schema.json'));

test('l10n data is consistent', (t) => {
t.plan(1);
const data = require('./dist/utils/l10n-data.json');
const res = validate(data);
if (res) {
t.pass('l10n data looks ok');
} else {
t.fail(JSON.stringify(validate.errors[0], null, 2));
}
});
14 changes: 13 additions & 1 deletion test.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ test('pluralize: choose between singular or plural forms', t => {
});

test('time: format dates within a string', t => {
t.plan(3);
t.plan(5);

t.is
( time`Last login on ${new Date('2020-01-09')}@Y-m-d`
Expand All @@ -157,6 +157,18 @@ test('time: format dates within a string', t => {
, "Last login on NaN"
, 'remove date format when date is not valid'
)

t.is
( time`Last login on ${new Date('2020-02-16')}@l`
, 'Last login on Sunday'
, 'Support formatting character `l` (day of the week)'
)

t.is
( time`Last login on ${new Date('2020-02-16')}@D`
, 'Last login on Sun'
, 'Support formatting character `D` (day of the week, short)'
)
});

test('trim: trim all values', t => {
Expand Down

0 comments on commit 26a3af3

Please sign in to comment.