3340dd88bb2da63b679e3fb143a31d05f2ce4a4b
[org.ibex.util.git] / src / org / ibex / util / RobustDateParser.java
1 package org.ibex.util;
2 import java.util.*;
3 import java.io.*;
4
5 // FEATURE: cope with stuff like "next tuesday" or "in three hours"
6
7 /** prototype parser for free-form dates; mostly needs to handle RFC2822 Date field */
8 public class RobustDateParser {
9
10     private static final int CHARCLASS_OTHER = 0;
11     private static final int CHARCLASS_WS    = 1;
12     private static final int CHARCLASS_ALPHA = 2;
13     private static final int CHARCLASS_NUM   = 3;
14     private static final int CHARCLASS_SYM   = 4;
15
16     public static int charClass(char c) {
17         if (Character.isWhitespace(c)) return CHARCLASS_WS;
18         if (Character.isLetter(c)) return CHARCLASS_ALPHA;
19         if (Character.isDigit(c)) return CHARCLASS_NUM;
20         return CHARCLASS_SYM;
21     }
22
23     public static int parseMonth(String s) {
24         s = s.trim().substring(0, 3).toLowerCase();
25         if (s.equals("jan")) return 1;
26         if (s.equals("feb")) return 2;
27         if (s.equals("mar")) return 3;
28         if (s.equals("apr")) return 4;
29         if (s.equals("may")) return 5;
30         if (s.equals("jun")) return 6;
31         if (s.equals("jul")) return 7;
32         if (s.equals("aug")) return 8;
33         if (s.equals("sep")) return 9;
34         if (s.equals("oct")) return 10;
35         if (s.equals("nov")) return 11;
36         if (s.equals("dec")) return 12;
37         return -1;
38     }
39
40     public static String[] tokenize(String s) {
41         Vec components = new Vec();
42         String tok = "";
43         for(int i=0; i<s.length(); i++) {
44             char c = s.charAt(i);
45             if (tok.length() > 0 && charClass(c) != charClass(tok.charAt(tok.length()-1))) {
46                 components.add(tok);
47                 tok = "";
48                 i--;
49                 continue;
50             }
51             if (charClass(c) == CHARCLASS_WS) continue;
52             tok += c;
53         }
54         if (tok.length() > 0)
55             components.add(tok);
56         String[] ret = new String[components.size()];
57         components.toArray(ret);
58         return ret;
59     }
60
61     public static Date parseDate(String s) {
62         if (s==null) return null;
63         try {
64             return parseDate_(s);
65         } catch (Exception e) {
66             Log.error(RobustDateParser.class, e);
67             return null;
68         }
69     }
70     public static Date parseDate_(String s) {
71         String[] toks = tokenize(s);
72         int[] cclass = new int[toks.length];
73         for(int i=0; i<toks.length; i++)
74             cclass[i] = charClass(toks[i].charAt(0));
75         int day   = -1;
76         int month = -1;
77         int year  = -1;
78         int hour  = -1;
79         int min   = -1;
80         int sec   = -1;
81         TimeZone tz = null;
82
83         for(int i=0; i<toks.length; i++) {
84             if (cclass[i]==CHARCLASS_ALPHA) {
85                 int tempmonth = parseMonth(toks[i]);
86                 if (tempmonth != -1) {
87                     month = tempmonth;
88                     if (i<toks.length-1 && cclass[i+1]==CHARCLASS_NUM && toks[i+1].length()<=2)
89                         day = Integer.parseInt(toks[i+1]);
90                     else if (i>0 && cclass[i-1]==CHARCLASS_NUM && toks[i-1].length()<=2)
91                         day = Integer.parseInt(toks[i-1]);
92                 } else if (tz==null && toks[i].length()==3 && toks[i].toUpperCase().equals(toks[i])) {
93                     tz = TimeZone.getTimeZone(toks[i]);
94                 }
95             }
96             if (cclass[i]==CHARCLASS_NUM) {
97                 if (toks[i].length()==4) {
98                     int y = Integer.parseInt(toks[i]);
99                     if (y > 1960 && y < 2100)
100                         year = Integer.parseInt(toks[i]);
101                     else if (i>0 && (toks[i-1].equals("+") || toks[i-1].equals("-"))) {
102                         String st = (toks[i-1]+toks[i]).trim();
103                         while (st.length() > 0 && st.charAt(0)=='+') st = st.substring(1);
104                         int ofs = Integer.parseInt(st);
105                         if (ofs % 100 == 0)
106                             tz = new SimpleTimeZone((ofs / 100) * 60 * 60 * 1000, (toks[i-1]+toks[i]));
107                     }
108                 } else if (i<toks.length-1 && toks[i+1].equals(":")) {
109                     if      (hour == -1) hour = Integer.parseInt(toks[i]);
110                     else if (min == -1)  min  = Integer.parseInt(toks[i]);
111                 } else if (i>0 && toks[i-1].equals(":")) {
112                     if (sec == -1)  sec  = Integer.parseInt(toks[i]);
113                 }
114             }
115         }
116
117         GregorianCalendar gc = new GregorianCalendar();
118         if (tz != null) gc.setTimeZone(tz);
119         gc.set(year, month-1, day, hour, min, sec); 
120         Date d = gc.getTime();
121
122         /*
123         StringBuffer sb = new StringBuffer();
124         sb.append("components: ");
125         for(String t : toks)
126             sb.append("\""+t+"\" ");
127         Log.warn("RobustDateParser",
128                  sb.toString() + "\n" +
129                  "components: " + year+":"+month+":"+day+":"+hour+":"+min+":"+sec + "\n" +
130                  "parsed as: " + d);
131         */
132         return d;
133     }
134
135 }