cosmjs-util/packages/encoding/src/rfc3339.ts
2020-06-09 17:46:03 +02:00

59 lines
1.9 KiB
TypeScript

import { ReadonlyDate } from "readonly-date";
const rfc3339Matcher = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2}):(\d{2})(\.\d{1,9})?((?:[+-]\d{2}:\d{2})|Z)$/;
function padded(integer: number, length = 2): string {
const filled = "00000" + integer.toString();
return filled.substring(filled.length - length);
}
export function fromRfc3339(str: string): ReadonlyDate {
const matches = rfc3339Matcher.exec(str);
if (!matches) {
throw new Error("Date string is not in RFC3339 format");
}
const year = +matches[1];
const month = +matches[2];
const day = +matches[3];
const hour = +matches[4];
const minute = +matches[5];
const second = +matches[6];
// fractional seconds match either undefined or a string like ".1", ".123456789"
const milliSeconds = matches[7] ? Math.floor(+matches[7] * 1000) : 0;
let tzOffsetSign: number;
let tzOffsetHours: number;
let tzOffsetMinutes: number;
// if timezone is undefined, it must be Z or nothing (otherwise the group would have captured).
if (matches[8] === "Z") {
tzOffsetSign = 1;
tzOffsetHours = 0;
tzOffsetMinutes = 0;
} else {
tzOffsetSign = matches[8].substring(0, 1) === "-" ? -1 : 1;
tzOffsetHours = +matches[8].substring(1, 3);
tzOffsetMinutes = +matches[8].substring(4, 6);
}
const tzOffset = tzOffsetSign * (tzOffsetHours * 60 + tzOffsetMinutes) * 60; // seconds
return new ReadonlyDate(
ReadonlyDate.UTC(year, month - 1, day, hour, minute, second, milliSeconds) - tzOffset * 1000,
);
}
export function toRfc3339(date: Date | ReadonlyDate): string {
const year = date.getUTCFullYear();
const month = padded(date.getUTCMonth() + 1);
const day = padded(date.getUTCDate());
const hour = padded(date.getUTCHours());
const minute = padded(date.getUTCMinutes());
const second = padded(date.getUTCSeconds());
const ms = padded(date.getUTCMilliseconds(), 3);
return `${year}-${month}-${day}T${hour}:${minute}:${second}.${ms}Z`;
}