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

Commit

Permalink
feat: implement tag time to format dates within a string
Browse files Browse the repository at this point in the history
  • Loading branch information
customcommander committed Jan 10, 2020
1 parent 535ac92 commit 7643979
Show file tree
Hide file tree
Showing 13 changed files with 395 additions and 19 deletions.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ lower`I only had ${num} ${food}!`;
* [hide](#hide) - _Hides interpolated values_
* [lower](#lower) - _Lowercase interpolated values_
* [pluralize](#pluralize) - _Choose between singular or plural forms._
* [time](#time) - _Format dates within a string._
* [trim](#trim) - _Trim interpolated values_
* [upper](#upper) - _Uppercase interpolated values_

Expand Down Expand Up @@ -198,6 +199,48 @@ the `pluralize` tag __won't__ perform any replacement on the adjacent text!

_(The `pluralize` tag does not have any options.)_

### time


The `time` tag formats all interpolated dates according to a given format.

```javascript
import {time} from '@customcommander/tagtical';
time`Last login on ${date}@Y-m-d`;
//=> "Last login on 2020-01-09"
```

The format is attached to the date as follow `${date}@Y-m-d`.

The `@` sign links a date with a format and the format is made of formatting characters
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:
| Character | Description |
|:----------|:------------------------------------------------|
| Y | Year. Four digits |
| y | Year. Two digits |
| m | Month. Two digits with leading zeros. e.g. "01" |
| 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" |
Anything that isn't a formatting character is rendered as is.

When an interpolated value isn't a date, the value is rendered as is and the date format is removed.
```javascript
time`Last login on ${0/0}@Y-m-d`;
//=> Last login on NaN
```
_(The `time` tag does not have any options.)_
### trim
Expand Down
9 changes: 7 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
const tag_function = require('./utils/tag-function');
const use_tag = require('./utils/use-tag');
const join = require('./utils/join');
const intersperse = require('./utils/intersperse');
const compose = require('./utils/compose');
const defaults = require('./defaults');
const hide = require('./hide');
const lower = require('./lower');
const pluralize = require('./pluralize');
const time = require('./time');
const trim = require('./trim');
const upper = require('./upper');

const
{ compose
, join
} = require('./utils/fp');

const tag = (...fns) =>
(strs, ...vals) =>
compose(join(''), ...fns.map(use_tag.unwrap))
Expand All @@ -19,6 +23,7 @@ tag.defaults = use_tag(defaults);
tag.hide = use_tag(hide);
tag.lower = use_tag(lower);
tag.pluralize = use_tag(pluralize);
tag.time = use_tag(time);
tag.trim = use_tag(trim);
tag.upper = use_tag(upper);
tag.of = compose(use_tag, tag_function);
Expand Down
161 changes: 161 additions & 0 deletions src/time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* @license
* Copyright (c) 2020 Julien Gonzalez
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

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

const
{ compose
, constant
, fallback
, join
, map
, match
, split
, tail
, trim
} = require('./utils/fp');

const INVALID_DATE = String(new Date('All those moments will be lost in time, like tears in the rain.'));
const RE = /^\s*@\S+/;

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

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

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

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

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

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

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

const is_date =
x =>
x instanceof Date
&& String(x) !== INVALID_DATE;

/**
* Extract a date format from a string
*
* @example
* get_format('@Y-m-d some extraneous text');
* //=> "Y-m-d"
*
* @function
* @param {string} str
* @return {string}
*/
const get_format =
compose
( tail
, trim
, fallback('')
, match(RE)
);

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

const formatter =
d =>
f =>
(formatFunctions[f] || constant(f))
(d);

const format =
(f, d) =>
compose
( join('')
, map(formatter(d))
, split(/([^Yymndj]+)/)
)(f);

/**
* Format dates within a string.
*
* The `time` tag formats all interpolated dates according to a given format.
*
* ```javascript
* import {time} from '@customcommander/tagtical';
*
* time`Last login on ${date}@Y-m-d`;
* //=> "Last login on 2020-01-09"
* ```
*
* The format is attached to the date as follow `${date}@Y-m-d`.
*
* The `@` sign links a date with a format and the format is made of formatting characters
* 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:
*
* | Character | Description |
* |:----------|:------------------------------------------------|
* | Y | Year. Four digits |
* | y | Year. Two digits |
* | m | Month. Two digits with leading zeros. e.g. "01" |
* | 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" |
*
* Anything that isn't a formatting character is rendered as is.
*
* When an interpolated value isn't a date, the value is rendered as is and the date format is removed.
*
* ```javascript
* time`Last login on ${0/0}@Y-m-d`;
* //=> Last login on NaN
* ```
*/
module.exports =
tag_function
( (l, x, r) =>
[ l
, !is_date(x) || !get_format(r)
? x
: format(get_format(r), x)
, clear_format(r)
]
);
9 changes: 0 additions & 9 deletions src/utils/compose.js

This file was deleted.

1 change: 0 additions & 1 deletion src/utils/cont.js

This file was deleted.

Loading

0 comments on commit 7643979

Please sign in to comment.