UNPKG

@js-joda/core

Version:

a date and time library for javascript

178 lines (165 loc) 6.63 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'; import { ChronoField } from '../../temporal/ChronoField'; //----------------------------------------------------------------------- const PATTERNS = [ '+HH', '+HHmm', '+HH:mm', '+HHMM', '+HH:MM', '+HHMMss', '+HH:MM:ss', '+HHMMSS', '+HH:MM:SS' ]; /** * Prints or parses an offset ID. * @private */ export class OffsetIdPrinterParser { /** * Constructor. * * @param {string} noOffsetText the text to use for UTC, not null * @param {string} pattern the pattern */ constructor(noOffsetText, pattern) { requireNonNull(noOffsetText, 'noOffsetText'); requireNonNull(pattern, 'pattern'); this.noOffsetText = noOffsetText; this.type = this._checkPattern(pattern); } /** * @param {String} pattern * @return {number} */ _checkPattern(pattern) { for (let i = 0; i < PATTERNS.length; i++) { if (PATTERNS[i] === pattern) { return i; } } throw new IllegalArgumentException(`Invalid zone offset pattern: ${pattern}`); } /** * @param {DateTimePrintContext} context * @param {StringBuilder} buf * @return {boolean} */ print(context, buf) { const offsetSecs = context.getValue(ChronoField.OFFSET_SECONDS); if (offsetSecs == null) { return false; } const totalSecs = MathUtil.safeToInt(offsetSecs); if (totalSecs === 0) { buf.append(this.noOffsetText); } else { const absHours = Math.abs(MathUtil.intMod(MathUtil.intDiv(totalSecs, 3600), 100)); // anything larger than 99 silently dropped const absMinutes = Math.abs(MathUtil.intMod(MathUtil.intDiv(totalSecs, 60), 60)); const absSeconds = Math.abs(MathUtil.intMod(totalSecs, 60)); const bufPos = buf.length(); let output = absHours; buf.append(totalSecs < 0 ? '-' : '+') .appendChar((`${MathUtil.intDiv(absHours, 10)}0`)).appendChar(`${MathUtil.intMod(absHours, 10)}0`); if (this.type >= 3 || (this.type >= 1 && absMinutes > 0)) { buf.append((this.type % 2) === 0 ? ':' : '') .appendChar((`${MathUtil.intDiv(absMinutes, 10)}0`)).appendChar((`${absMinutes % 10}0`)); output += absMinutes; if (this.type >= 7 || (this.type >= 5 && absSeconds > 0)) { buf.append((this.type % 2) === 0 ? ':' : '') .appendChar((`${MathUtil.intDiv(absSeconds, 10)}0`)).appendChar((`${absSeconds % 10}0`)); output += absSeconds; } } if (output === 0) { buf.setLength(bufPos); buf.append(this.noOffsetText); } } return true; } /** * @param {DateTimeParseContext} context * @param {String} text * @param {number} position * @return {number} */ parse(context, text, position) { const length = text.length; const noOffsetLen = this.noOffsetText.length; if (noOffsetLen === 0) { if (position === length) { return context.setParsedField(ChronoField.OFFSET_SECONDS, 0, position, position); } } else { if (position === length) { return ~position; } if (context.subSequenceEquals(text, position, this.noOffsetText, 0, noOffsetLen)) { return context.setParsedField(ChronoField.OFFSET_SECONDS, 0, position, position + noOffsetLen); } } // parse normal plus/minus offset const sign = text[position]; // IOOBE if invalid position if (sign === '+' || sign === '-') { // starts const negative = (sign === '-' ? -1 : 1); const array = [0,0,0,0]; array[0] = position + 1; if ((this._parseNumber(array, 1, text, true) || this._parseNumber(array, 2, text, this.type >=3) || this._parseNumber(array, 3, text, false)) === false) { // success const offsetSecs = MathUtil.safeZero(negative * (array[1] * 3600 + array[2] * 60 + array[3])); return context.setParsedField(ChronoField.OFFSET_SECONDS, offsetSecs, position, array[0]); } } // handle special case of empty no offset text if (noOffsetLen === 0) { return context.setParsedField(ChronoField.OFFSET_SECONDS, 0, position, position + noOffsetLen); } return ~position; } /** * Parse a two digit zero-prefixed number. * * @param {number[]} array the array of parsed data, 0=pos,1=hours,2=mins,3=secs, not null * @param {number} arrayIndex the index to parse the value into * @param {string} parseText the offset ID, not null * @param {boolean} required whether this number is required * @return {boolean} true if an error occurred */ _parseNumber(array, arrayIndex, parseText, required) { if ((this.type + 3) / 2 < arrayIndex) { return false; // ignore seconds/minutes } let pos = array[0]; if ((this.type % 2) === 0 && arrayIndex > 1) { if (pos + 1 > parseText.length || parseText[pos] !== ':') { return required; } pos++; } if (pos + 2 > parseText.length) { return required; } const ch1 = parseText[pos++]; const ch2 = parseText[pos++]; if (ch1 < '0' || ch1 > '9' || ch2 < '0' || ch2 > '9') { return required; } const value = (ch1.charCodeAt(0) - 48) * 10 + (ch2.charCodeAt(0) - 48); if (value < 0 || value > 59) { return required; } array[arrayIndex] = value; array[0] = pos; return false; } toString() { const converted = this.noOffsetText.replace('\'', '\'\''); return `Offset(${PATTERNS[this.type]},'${converted}')`; } } OffsetIdPrinterParser.INSTANCE_ID = new OffsetIdPrinterParser('Z', '+HH:MM:ss'); OffsetIdPrinterParser.PATTERNS = PATTERNS;