UNPKG

@js-joda/core

Version:

a date and time library for javascript

163 lines (153 loc) 6.28 kB
/** * @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper * @copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos * @license BSD-3-Clause (see LICENSE in the root directory of this source tree) */ import { requireNonNull } from '../../assert'; import { IllegalArgumentException } from '../../errors'; import { MathUtil } from '../../MathUtil'; /** * TODO optimize FractionPrinterParser, fix documentation * * Prints and parses a numeric date-time field with optional padding. * @private */ export class FractionPrinterParser { /** * Constructor. * * @param {TemporalField} field the field to output, not null * @param {Number} minWidth the minimum width to output, from 0 to 9 * @param {Number} maxWidth the maximum width to output, from 0 to 9 * @param {boolean} decimalPoint whether to output the localized decimal point symbol */ constructor(field, minWidth, maxWidth, decimalPoint) { requireNonNull(field, 'field'); if (field.range().isFixed() === false) { throw new IllegalArgumentException(`Field must have a fixed set of values: ${field}`); } if (minWidth < 0 || minWidth > 9) { throw new IllegalArgumentException(`Minimum width must be from 0 to 9 inclusive but was ${minWidth}`); } if (maxWidth < 1 || maxWidth > 9) { throw new IllegalArgumentException(`Maximum width must be from 1 to 9 inclusive but was ${maxWidth}`); } if (maxWidth < minWidth) { throw new IllegalArgumentException(`Maximum width must exceed or equal the minimum width but ${ maxWidth} < ${minWidth}`); } this.field = field; this.minWidth = minWidth; this.maxWidth = maxWidth; this.decimalPoint = decimalPoint; } print(context, buf) { const value = context.getValue(this.field); if (value === null) { return false; } const symbols = context.symbols(); if (value === 0) { // scale is zero if value is zero if (this.minWidth > 0) { if (this.decimalPoint) { buf.append(symbols.decimalSeparator()); } for (let i = 0; i < this.minWidth; i++) { buf.append(symbols.zeroDigit()); } } } else { let fraction = this.convertToFraction(value, symbols.zeroDigit()); const outputScale = Math.min(Math.max(fraction.length, this.minWidth), this.maxWidth); fraction = fraction.substr(0, outputScale); if(fraction * 1 > 0 ) { while (fraction.length > this.minWidth && fraction[fraction.length - 1] === '0') { fraction = fraction.substr(0, fraction.length - 1); } } let str = fraction; str = symbols.convertNumberToI18N(str); if (this.decimalPoint) { buf.append(symbols.decimalSeparator()); } buf.append(str); } return true; } parse(context, text, position) { const effectiveMin = (context.isStrict() ? this.minWidth : 0); const effectiveMax = (context.isStrict() ? this.maxWidth : 9); const length = text.length; if (position === length) { // valid if whole field is optional, invalid if minimum width return (effectiveMin > 0 ? ~position : position); } if (this.decimalPoint) { if (text[position] !== context.symbols().decimalSeparator()) { // valid if whole field is optional, invalid if minimum width return (effectiveMin > 0 ? ~position : position); } position++; } const minEndPos = position + effectiveMin; if (minEndPos > length) { return ~position; // need at least min width digits } const maxEndPos = Math.min(position + effectiveMax, length); let total = 0; // can use int because we are only parsing up to 9 digits let pos = position; while (pos < maxEndPos) { const ch = text.charAt(pos++); const digit = context.symbols().convertToDigit(ch); if (digit < 0) { if (pos < minEndPos) { return ~position; // need at least min width digits } pos--; break; } total = total * 10 + digit; } const moveLeft = pos - position; const scale = Math.pow(10, moveLeft); const value = this.convertFromFraction(total, scale); return context.setParsedField(this.field, value, position, pos); } /** * * @param {Number} value the value to convert, must be valid for this rule * @param {String} zeroDigit the character for zero * @return {String} the value as a fraction within the range, from 0 to 1, not null */ convertToFraction(value, zeroDigit) { const range = this.field.range(); range.checkValidValue(value, this.field); const _min = range.minimum(); const _range = range.maximum() - _min + 1; const _value = value - _min; const _scaled = MathUtil.intDiv((_value * 1000000000), _range); let fraction = `${_scaled}`; while(fraction.length < 9){ fraction = zeroDigit + fraction; } return fraction; } /** * * @param {Number} total the fraction to convert, not null * @param {Number} scale the scale, not null * @return {Number} the value of the field, valid for this rule * @throws DateTimeException if the value cannot be converted */ convertFromFraction(total, scale) { const range = this.field.range(); const _min = range.minimum(); const _range = range.maximum() - _min + 1; const _value = MathUtil.intDiv((total * _range), scale); return _value; } toString() { const decimal = (this.decimalPoint ? ',DecimalPoint' : ''); return `Fraction(${this.field},${this.minWidth},${this.maxWidth}${decimal})`; } }