001package fr.aumgn.bukkitutils.util;
002
003import java.util.regex.Matcher;
004import java.util.regex.Pattern;
005
006/**
007 * Time parser extracted from CommandBook
008 * Credits go to sk89q and contributors.
009 */
010public final class TimeUtil {
011
012    protected static final Pattern TWELVE_HOUR_TIME =
013            Pattern.compile("^([0-9]+(?::[0-9]+)?)([apmAPM\\.]+)$");
014
015    public static class UnknownTimeFormatException extends Exception {
016        private static final long serialVersionUID = -3382490815044549013L;
017
018        private final String time;
019
020        public UnknownTimeFormatException(String time) {
021            super("Unrecognized format \"" + time + "\".");
022            this.time = time;
023        }
024
025        public String getTime() {
026            return time;
027        }
028    }
029
030    public static class UnknownTimePeriodException extends Exception {
031        private static final long serialVersionUID = -1749578413958028355L;
032
033        private final String period;
034
035        public UnknownTimePeriodException(String period) {
036            super("'am' or 'pm' expected, got '" + period + "'.");
037            this.period = period;
038        }
039
040        public String getPeriod() {
041            return period;
042        }
043    }
044
045    /**
046     * Parse a time string
047     * Accepted format are:
048     * <ul>
049     *   <li>0 to 24 numbers for hour</li>
050     *   <li>Numbers above 24 for ticks</li>
051     *   <li>hh:mm (24h format)</li>
052     *   <li>hh:mm{am,pm,a.m.,p.m.} (12h format)</li>
053     *   <li>
054     *     shortcuts :
055     *     <ul>
056     *       <li>dawn</li>
057     *       <li>sunrise</li>
058     *       <li>morning</li>
059     *       <li>day</li>
060     *       <li>midday</li>
061     *       <li>noon</li>
062     *       <li>afternoon</li>
063     *       <li>evening</li>
064     *       <li>sunset</li>
065     *       <li>dusk</li>
066     *       <li>night</li>
067     *       <li>midnight</li>
068     *     </ul>
069     *   </li>
070     * </ul>
071     */
072    public static int parseTime(String timeStr)
073            throws UnknownTimeFormatException, UnknownTimePeriodException {
074        Matcher matcher;
075
076        try {
077            int time = Integer.parseInt(timeStr);
078
079            // People tend to enter just a number of the hour
080            if (time <= 24) {
081                return ((time - 8) % 24) * 1000;
082            }
083
084            return time;
085        } catch (NumberFormatException _) {
086            // Not an integer!
087        }
088
089        // Tick time
090        if (timeStr.matches("^*[0-9]+$")) {
091            return Integer.parseInt(timeStr.substring(1));
092
093            // Allow 24-hour time
094        } else if (timeStr.matches("^[0-9]+:[0-9]+$")) {
095            String[] parts = timeStr.split(":");
096            int hours = Integer.parseInt(parts[0]);
097            int mins = Integer.parseInt(parts[1]);
098            return (int) (((hours - 8) % 24) * 1000
099                    + Math.round((mins % 60) / 60.0 * 1000));
100
101            // Or perhaps 12-hour time
102        } else if ((matcher = TWELVE_HOUR_TIME.matcher(timeStr)).matches()) {
103            String time = matcher.group(1);
104            String period = matcher.group(2);
105            int shift;
106
107            if (period.equalsIgnoreCase("am")
108                    || period.equalsIgnoreCase("a.m.")) {
109                shift = 0;
110            } else if (period.equalsIgnoreCase("pm")
111                    || period.equalsIgnoreCase("p.m.")) {
112                shift = 12;
113            } else {
114                throw new UnknownTimePeriodException(period);
115            }
116
117            String[] parts = time.split(":");
118            int hours = Integer.parseInt(parts[0]);
119            int mins = parts.length >= 2 ? Integer.parseInt(parts[1]) : 0;
120            return (int) ((((hours % 12) + shift - 8) % 24) * 1000
121                    + (mins % 60) / 60.0 * 1000);
122
123            // Or some shortcuts
124        } else if (timeStr.equalsIgnoreCase("dawn")) {
125            return (6 - 8 + 24) * 1000;
126        } else if (timeStr.equalsIgnoreCase("sunrise")) {
127            return (7 - 8 + 24) * 1000;
128        } else if (timeStr.equalsIgnoreCase("morning")) {
129            return (24) * 1000;
130        } else if (timeStr.equalsIgnoreCase("day")) {
131            return (24) * 1000;
132        } else if (timeStr.equalsIgnoreCase("midday")
133                || timeStr.equalsIgnoreCase("noon")) {
134            return (12 - 8 + 24) * 1000;
135        } else if (timeStr.equalsIgnoreCase("afternoon")) {
136            return (14 - 8 + 24) * 1000;
137        } else if (timeStr.equalsIgnoreCase("evening")) {
138            return (16 - 8 + 24) * 1000;
139        } else if (timeStr.equalsIgnoreCase("sunset")) {
140            return (21 - 8 + 24) * 1000;
141        } else if (timeStr.equalsIgnoreCase("dusk")) {
142            return (21 - 8 + 24) * 1000 + (int) (30 / 60.0 * 1000);
143        } else if (timeStr.equalsIgnoreCase("night")) {
144            return (22 - 8 + 24) * 1000;
145        } else if (timeStr.equalsIgnoreCase("midnight")) {
146            return (0 - 8 + 24) * 1000;
147        }
148
149        throw new UnknownTimeFormatException(timeStr);
150    }
151
152    private TimeUtil() {
153    }
154}