fastcronparser.java
来自「一个不错的cache」· Java 代码 · 共 1,043 行 · 第 1/3 页
JAVA
1,043 行
/* * Copyright (c) 2002-2003 by OpenSymphony * All rights reserved. */package com.opensymphony.oscache.util;import java.text.ParseException;import java.util.*;import java.util.Calendar;/** * Parses cron expressions and determines at what time in the past is the * most recent match for the supplied expression. * * @author <a href="mailto:chris@swebtec.com">Chris Miller</a> * @author $Author: ltorunski $ * @version $Revision: 1.3.2.1 $ */public class FastCronParser { private static final int NUMBER_OF_CRON_FIELDS = 5; private static final int MINUTE = 0; private static final int HOUR = 1; private static final int DAY_OF_MONTH = 2; private static final int MONTH = 3; private static final int DAY_OF_WEEK = 4; // Lookup tables that hold the min/max/size of each of the above field types. // These tables are precalculated for performance. private static final int[] MIN_VALUE = {0, 0, 1, 1, 0}; private static final int[] MAX_VALUE = {59, 23, 31, 12, 6}; /** * A lookup table holding the number of days in each month (with the obvious exception * that February requires special handling). */ private static final int[] DAYS_IN_MONTH = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; /** * Holds the raw cron expression that this parser is handling. */ private String cronExpression = null; /** * This is the main lookup table that holds a parsed cron expression. each long * represents one of the above field types. Bits in each long value correspond * to one of the possbile field values - eg, for the minute field, bits 0 -> 59 in * <code>lookup[MINUTE]</code> map to minutes 0 -> 59 respectively. Bits are set if * the corresponding value is enabled. So if the minute field in the cron expression * was <code>"0,2-8,50"</code>, bits 0, 2, 3, 4, 5, 6, 7, 8 and 50 will be set. * If the cron expression is <code>"*"</code>, the long value is set to * <code>Long.MAX_VALUE</code>. */ private long[] lookup = { Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE }; /** * This is based on the contents of the <code>lookup</code> table. It holds the * <em>highest</em> valid field value for each field type. */ private int[] lookupMax = {-1, -1, -1, -1, -1}; /** * This is based on the contents of the <code>lookup</code> table. It holds the * <em>lowest</em> valid field value for each field type. */ private int[] lookupMin = { Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE }; /** * Creates a FastCronParser that uses a default cron expression of <code>"* * * * *"</cron>. * This will match any time that is supplied. */ public FastCronParser() { } /** * Constructs a new FastCronParser based on the supplied expression. * * @throws ParseException if the supplied expression is not a valid cron expression. */ public FastCronParser(String cronExpression) throws ParseException { setCronExpression(cronExpression); } /** * Resets the cron expression to the value supplied. * * @param cronExpression the new cron expression. * * @throws ParseException if the supplied expression is not a valid cron expression. */ public void setCronExpression(String cronExpression) throws ParseException { if (cronExpression == null) { throw new IllegalArgumentException("Cron time expression cannot be null"); } this.cronExpression = cronExpression; parseExpression(cronExpression); } /** * Retrieves the current cron expression. * * @return the current cron expression. */ public String getCronExpression() { return this.cronExpression; } /** * Determines whether this cron expression matches a date/time that is more recent * than the one supplied. * * @param time The time to compare the cron expression against. * * @return <code>true</code> if the cron expression matches a time that is closer * to the current time than the supplied time is, <code>false</code> otherwise. */ public boolean hasMoreRecentMatch(long time) { return time < getTimeBefore(System.currentTimeMillis()); } /** * Find the most recent time that matches this cron expression. This time will * always be in the past, ie a lower value than the supplied time. * * @param time The time (in milliseconds) that we're using as our upper bound. * * @return The time (in milliseconds) when this cron event last occurred. */ public long getTimeBefore(long time) { // It would be nice to get rid of the Calendar class for speed, but it's a lot of work... // We create this Calendar cal = new GregorianCalendar(); cal.setTime(new Date(time)); int minute = cal.get(Calendar.MINUTE); int hour = cal.get(Calendar.HOUR_OF_DAY); int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH); int month = cal.get(Calendar.MONTH) + 1; // Calendar is 0-based for this field, and we are 1-based int year = cal.get(Calendar.YEAR); long validMinutes = lookup[MINUTE]; long validHours = lookup[HOUR]; long validDaysOfMonth = lookup[DAY_OF_MONTH]; long validMonths = lookup[MONTH]; long validDaysOfWeek = lookup[DAY_OF_WEEK]; // Find out if we have a Day of Week or Day of Month field boolean haveDOM = validDaysOfMonth != Long.MAX_VALUE; boolean haveDOW = validDaysOfWeek != Long.MAX_VALUE; boolean skippedNonLeapYear = false; while (true) { boolean retry = false; // Clean up the month if it was wrapped in a previous iteration if (month < 1) { month += 12; year--; } // get month................................................... boolean found = false; if (validMonths != Long.MAX_VALUE) { for (int i = month + 11; i > (month - 1); i--) { int testMonth = (i % 12) + 1; // Check if the month is valid if (((1L << (testMonth - 1)) & validMonths) != 0) { if ((testMonth > month) || skippedNonLeapYear) { year--; } // Check there are enough days in this month (catches non leap-years trying to match the 29th Feb) int numDays = numberOfDaysInMonth(testMonth, year); if (!haveDOM || (numDays >= lookupMin[DAY_OF_MONTH])) { if ((month != testMonth) || skippedNonLeapYear) { // New DOM = min(maxDOM, prevDays); ie, the highest valid value dayOfMonth = (numDays <= lookupMax[DAY_OF_MONTH]) ? numDays : lookupMax[DAY_OF_MONTH]; hour = lookupMax[HOUR]; minute = lookupMax[MINUTE]; month = testMonth; } found = true; break; } } } skippedNonLeapYear = false; if (!found) { // The only time we drop out here is when we're searching for the 29th of February and no other date! skippedNonLeapYear = true; continue; } } // Clean up if the dayOfMonth was wrapped. This takes leap years into account. if (dayOfMonth < 1) { month--; dayOfMonth += numberOfDaysInMonth(month, year); hour = lookupMax[HOUR]; continue; } // get day................................................... if (haveDOM && !haveDOW) { // get day using just the DAY_OF_MONTH token int daysInThisMonth = numberOfDaysInMonth(month, year); int daysInPreviousMonth = numberOfDaysInMonth(month - 1, year); // Find the highest valid day that is below the current day for (int i = dayOfMonth + 30; i > (dayOfMonth - 1); i--) { int testDayOfMonth = (i % 31) + 1; // Skip over any days that don't actually exist (eg 31st April) if ((testDayOfMonth <= dayOfMonth) && (testDayOfMonth > daysInThisMonth)) { continue; } if ((testDayOfMonth > dayOfMonth) && (testDayOfMonth > daysInPreviousMonth)) { continue; } if (((1L << (testDayOfMonth - 1)) & validDaysOfMonth) != 0) { if (testDayOfMonth > dayOfMonth) { // We've found a valid day, but we had to move back a month month--; retry = true; } if (dayOfMonth != testDayOfMonth) { hour = lookupMax[HOUR]; minute = lookupMax[MINUTE]; } dayOfMonth = testDayOfMonth; break; } } if (retry) { continue; } } else if (haveDOW && !haveDOM) { // get day using just the DAY_OF_WEEK token int daysLost = 0; int currentDOW = dayOfWeek(dayOfMonth, month, year); for (int i = currentDOW + 7; i > currentDOW; i--) { int testDOW = i % 7; if (((1L << testDOW) & validDaysOfWeek) != 0) { dayOfMonth -= daysLost; if (dayOfMonth < 1) { // We've wrapped back a month month--; dayOfMonth += numberOfDaysInMonth(month, year); retry = true; } if (currentDOW != testDOW) { hour = lookupMax[HOUR]; minute = lookupMax[MINUTE]; } break; } daysLost++; } if (retry) { continue; } } // Clean up if the hour has been wrapped if (hour < 0) { hour += 24; dayOfMonth--; continue; } // get hour................................................... if (validHours != Long.MAX_VALUE) { // Find the highest valid hour that is below the current hour for (int i = hour + 24; i > hour; i--) { int testHour = i % 24; if (((1L << testHour) & validHours) != 0) { if (testHour > hour) { // We've found an hour, but we had to move back a day dayOfMonth--; retry = true; } if (hour != testHour) { minute = lookupMax[MINUTE]; } hour = testHour; break; } } if (retry) { continue; } } // get minute................................................. if (validMinutes != Long.MAX_VALUE) { // Find the highest valid minute that is below the current minute for (int i = minute + 60; i > minute; i--) { int testMinute = i % 60; if (((1L << testMinute) & validMinutes) != 0) { if (testMinute > minute) { // We've found a minute, but we had to move back an hour hour--; retry = true; } minute = testMinute; break; } } if (retry) { continue; } }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?