UNPKG

@parischap/conversions

Version:

A functional library to replace partially the native Intl API

316 lines 11.2 kB
/** * 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