export const fields = { G: { field: 'era', desc: 'Era' }, y: { field: 'year', desc: 'Year' }, Y: { field: 'year', desc: 'Year of "Week of Year"' }, u: { field: 'year', desc: 'Extended year' }, U: { field: 'year', desc: 'Cyclic year name' }, r: { field: 'year', desc: 'Related Gregorian year' }, Q: { field: 'quarter', desc: 'Quarter' }, q: { field: 'quarter', desc: 'Stand-alone quarter' }, M: { field: 'month', desc: 'Month in year' }, L: { field: 'month', desc: 'Stand-alone month in year' }, w: { field: 'week', desc: 'Week of year' }, W: { field: 'week', desc: 'Week of month' }, d: { field: 'day', desc: 'Day in month' }, D: { field: 'day', desc: 'Day of year' }, F: { field: 'day', desc: 'Day of week in month' }, g: { field: 'day', desc: 'Modified julian day' }, E: { field: 'weekday', desc: 'Day of week' }, e: { field: 'weekday', desc: 'Local day of week' }, c: { field: 'weekday', desc: 'Stand-alone local day of week' }, a: { field: 'period', desc: 'AM/PM marker' }, b: { field: 'period', desc: 'AM/PM/noon/midnight marker' }, B: { field: 'period', desc: 'Flexible day period' }, h: { field: 'hour', desc: 'Hour in AM/PM (1~12)' }, H: { field: 'hour', desc: 'Hour in day (0~23)' }, k: { field: 'hour', desc: 'Hour in day (1~24)' }, K: { field: 'hour', desc: 'Hour in AM/PM (0~11)' }, j: { field: 'hour', desc: 'Hour in preferred cycle' }, J: { field: 'hour', desc: 'Hour in preferred cycle without marker' }, C: { field: 'hour', desc: 'Hour in preferred cycle with flexible marker' }, m: { field: 'min', desc: 'Minute in hour' }, s: { field: 'sec', desc: 'Second in minute' }, S: { field: 'sec-frac', desc: 'Fractional second' }, A: { field: 'ms', desc: 'Milliseconds in day' }, z: { field: 'tz', desc: 'Time Zone: specific non-location' }, Z: { field: 'tz', desc: 'Time Zone' }, O: { field: 'tz', desc: 'Time Zone: localized' }, v: { field: 'tz', desc: 'Time Zone: generic non-location' }, V: { field: 'tz', desc: 'Time Zone: ID' }, X: { field: 'tz', desc: 'Time Zone: ISO8601 with Z' }, x: { field: 'tz', desc: 'Time Zone: ISO8601' } }; const isLetter = (char) => (char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z'); function readFieldToken(src, pos) { const char = src[pos]; let width = 1; while (src[++pos] === char) ++width; const field = fields[char]; if (!field) { const msg = `The letter ${char} is not a valid field identifier`; return { char, error: new Error(msg), width }; } return { char, field: field.field, desc: field.desc, width }; } function readQuotedToken(src, pos) { let str = src[++pos]; let width = 2; if (str === "'") return { char: "'", str, width }; while (true) { const next = src[++pos]; ++width; if (next === undefined) { const msg = `Unterminated quoted literal in pattern: ${str || src}`; return { char: "'", error: new Error(msg), str, width }; } else if (next === "'") { if (src[++pos] !== "'") return { char: "'", str, width }; else ++width; } str += next; } } function readToken(src, pos) { const char = src[pos]; if (!char) return null; if (isLetter(char)) return readFieldToken(src, pos); if (char === "'") return readQuotedToken(src, pos); let str = char; let width = 1; while (true) { const next = src[++pos]; if (!next || isLetter(next) || next === "'") return { char, str, width }; str += next; width += 1; } } /** * Parse an {@link http://userguide.icu-project.org/formatparse/datetime | ICU * DateFormat skeleton} string into a {@link DateToken} array. * * @remarks * Errors will not be thrown, but if encountered are included as the relevant * token's `error` value. * * @public * @param src - The skeleton string * * @example * ```js * import { parseDateTokens } from '@messageformat/date-skeleton' * * parseDateTokens('GrMMMdd', console.error) * // [ * // { char: 'G', field: 'era', desc: 'Era', width: 1 }, * // { char: 'r', field: 'year', desc: 'Related Gregorian year', width: 1 }, * // { char: 'M', field: 'month', desc: 'Month in year', width: 3 }, * // { char: 'd', field: 'day', desc: 'Day in month', width: 2 } * // ] * ``` */ export function parseDateTokens(src) { const tokens = []; let pos = 0; while (true) { const token = readToken(src, pos); if (!token) return tokens; tokens.push(token); pos += token.width; } }