UNPKG

3.77 kBJavaScriptView Raw
1'use strict';
2
3var stringifyNumber = require('../../stringify/stringifyNumber.js');
4
5/** Internal types handle bigint as number, because TS can't figure it out. */
6function parseSexagesimal(str, asBigInt) {
7 const sign = str[0];
8 const parts = sign === '-' || sign === '+' ? str.substring(1) : str;
9 const num = (n) => asBigInt ? BigInt(n) : Number(n);
10 const res = parts
11 .replace(/_/g, '')
12 .split(':')
13 .reduce((res, p) => res * num(60) + num(p), num(0));
14 return (sign === '-' ? num(-1) * res : res);
15}
16/**
17 * hhhh:mm:ss.sss
18 *
19 * Internal types handle bigint as number, because TS can't figure it out.
20 */
21function stringifySexagesimal(node) {
22 let { value } = node;
23 let num = (n) => n;
24 if (typeof value === 'bigint')
25 num = n => BigInt(n);
26 else if (isNaN(value) || !isFinite(value))
27 return stringifyNumber.stringifyNumber(node);
28 let sign = '';
29 if (value < 0) {
30 sign = '-';
31 value *= num(-1);
32 }
33 const _60 = num(60);
34 const parts = [value % _60]; // seconds, including ms
35 if (value < 60) {
36 parts.unshift(0); // at least one : is required
37 }
38 else {
39 value = (value - parts[0]) / _60;
40 parts.unshift(value % _60); // minutes
41 if (value >= 60) {
42 value = (value - parts[0]) / _60;
43 parts.unshift(value); // hours
44 }
45 }
46 return (sign +
47 parts
48 .map(n => String(n).padStart(2, '0'))
49 .join(':')
50 .replace(/000000\d*$/, '') // % 60 may introduce error
51 );
52}
53const intTime = {
54 identify: value => typeof value === 'bigint' || Number.isInteger(value),
55 default: true,
56 tag: 'tag:yaml.org,2002:int',
57 format: 'TIME',
58 test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/,
59 resolve: (str, _onError, { intAsBigInt }) => parseSexagesimal(str, intAsBigInt),
60 stringify: stringifySexagesimal
61};
62const floatTime = {
63 identify: value => typeof value === 'number',
64 default: true,
65 tag: 'tag:yaml.org,2002:float',
66 format: 'TIME',
67 test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/,
68 resolve: str => parseSexagesimal(str, false),
69 stringify: stringifySexagesimal
70};
71const timestamp = {
72 identify: value => value instanceof Date,
73 default: true,
74 tag: 'tag:yaml.org,2002:timestamp',
75 // If the time zone is omitted, the timestamp is assumed to be specified in UTC. The time part
76 // may be omitted altogether, resulting in a date format. In such a case, the time part is
77 // assumed to be 00:00:00Z (start of day, UTC).
78 test: RegExp('^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})' + // YYYY-Mm-Dd
79 '(?:' + // time is optional
80 '(?:t|T|[ \\t]+)' + // t | T | whitespace
81 '([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)' + // Hh:Mm:Ss(.ss)?
82 '(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?' + // Z | +5 | -03:30
83 ')?$'),
84 resolve(str) {
85 const match = str.match(timestamp.test);
86 if (!match)
87 throw new Error('!!timestamp expects a date, starting with yyyy-mm-dd');
88 const [, year, month, day, hour, minute, second] = match.map(Number);
89 const millisec = match[7] ? Number((match[7] + '00').substr(1, 3)) : 0;
90 let date = Date.UTC(year, month - 1, day, hour || 0, minute || 0, second || 0, millisec);
91 const tz = match[8];
92 if (tz && tz !== 'Z') {
93 let d = parseSexagesimal(tz, false);
94 if (Math.abs(d) < 30)
95 d *= 60;
96 date -= 60000 * d;
97 }
98 return new Date(date);
99 },
100 stringify: ({ value }) => value.toISOString().replace(/((T00:00)?:00)?\.000Z$/, '')
101};
102
103exports.floatTime = floatTime;
104exports.intTime = intTime;
105exports.timestamp = timestamp;