@parischap/conversions
Version:
A functional library to replace partially the native Intl API
316 lines • 11.2 kB
JavaScript
/**
* This module implements a `CVDateFormatContext` which is used when parsing or formatting dates
* (see DateTimeFormat.ts) to provide the translations used by language specific tokens (e.g.
* `MMMM`)
*/
import * as MArray from '@parischap/effect-lib/MArray';
import * as MInputError from '@parischap/effect-lib/MInputError';
import * as MInspectable from '@parischap/effect-lib/MInspectable';
import * as MPipeable from '@parischap/effect-lib/MPipeable';
import * as MPredicate from '@parischap/effect-lib/MPredicate';
import * as MString from '@parischap/effect-lib/MString';
import * as MTypes from '@parischap/effect-lib/MTypes';
import * as Array from 'effect/Array';
import * as Either from 'effect/Either';
import {flow} from 'effect/Function';
import * as HashMap from 'effect/HashMap';
import * as Number from 'effect/Number';
import * as Option from 'effect/Option';
import {pipe} from 'effect/Function';
import * as Predicate from 'effect/Predicate';
import * as Struct from 'effect/Struct';
import * as Tuple from 'effect/Tuple';
import * as CVDateTime from './DateTime.js';
import * as CVNumberBase10Format from './NumberBase10Format.js';
import * as CVReal from './Real.js';
import * as CVTemplatePlaceholder from './TemplatePlaceholder.js';
/**
* Module tag
*
* @category Module markers
*/
export const moduleTag = '@parischap/conversions/DateTimeFormatTemplate/';
const _TypeId = /*#__PURE__*/Symbol.for(moduleTag);
const WEEKDAY_DATES = /*#__PURE__*/pipe(7, /*#__PURE__*/Array.makeBy(/*#__PURE__*/flow(/*#__PURE__*/Number.multiply(CVDateTime.DAY_MS), /*#__PURE__*/Number.sum(4 * CVDateTime.DAY_MS))), /*#__PURE__*/Array.map(timestamp => new Date(timestamp)));
const MONTH_DATES = /*#__PURE__*/pipe(12, /*#__PURE__*/Array.makeBy(/*#__PURE__*/Number.multiply(31 * CVDateTime.DAY_MS)), /*#__PURE__*/Array.map(timestamp => pipe(new Date(timestamp))));
/**
* Type guard
*
* @category Guards
*/
export const has = u => Predicate.hasProperty(u, _TypeId);
/** Prototype */
const proto = {
[_TypeId]: _TypeId,
[MInspectable.IdSymbol]() {
return this.name;
},
... /*#__PURE__*/MInspectable.BaseProto(moduleTag),
...MPipeable.BaseProto
};
const _make = params => MTypes.objectFromDataAndProto(proto, params);
/**
* Constructs a `CVDateTimeFormatContext` from translations provided as strings
*
* @category Constructors
*/
export const fromNames = ({
name,
shortWeekdayNames,
longWeekdayNames,
shortMonthNames,
longMonthNames,
dayPeriodNames
}) => {
const integer = CVNumberBase10Format.integer;
const signedInteger = pipe(integer, CVNumberBase10Format.withSignDisplay);
const params = {
fillChar: '0',
numberBase10Format: integer
};
const templatepartEntries = [['y', CVTemplatePlaceholder.real({
...params,
name: 'year'
})], ['yy', pipe(CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'year',
length: 2
}), CVTemplatePlaceholder.modify({
descriptorMapper: MString.append(' between 2000 and 2099 included'),
postParser: function (value) {
return pipe(value, Number.sum(2000), MInputError.assertInRange({
min: 2000,
max: 2099,
minIncluded: true,
maxIncluded: true,
offset: -2000,
name: this.label
}), Either.map(CVReal.unsafeFromNumber));
},
preFormatter: function (value) {
return pipe(value, MInputError.assertInRange({
min: 2000,
max: 2099,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: this.label
}), Either.map(flow(Number.subtract(2000), CVReal.unsafeFromNumber)));
}
}))], ['yyyy', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'year',
length: 4
})], ['R', CVTemplatePlaceholder.real({
...params,
name: 'isoYear'
})], ['RR', pipe(CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'isoYear',
length: 2
}), CVTemplatePlaceholder.modify({
descriptorMapper: MString.append(' between 2000 and 2099 included'),
postParser: function (value) {
return pipe(value, Number.sum(2000), MInputError.assertInRange({
min: 2000,
max: 2099,
minIncluded: true,
maxIncluded: true,
offset: -2000,
name: this.label
}), Either.map(CVReal.unsafeFromNumber));
},
preFormatter: function (value) {
return pipe(value, MInputError.assertInRange({
min: 2000,
max: 2099,
minIncluded: true,
maxIncluded: true,
offset: 0,
name: this.label
}), Either.map(flow(Number.subtract(2000), CVReal.unsafeFromNumber)));
}
}))], ['RRRR', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'isoYear',
length: 4
})], ['M', CVTemplatePlaceholder.real({
...params,
name: 'month'
})], ['MM', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'month',
length: 2
})], ['MMM', CVTemplatePlaceholder.realMappedLiterals({
name: 'month',
keyValuePairs: pipe(shortMonthNames, Array.map((name, i) => Tuple.make(name, CVReal.unsafeFromNumber(i + 1))))
})], ['MMMM', CVTemplatePlaceholder.realMappedLiterals({
name: 'month',
keyValuePairs: pipe(longMonthNames, Array.map((name, i) => Tuple.make(name, CVReal.unsafeFromNumber(i + 1))))
})], ['I', CVTemplatePlaceholder.real({
name: 'isoWeek',
numberBase10Format: CVNumberBase10Format.integer
})], ['II', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'isoWeek',
length: 2
})], ['d', CVTemplatePlaceholder.real({
...params,
name: 'monthDay'
})], ['dd', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'monthDay',
length: 2
})], ['D', CVTemplatePlaceholder.real({
...params,
name: 'ordinalDay'
})], ['DDD', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'ordinalDay',
length: 3
})], ['i', CVTemplatePlaceholder.real({
...params,
name: 'weekday'
})], ['iii', CVTemplatePlaceholder.realMappedLiterals({
name: 'weekday',
keyValuePairs: pipe(shortWeekdayNames, Array.map((name, i) => Tuple.make(name, CVReal.unsafeFromNumber(i + 1))))
})], ['iiii', CVTemplatePlaceholder.realMappedLiterals({
name: 'weekday',
keyValuePairs: pipe(longWeekdayNames, Array.map((name, i) => Tuple.make(name, CVReal.unsafeFromNumber(i + 1))))
})], ['a', CVTemplatePlaceholder.realMappedLiterals({
name: 'meridiem',
keyValuePairs: pipe(dayPeriodNames, Array.map((name, i) => Tuple.make(name, CVReal.unsafeFromNumber(i * 12))))
})], ['H', CVTemplatePlaceholder.real({
...params,
name: 'hour23'
})], ['HH', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'hour23',
length: 2
})], ['K', CVTemplatePlaceholder.real({
...params,
name: 'hour11'
})], ['KK', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'hour11',
length: 2
})], ['m', CVTemplatePlaceholder.real({
...params,
name: 'minute'
})], ['mm', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'minute',
length: 2
})], ['s', CVTemplatePlaceholder.real({
...params,
name: 'second'
})], ['ss', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'second',
length: 2
})], ['S', CVTemplatePlaceholder.real({
...params,
name: 'millisecond'
})], ['SSS', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'millisecond',
length: 3
})], ['zH', CVTemplatePlaceholder.real({
...params,
name: 'zoneHour',
numberBase10Format: signedInteger
})], ['zHzH', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'zoneHour',
length: 3,
numberBase10Format: signedInteger
})], ['zm', CVTemplatePlaceholder.real({
...params,
name: 'zoneMinute'
})], ['zmzm', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'zoneMinute',
length: 2
})], ['zs', CVTemplatePlaceholder.real({
...params,
name: 'zoneSecond'
})], ['zszs', CVTemplatePlaceholder.fixedLengthToReal({
...params,
name: 'zoneSecond',
length: 2
})]];
return _make({
name,
tokenMap: HashMap.make(...templatepartEntries)
});
};
/**
* `CVDateTimeFormatContext` instance for Great-Britain English language
*
* @category Instances
*/
export const enGB = /*#__PURE__*/fromNames({
name: 'en-GB',
longWeekdayNames: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
shortWeekdayNames: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
longMonthNames: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
shortMonthNames: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
dayPeriodNames: ['AM', 'PM']
});
const _safeDateTimeFormat = /*#__PURE__*/Option.liftThrowable(Intl.DateTimeFormat);
const _extractType = type => flow(Array.findFirst(flow(Struct.get('type'), MPredicate.strictEquals(type))), Option.map(Struct.get('value')));
const _extractWeekday = /*#__PURE__*/_extractType('weekday');
const _extractMonth = /*#__PURE__*/_extractType('month');
/**
* Tries to build a `CVDateTimeFormatContext` from locale `locale`. Returns a `Some` if successful.
* Returns a `None` otherwise (non-existent or unavailable locale,...),
*
* @category Constructors
*/
export const fromLocale = locale => Option.gen(function* () {
const longDateTimeFormatInLocale = yield* _safeDateTimeFormat(locale, {
timeZone: 'UTC',
weekday: 'long',
month: 'long'
});
const toLongParts = Intl.DateTimeFormat.prototype.formatToParts.bind(longDateTimeFormatInLocale);
const shortDateTimeFormatInLocale = yield* _safeDateTimeFormat(locale, {
timeZone: 'UTC',
weekday: 'short',
month: 'short'
});
const toShortParts = Intl.DateTimeFormat.prototype.formatToParts.bind(shortDateTimeFormatInLocale);
const longWeekdayNames = yield* pipe(WEEKDAY_DATES, MArray.mapUnlessNone(flow(toLongParts, _extractWeekday)));
const longMonthNames = yield* pipe(MONTH_DATES, MArray.mapUnlessNone(flow(toLongParts, _extractMonth)));
const shortWeekdayNames = yield* pipe(WEEKDAY_DATES, MArray.mapUnlessNone(flow(toShortParts, _extractWeekday)));
const shortMonthNames = yield* pipe(MONTH_DATES, MArray.mapUnlessNone(flow(toShortParts, _extractMonth)));
const dayPeriodNames = ['am', 'pm'];
return fromNames({
name: locale,
shortWeekdayNames,
longWeekdayNames,
shortMonthNames,
longMonthNames,
dayPeriodNames
});
});
/**
* Same as `fromLocale` but returns directly a `CVDateTimeFormatContext` or throws in case of an
* error
*
* @category Constructors
*/
export const fromLocaleOrThrow = locale => pipe(locale, fromLocale, Option.getOrThrowWith(() => new Error(`A CVDateTimeFormat.Context could not be built for locale '${locale}'`)));
/**
* Returns the `name` property of `self`
*
* @category Destructors
*/
export const name = /*#__PURE__*/Struct.get('name');
/**
* Returns the `tokenMap` property of `self`
*
* @category Destructors
*/
export const tokenMap = /*#__PURE__*/Struct.get('tokenMap');
//# sourceMappingURL=DateTimeFormatContext.js.map