public static CronExpression create(String expression) {
        if (expression.isEmpty()) {
            throw new InvalidCronExpressionException("empty expression");
        }
        String[] fields = expression.trim().toLowerCase().split("\\s+");
        int count = fields.length;
        if (count > 6 || count < 5) {
            throw new InvalidCronExpressionException(
                    "crontab expression should have 6 fields for (seconds resolution) or 5 fields for (minutes resolution)");
        }
        CronExpression cronExpression = new CronExpression();
        cronExpression.hasSecondsField = count == 6;
        String token;
        int index = 0;
        if (cronExpression.hasSecondsField) {
            token = fields[index++];
            cronExpression.seconds = CronExpression.SECONDS_FIELD_PARSER.parse(token);
        } else {
            cronExpression.seconds = new BitSet(1);
            cronExpression.seconds.set(0);
        }
        token = fields[index++];
        cronExpression.minutes = CronExpression.MINUTES_FIELD_PARSER.parse(token);

        token = fields[index++];
        cronExpression.hours = CronExpression.HOURS_FIELD_PARSER.parse(token);

        token = fields[index++];
        String daysToken = token;
        cronExpression.days = CronExpression.DAYS_FIELD_PARSER.parse(token);
        cronExpression.isLastDayOfMonth = token.equals("l");
        boolean daysStartWithAsterisk = token.startsWith("*");

        token = fields[index++];
        cronExpression.months = CronExpression.MONTHS_FIELD_PARSER.parse(token);

        token = fields[index++];
        cronExpression.daysOfWeek = CronExpression.DAY_OF_WEEK_FIELD_PARSER.parse(token);
        boolean daysOfWeekStartAsterisk = token.startsWith("*");

        if (token.length() == 2 && token.endsWith("l")) {
            if (cronExpression.isLastDayOfMonth) {
                throw new InvalidCronExpressionException("You can only specify the last day of month week in either the DAY field or in the DAY_OF_WEEK field, not both.");
            }
            if (!daysToken.equalsIgnoreCase("*")) {
                throw new InvalidCronExpressionException("when last days of month is specified. the day of the month must be \"*\"");
            }
            // this flag will be used later duing finding the next schedule as some months have less than 31 days
            cronExpression.isSpecificLastDayOfMonth = true;
        }
        cronExpression.daysOf5Weeks = generateDaysOf5Weeks(cronExpression.daysOfWeek);

        cronExpression.daysAndDaysOfWeekRelation = (daysStartWithAsterisk || daysOfWeekStartAsterisk)
                ? DaysAndDaysOfWeekRelation.INTERSECT
                : DaysAndDaysOfWeekRelation.UNION;

        if (!cronExpression.canScheduleActuallyOccur())
            throw new InvalidCronExpressionException("Cron expression not valid. The specified months do not have the day 30th or the day 31st");
        cronExpression.expression = expression.trim();
        return cronExpression;
    }
@Test
    void invalidCronExpressionThrowsException() {
        assertThatThrownBy(() -> CronExpression.create("invalid")).isInstanceOf(InvalidCronExpressionException.class);
    }