UNPKG

@protontech/timezone-support

Version:

Lightweight time zone support for your applications or other date libraries.

179 lines (156 loc) 4.76 kB
import formattingTokens from './tokens' const match1 = /\d/ // 0 - 9 const match2 = /\d\d/ // 00 - 99 const match3 = /\d{3}/ // 000 - 999 const match4 = /\d{4}/ // 0000 - 9999 const match1to2 = /\d\d?/ // 0 - 99 const matchUpperAMPM = /[AP]M/ const matchLowerAMPM = /[ap]m/ const matchSigned = /[+-]?\d+/ // -inf - inf const matchOffset = /(?:[+-]\d\d:?\d\d|Z)/ // +00:00 -00:00 +0000 -0000 or Z const matchAbbreviation = /[A-Z]{3,4}/ // CET const parseTokenExpressions = {} const parseTokenFunctions = {} const parsers = {} function correctDayPeriod (time) { const { afternoon } = time if (afternoon !== undefined) { const { hours } = time if (afternoon) { if (hours < 12) { time.hours += 12 } } else { if (hours === 12) { time.hours = 0 } } delete time.afternoon } } function makeParser (format) { const array = format.match(formattingTokens) if (!array) { throw new Error(`Invalid format: "${format}".`) } const { length } = array for (let i = 0; i < length; ++i) { const token = array[i] const regex = parseTokenExpressions[token] const parser = parseTokenFunctions[token] if (parser) { array[i] = { regex, parser } } else { array[i] = token.replace(/^\[|\]$/g, '') } } return function (input) { const time = {} for (let i = 0, start = 0; i < length; ++i) { const token = array[i] if (typeof token === 'string') { if (input.indexOf(token, start) !== start) { const part = input.substr(start, token.length) throw new Error(`Expected "${token}" at character ${start}, found "${part}".`) } start += token.length } else { const { regex, parser } = token const part = input.substr(start) const match = regex.exec(part) if (!match || match.index !== 0) { throw new Error(`Matching "${regex}" at character ${start} failed with "${part}".`) } const value = match[0] parser.call(time, value) start += value.length } } correctDayPeriod(time) return time } } function addExpressionToken (token, regex) { parseTokenExpressions[token] = regex } function addParseToken (tokens, property) { if (typeof tokens === 'string') { tokens = [ tokens ] } const callback = typeof property === 'string' ? function (input) { this[property] = +input } : property for (let token of tokens) { parseTokenFunctions[token] = callback } } function offsetFromString (string) { if (string === 'Z') { return 0 } const parts = string.match(/([+-]|\d\d)/g) const minutes = +(parts[1] * 60) + +parts[2] return minutes === 0 ? 0 : parts[0] === '+' ? -minutes : minutes } addExpressionToken('A', matchUpperAMPM) addParseToken(['A'], function (input) { this.afternoon = input === 'PM' }) addExpressionToken('a', matchLowerAMPM) addParseToken(['a'], function (input) { this.afternoon = input === 'pm' }) addExpressionToken('S', match1) addExpressionToken('SS', match2) addExpressionToken('SSS', match3) for (let token = 'S', factor = 100; factor >= 1; token += 'S', factor /= 10) { addParseToken(token, function (input) { this.milliseconds = +input * factor }) } addExpressionToken('s', match1to2) addExpressionToken('ss', match2) addParseToken(['s', 'ss'], 'seconds') addExpressionToken('m', match1to2) addExpressionToken('mm', match2) addParseToken(['m', 'mm'], 'minutes') addExpressionToken('H', match1to2) addExpressionToken('h', match1to2) addExpressionToken('HH', match2) addExpressionToken('hh', match2) addParseToken(['H', 'HH', 'h', 'hh'], 'hours') addExpressionToken('d', match1) addParseToken('d', 'dayOfWeek') addExpressionToken('D', match1to2) addExpressionToken('DD', match2) addParseToken(['D', 'DD'], 'day') addExpressionToken('M', match1to2) addExpressionToken('MM', match2) addParseToken(['M', 'MM'], 'month') addExpressionToken('Y', matchSigned) addExpressionToken('YY', match2) addExpressionToken('YYYY', match4) addParseToken(['Y', 'YYYY'], 'year') addParseToken('YY', function (input) { input = +input this.year = input + (input > 68 ? 1900 : 2000) }) addExpressionToken('z', matchAbbreviation) addParseToken('z', function (input) { const zone = this.zone || (this.zone = {}) zone.abbreviation = input }) addExpressionToken('Z', matchOffset) addExpressionToken('ZZ', matchOffset) addParseToken(['Z', 'ZZ'], function (input) { const zone = this.zone || (this.zone = {}) zone.offset = offsetFromString(input) }) function parseZonedTime (input, format) { let parser = parsers[format] if (!parser) { parser = parsers[format] = makeParser(format) } return parser(input) } export { parseZonedTime }