private void parseNormal(DateTimeParserContext ctx) {
        int lastMatchIdx = 0;
        boolean matches = true;

        while (ctx.hasMoreTokens() && matches) {
            if (SKIP_SYMBOLS.contains(ctx.symbolAtCursor())) {
                ctx.cursor().inc();
                lastMatchIdx++;
                continue;
            }

            DateTimeTokenizer.Symbol symbol = ctx.symbolAtCursor();
            List<GroupNode> groupNodes = parseTree.get(symbol);

            if (groupNodes == null) {
                throw new DateTimeException("Cannot find parser tree for symbol: " + symbol);
            }

            for (GroupNode group : groupNodes) {
                matches = group.matches(ctx);
                if (matches) {
                    // we should delay processing the relative groups
                    if (group.isRelative()) {
                        relatives.offer(new CursorAwareNode(lastMatchIdx, group));
                    } else {
                        matched.offer(group);
                        ctx.cursor().setValue(lastMatchIdx);
                        group.apply(ctx);
                    }

                    lastMatchIdx = ctx.cursor().value();
                    break;
                }
            }
        }
    }
The base time for relative statements.
exts/jphp-zend-ext/src/main/java/org/develnext/jphp/zend/ext/standard/date/DateTimeParser.java