| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- // @ts-ignore
- import _ from 'lodash';
- import moment from 'moment';
- const units = ['y', 'M', 'w', 'd', 'h', 'm', 's'];
- export type Timezone = 'utc';
- /**
- * Parses different types input to a moment instance. There is a specific formatting language that can be used
- * if text arg is string. See unit tests for examples.
- * @param text
- * @param roundUp See parseDateMath function.
- * @param timezone Only string 'utc' is acceptable here, for anything else, local timezone is used.
- */
- export function parse(
- text: string | moment.Moment | Date,
- roundUp?: boolean,
- timezone?: Timezone
- ): moment.Moment | undefined {
- if (!text) {
- return undefined;
- }
- if (typeof text !== 'string') {
- if (moment.isMoment(text)) {
- return text;
- }
- if (_.isDate(text)) {
- return moment(text);
- }
- // We got some non string which is not a moment nor Date. TS should be able to check for that but not always.
- return undefined;
- } else {
- let time;
- let mathString = '';
- let index;
- let parseString;
- if (text.substring(0, 3) === 'now') {
- if (timezone === 'utc') {
- time = moment.utc();
- } else {
- time = moment();
- }
- mathString = text.substring('now'.length);
- } else {
- index = text.indexOf('||');
- if (index === -1) {
- parseString = text;
- mathString = ''; // nothing else
- } else {
- parseString = text.substring(0, index);
- mathString = text.substring(index + 2);
- }
- // We're going to just require ISO8601 timestamps, k?
- time = moment(parseString, moment.ISO_8601);
- }
- if (!mathString.length) {
- return time;
- }
- return parseDateMath(mathString, time, roundUp);
- }
- }
- /**
- * Checks if text is a valid date which in this context means that it is either a Moment instance or it can be parsed
- * by parse function. See parse function to see what is considered acceptable.
- * @param text
- */
- export function isValid(text: string | moment.Moment): boolean {
- const date = parse(text);
- if (!date) {
- return false;
- }
- if (moment.isMoment(date)) {
- return date.isValid();
- }
- return false;
- }
- /**
- * Parses math part of the time string and shifts supplied time according to that math. See unit tests for examples.
- * @param mathString
- * @param time
- * @param roundUp If true it will round the time to endOf time unit, otherwise to startOf time unit.
- */
- // TODO: Had to revert Andrejs `time: moment.Moment` to `time: any`
- export function parseDateMath(mathString: string, time: any, roundUp?: boolean): moment.Moment | undefined {
- const dateTime = time;
- let i = 0;
- const len = mathString.length;
- while (i < len) {
- const c = mathString.charAt(i++);
- let type;
- let num;
- let unit;
- if (c === '/') {
- type = 0;
- } else if (c === '+') {
- type = 1;
- } else if (c === '-') {
- type = 2;
- } else {
- return undefined;
- }
- if (isNaN(parseInt(mathString.charAt(i), 10))) {
- num = 1;
- } else if (mathString.length === 2) {
- num = mathString.charAt(i);
- } else {
- const numFrom = i;
- while (!isNaN(parseInt(mathString.charAt(i), 10))) {
- i++;
- if (i > 10) {
- return undefined;
- }
- }
- num = parseInt(mathString.substring(numFrom, i), 10);
- }
- if (type === 0) {
- // rounding is only allowed on whole, single, units (eg M or 1M, not 0.5M or 2M)
- if (num !== 1) {
- return undefined;
- }
- }
- unit = mathString.charAt(i++);
- if (!_.includes(units, unit)) {
- return undefined;
- } else {
- if (type === 0) {
- if (roundUp) {
- dateTime.endOf(unit);
- } else {
- dateTime.startOf(unit);
- }
- } else if (type === 1) {
- dateTime.add(num, unit);
- } else if (type === 2) {
- dateTime.subtract(num, unit);
- }
- }
- }
- return dateTime;
- }
|