// vim: set et sw=2 ts=2: // // This file is part of Moonfire NVR, a security camera network video recorder. // Copyright (C) 2018 The Moonfire NVR Authors // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // In addition, as a special exception, the copyright holders give // permission to link the code of portions of this program with the // OpenSSL library under certain conditions as described in each // individual source file, and distribute linked combinations including // the two. // // You must obey the GNU General Public License in all respects for all // of the code used other than OpenSSL. If you modify file(s) with this // exception, you may extend this exception to your version of the // file(s), but you are not obligated to do so. If you do not wish to do // so, delete this exception statement from your version. If you delete // this exception statement from all source files in the program, then // also delete it here. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . import moment from 'moment-timezone'; /** * Regular expression for parsing time format from timestamps. * * These regex captures groups: * 0: whole match or null if none * 1: HH:MM portion, or undefined * 2: :ss portion, or undefined * 3: FFFFF portion, or undefined * 4: [+-]hh[:mm] portion, or undefined * * @type {RegExp} */ const timeRe = new RegExp( [ '^', // Start '([0-9]{1,2}:[0-9]{2})', // Capture HH:MM '(?:(:[0-9]{2})(?::([0-9]{5}))?)?', // Capture [:ss][:FFFFF] '([+-][0-9]{1,2}:?(?:[0-9]{2})?)?', // Capture [+-][zone] '$', // End ].join('') ); /** * Class to parse time strings that possibly contain fractional * seconds in 90k units into a Number representation. * * The general format: * Expected timestamps are in this format: * HH:MM[:ss][:FFFFF][[+-]hh[:mm]] * where * HH = hours in one or two digits * MM = minutes in one or two digits * ss = seconds in one or two digits * FFFFF = fractional seconds in 90k units in exactly 5 digits * hh = hours of timezone offset in one or two digits * mm = minutes of timezone offset in one or two digits * */ export default class Time90kParser { /** * Construct with specific timezone. * * @param {String} tz Timezone */ constructor(tz) { self._tz = tz; } /** * Set (another) timezone. * * @param {String} tz Timezone */ set tz(tz) { self._tz = tz; } /** * Parses the given date and time string into a valid time90k or null. * * The date and time strings must be compatible with the partial ISO-8601 * formats for each, or acceptable to the standard Date object. * * If only a date is specified and dateOnlyThenEndOfDay is false, the 00:00 * timestamp for that day is returned. If dateOnlyThenEndOfDay is true, the * 00:00 of the very next day is returned. * * @param {String} dateStr String representing date * @param {String} timeStr String representing time * @param {Boolean} dateOnlyThenEndOfDay If only a date was specified and * this is true, then return time * for the end of day * @return {Number} Timestamp in 90k units, or null if parsing failed */ parseDateTime90k(dateStr, timeStr, dateOnlyThenEndOfDay) { // If just date, no special handling needed if (!timeStr) { const m = moment.tz(dateStr, self._tz); if (dateOnlyThenEndOfDay) { m.add({days: 1}); } return m.valueOf() * 90; } const [match, hhmm, ss, fffff, tz] = timeRe.exec(timeStr) || []; if (!match) { return null; } const orBlank = (s) => s || ''; const datetimeStr = dateStr + 'T' + hhmm + orBlank(ss) + orBlank(tz); const m = moment.tz(datetimeStr, self._tz); if (!m.isValid()) { return null; } const frac = fffff === undefined ? 0 : parseInt(fffff, 10); return m.valueOf() * 90 + frac; } }