package com.github.kmkt.util;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* W3CDTF 形式の日時フォーマットをパース・出力するクラス。
*
* http://www.kanzaki.com/docs/html/dtf.html#w3cdtf より
* (1) 年のみ
* YYYY(例:2001)
* (2) 年月
* YYYY-MM(例:2001-08)
* (3) 年月日
* YYYY-MM-DD(例:2001-08-02)
* (4) 年月日および時分
* YYYY-MM-DDThh:mmTZD(例:2001-08-02T10:45+09:00)
* (5) 年月日および時分秒
* YYYY-MM-DDThh:mm:ssTZD(例:2001-08-02T10:45:23+09:00)
* (6) 年月日および時分秒および小数部分
* YYYY-MM-DDThh:mm:ss.sTZD(例:2001-08-02T10:45:23.5+09:00)
*
* License : MIT License
*/
public class W3CDTF {
private static final Pattern W3CDTF_PATTERN = Pattern.compile("(\\d{4})(?:-(\\d{2})(?:-(\\d{2})(?:T(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d+))?)?(?:(?:([\\+-])(\\d{2})\\:(\\d{2}))|(Z)))?)?)?");
// 文字列形式
public static final int YEAR = 1; // (1) 年のみ
public static final int YEAR_MONTH = 2; // (2) 年月
public static final int FULL_DATE = 3; // (3) 年月日
public static final int DATE_HOURS_MINUTE = 4; // (4) 年月日および時分
public static final int DATE_HOURS_MINUTE_SECOND = 5; // (5) 年月日および時分秒
public static final int FULL = 6; // (6) 年月日および時分秒および小数部分
/**
* W3CDTF 形式の日時フォーマットをパースする。
*
* @param w3cdtf W3CDTF 形式の文字列
* @return パースされた Calendar
*/
public static Calendar parse(String w3cdtf) {
if (w3cdtf == null || "".equals(w3cdtf))
throw new IllegalArgumentException("w3cdtf should not be null or empty.");
Matcher m = W3CDTF_PATTERN.matcher(w3cdtf);
if (!m.matches())
return null;
Calendar result = Calendar.getInstance();
result.clear();
result.set(Calendar.YEAR, Integer.parseInt(m.group(1)));
result.set(Calendar.MONTH, m.group(2) != null ? Integer.parseInt(m.group(2)) - 1: 0);
result.set(Calendar.DAY_OF_MONTH, m.group(3) != null ? Integer.parseInt(m.group(3)) : 1);
if ((m.group(8) != null && m.group(9) != null && m.group(10) != null) || "Z".equals(m.group(11))) {
result.set(Calendar.HOUR_OF_DAY, m.group(4) != null ? Integer.parseInt(m.group(4)) : 0);
result.set(Calendar.MINUTE, m.group(5) != null ? Integer.parseInt(m.group(5)) : 0);
result.set(Calendar.SECOND, m.group(6) != null ? Integer.parseInt(m.group(6)) : 0);
if (m.group(7) != null) {
result.set(Calendar.MILLISECOND, (int) (Double.parseDouble("0." + m.group(7)) * 1000.0));
} else {
result.set(Calendar.MILLISECOND, 0);
}
if ("Z".equals(m.group(11))) {
TimeZone tz = TimeZone.getTimeZone("Europ/London");
tz.setRawOffset(0);
result.setTimeZone(tz);
} else {
TimeZone tz = TimeZone.getTimeZone("Europ/London");
int offset_hour = Integer.parseInt(m.group(9));
int offset_min = Integer.parseInt(m.group(10));
int offset = (offset_hour*60+offset_min) * 60 * 1000;
if ("-".equals(m.group(8)))
offset = -offset;
tz.setRawOffset(offset);
result.setTimeZone(tz);
}
}
return result;
}
/**
* Date から W3CDTF形式文字列を得る。
* @param date 変換元 Date オブジェクト
* @param format 出力形式
* @return W3CDTF形式文字列
*/
public static String format(Date date, int format) {
return format(date, TimeZone.getDefault(), format);
}
/**
* Date から W3CDTF形式文字列を得る。
* @param date 変換元 Date オブジェクト
* @param tz date のタイムゾーン
* @param format 出力形式
* @return W3CDTF形式文字列
*/
public static String format(Date date, TimeZone tz, int format) {
if (date == null)
throw new IllegalArgumentException("date should not be null.");
if (tz == null)
throw new IllegalArgumentException("tz should not be null.");
if (format < YEAR || FULL < format)
throw new IllegalArgumentException("invalid format value.");
Calendar c = Calendar.getInstance(tz);
c.setTime(date);
return format(c, format);
}
/**
* Calendar から W3CDTF形式文字列を得る。
* @param calendar 変換元 Calendar オブジェクト
* @param format 出力形式
* @return W3CDTF形式文字列
*/
public static String format(Calendar calendar, int format) {
if (calendar == null)
throw new IllegalArgumentException("calendar should not be null.");
if (format < YEAR || FULL < format)
throw new IllegalArgumentException("invalid format value.");
StringBuilder buf = new StringBuilder();
if (YEAR <= format) {
buf.append(String.format("%04d", calendar.get(Calendar.YEAR)));
}
if (YEAR_MONTH <= format) {
buf.append(String.format("-%02d", calendar.get(Calendar.MONTH) + 1));
}
if (FULL_DATE <= format) {
buf.append(String.format("-%02d", calendar.get(Calendar.DAY_OF_MONTH)));
}
if (DATE_HOURS_MINUTE <= format) {
buf.append(String.format("T%02d:%02d", calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE)));
}
if (DATE_HOURS_MINUTE_SECOND <= format) {
buf.append(String.format(":%02d", calendar.get(Calendar.SECOND)));
}
if (FULL == format) {
buf.append(String.format(".%03d", calendar.get(Calendar.MILLISECOND)));
}
if (DATE_HOURS_MINUTE <= format) {
TimeZone tz = calendar.getTimeZone();
if (tz.getRawOffset() == 0) {
buf.append("Z");
} else {
int offset = tz.getRawOffset() / (60*1000);
if (0 < offset) {
buf.append(String.format("+%02d:%02d", offset/60, offset%60));
} else {
offset = -offset;
buf.append(String.format("-%02d:%02d", offset/60, offset%60));
}
}
}
return buf.toString();
}
}