Codeforces 開催日時取得プログラムβ

TopcoderSRM開催日時を取得するプログラムのついでにCodeforcesのほうもテキトーに書いた.こっちのほうはページの仕様が変わりそうだしサーバが不調そうなのでCodeforcesと同じようにベータ扱い.
対象URL:http://www.codeforces.com/contests

使用ライブラリ

HTMLParser(http://htmlparser.sourceforge.net/)

出力(12月25日時点)

>UpcomingContests
CodeforcesContest [name=Codeforces Beta Round #48, isPast=false, data=2010年12月29日(水)01時00分, duration=150]
>ContestHistory
CodeforcesContest [name=Codeforces Beta Round #47, isPast=true, data=2010年12月20日, duration=120]
CodeforcesContest [name=Codeforces Beta Round #46 (Div. 2), isPast=true, data=2010年12月17日, duration=120]
CodeforcesContest [name=School Personal Contest #3 (Winter Computer School 2010/11), isPast=true, data=2010年12月12日, duration=180]
CodeforcesContest [name=Codeforces Beta Round #44 (Div. 2), isPast=true, data=2010年12月07日, duration=120]
CodeforcesContest [name=School Personal Contest #2 (Winter Computer School 2010/11), isPast=true, data=2010年12月05日, duration=180]
...

コード

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;

import org.htmlparser.Parser;
import org.htmlparser.filters.HasAttributeFilter;
import org.htmlparser.filters.NodeClassFilter;
import org.htmlparser.tags.TableColumn;
import org.htmlparser.tags.TableRow;
import org.htmlparser.tags.TableTag;
import org.htmlparser.util.NodeList;

public class Main {

	public static void main(String[] args) throws Exception {
		Main main = new Main();
		System.out.println(">UpcomingContests");
		for(CodeforcesContest contest : main.getUpcomingContests()){
			System.out.println(contest);
		}
		
		System.out.println(">ContestHistory");
		for(CodeforcesContest contest : main.getContestHistory()){
			System.out.println(contest);
		}
	}

	private static final String url = "http://codeforces.com/contests";
	private static final SimpleDateFormat parseFormat1;
	private static final SimpleDateFormat parseFormat2;
	private static final SimpleDateFormat printFormat1;
	private static final SimpleDateFormat printFormat2;

	static {
		Locale us = Locale.US, jp = Locale.JAPAN;

		parseFormat1 = new SimpleDateFormat("MM/dd/yyyy hh:mma", us);
		parseFormat1.setTimeZone(TimeZone.getTimeZone("GMT+03:00"));
		parseFormat2 = new SimpleDateFormat("MM/dd/yyyy", us);
		parseFormat2.setTimeZone(TimeZone.getTimeZone("GMT+03:00"));

		printFormat1 = new SimpleDateFormat("yyyy年MM月dd日(E)HH時mm分", jp);
		printFormat1.setTimeZone(TimeZone.getTimeZone("GMT+09:00"));
		printFormat2 = new SimpleDateFormat("yyyy年MM月dd日", jp);
		printFormat2.setTimeZone(TimeZone.getTimeZone("GMT+09:00"));
	}

	public Main() {
		try {
			Parser parser = new Parser(url);
			// NodeList list = parser.parse(new HasAttributeFilter("class",
			// "contestlist"));
			NodeList list = parser.parse(new HasAttributeFilter("class",
					"datatable"));
			NodeList tables = list.extractAllNodesThatMatch(
					new NodeClassFilter(TableTag.class), true);

			TableTag ucTable = (TableTag) tables.elementAt(0);
			upcomingContests = getContests(ucTable, false);
			TableTag chTable = (TableTag) tables.elementAt(1);
			contestHistory = getContests(chTable, true);
		} catch (Exception e) {
			// log (network)
		}

	}

	private List<CodeforcesContest> getContests(TableTag table, boolean isPast) {
		List<CodeforcesContest> contests = new ArrayList<CodeforcesContest>();
		for (TableRow row : Arrays.copyOfRange(table.getRows(), 1,
				table.getRowCount())) {
			CodeforcesContest contest = new CodeforcesContest();
			TableColumn[] cols = row.getColumns();
			contest.setName(toName(cols[0]));
			contest.setDate(toDate(cols[1]), isPast);
			contest.setDuration(toDurMins(cols[2]));
			contests.add(contest);
		}
		return contests;
	}

	private List<CodeforcesContest> upcomingContests;
	private List<CodeforcesContest> contestHistory;

	public List<CodeforcesContest> getUpcomingContests() {
		return upcomingContests;
	}

	public List<CodeforcesContest> getContestHistory() {
		return contestHistory;
	}

	private String toName(TableColumn col) {
		return col.getFirstChild().toPlainTextString().trim();
	}

	private Date toDate(TableColumn col) {
		String str = col.toPlainTextString().trim();
		Date r = null;
		try {
			if (str.matches("\\d{2}/\\d{2}/\\d{4}")) {
				r = parseFormat2.parse(str);
			} else {
				r = parseFormat1.parse(str);
			}
		} catch (Exception e) {
			// log
		}
		return r;
	}

	private int toDurMins(TableColumn col) {
		String[] strs = col.toPlainTextString().trim().split(":");
		return 60 * new Integer(strs[0]) + new Integer(strs[1]);
	}

	static public class CodeforcesContest {

		private String name;
		private boolean isPast;
		private Date date;
		private int duration; // minutes

		@Override
		public String toString() {
			DateFormat format = isPast ? printFormat2 : printFormat1;
			return "CodeforcesContest [name=" + name + ", isPast=" + isPast
					+ ", date=" + format.format(date) + ", duration="
					+ duration + "]";
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public Date getDate() {
			return date;
		}

		public void setDate(Date date, boolean isPast) {
			this.date = date;
			this.isPast = isPast;
		}

		public boolean isPast() {
			return isPast;
		}

		public int getDuration() {
			return duration;
		}

		public void setDuration(int duration) {
			this.duration = duration;
		}
	}
}