Repository: mermaid-js/mermaid
File: packages/mermaid/src/diagrams/gantt/ganttDb.js (JavaScript)

Code before:
 * - `y` for years
 * - `M` for months
 * - `w` for weeks
 * - `d` for days
 * - `h` for hours
 * - `s` for seconds
 * - `ms` for milliseconds
 *
 * @param {string} str - A string representing the duration.
 * @returns {[value: number, unit: dayjs.ManipulateType]} Arguments to pass to `dayjs.add()`
 */
const parseDuration = function (str) {
  const statement = /^(\d+(?:\.\d+)?)([Mdhmswy]|ms)$/.exec(str.trim());
  if (statement !== null) {
    return [Number.parseFloat(statement[1]), statement[2]];
  }
  // NaN means an invalid duration
  return [NaN, 'ms'];
};

const getEndDate = function (prevTime, dateFormat, str, inclusive = false) {
  str = str.trim();

  // Test for until
  const re = /^until\s+([\d\w- ]+)/;
  const untilStatement = re.exec(str.trim());

  if (untilStatement !== null) {
    // check all until ids and take the earliest
    let earliestStartingTask = null;
    untilStatement[1].split(' ').forEach(function (id) {
      let task = findTaskById(id);
      if (task !== undefined) {
        if (!earliestStartingTask) {
          earliestStartingTask = task;
        } else {
          if (task.startTime < earliestStartingTask.startTime) {
            earliestStartingTask = task;
          }
        }
      }
    });

    if (!earliestStartingTask) {
      const dt = new Date();
      dt.setHours(0, 0, 0, 0);
      return dt;
    } else {
      return earliestStartingTask.startTime;
    }
  }

Review comment (suggestion):
str is already trimmed.
```suggestion
  str = str.trim();

  // Test for until
  const re = /^until\s+([\d\w- ]+)/;
  const untilStatement = re.exec(str);
```

Code after:
 *
 * @param {string} str - A string representing the duration.
 * @returns {[value: number, unit: dayjs.ManipulateType]} Arguments to pass to `dayjs.add()`
 */
const parseDuration = function (str) {
  // cspell:disable-next-line
  const statement = /^(\d+(?:\.\d+)?)([Mdhmswy]|ms)$/.exec(str.trim());
  if (statement !== null) {
    return [Number.parseFloat(statement[1]), statement[2]];
  }
  // NaN means an invalid duration
  return [NaN, 'ms'];
};

const getEndDate = function (prevTime, dateFormat, str, inclusive = false) {
  str = str.trim();

  // test for until
  const untilRePattern = /^until\s+(?<ids>[\d\w- ]+)/;
  const untilStatement = untilRePattern.exec(str);

  if (untilStatement !== null) {
    // check all until ids and take the earliest
    let earliestTask = null;
    for (const id of untilStatement.groups.ids.split(' ')) {
      let task = findTaskById(id);
      if (task !== undefined && (!earliestTask || task.startTime < earliestTask.startTime)) {
        earliestTask = task;
      }
    }

    if (earliestTask) {
      return earliestTask.startTime;
    }
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    return today;
  }

  // check for actual date
  let parsedDate = dayjs(str, dateFormat.trim(), true);
  if (parsedDate.isValid()) {
    if (inclusive) {
      parsedDate = parsedDate.add(1, 'd');
    }
    return parsedDate.toDate();
  }

  let endTime = dayjs(prevTime);
  const [durationValue, durationUnit] = parseDuration(str);
  if (!Number.isNaN(durationValue)) {