ant でロジックを記述するのはなかなか大変であるし、今となっては潰しのきかないスキルでもあり、未来ある若者に ant を使わせるべきではない。
しかし、世の中には ant を利用することを強いられている若者もいると聞く。
そういった中で、タスクを groovy でこなす方法を紹介しよう。
groovy でタスクを定義できれば、コードの可読性があがりハッピーになれるはずだ。
groovy でタスクを定義するためには groovy 本体を自動で取得する必要があるが、これは maven ant tasks で行う。maven ant tasks は、ant から maven の依存関係解決を呼べるようにしたライブラリである。
maven ant tasks はすでにインストール済みである場合も多いと思うが、入っていない場合は以下のようにすればよい。
mkdir ~/.ant/lib/
wget http://ftp.meisei-u.ac.jp/mirror/apache/dist/maven/ant-tasks/2.1.3/binaries/maven-ant-tasks-2.1.3.jar -P ~/.ant/lib/
さて、準備は整った。あとは以下のように記述すればよいだけである。
極めて簡単である。
<project name="demo" default="dist" xmlns:artifact="antlib:org.apache.maven.artifact.ant">
<target name="dist">
<!-- groovy-all を依存として宣言 -->
<artifact:dependencies pathId="groovy.classpath">
<dependency groupId="org.codehaus.groovy" artifactId="groovy-all" version="2.4.4" scope="compile"/>
</artifact:dependencies>
<!-- groovy-all を利用して groovy タグを有効化 -->
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="groovy.classpath"/>
<!-- build.groovy を実行 -->
<groovy src="build.groovy"/>
</target>
</project>
以上、ant から groovy を呼び出す方法を紹介した。
groovy スクリプトを実行できるようになったのはいいが、普通に groovy script で記述していくのは辛い。しかし ant から呼ばれた場合、ant の機能をつめこんだ AntBuilder といオブジェクトが渡されてくる。これを利用して、ant の機能を利用してコードを書いていけば良い。
AntBuilder についての解説はこのへんを見れば良い。
http://docs.groovy-lang.org/latest/html/documentation/ant-builder.html
良いのだが、これを直接利用するのは辛いので、ヘルパークラスを定義してこれを利用する。
以下は基本的なオペレーションをラップしている。直接 Ant を利用するのではなく、このヘルパクラスを利用して操作を行っていく。
import groovy.util.AntBuilder
import groovy.xml.NamespaceBuilder
import org.apache.tools.ant.BuildException
import org.codehaus.groovy.ant.AntProjectPropertiesDelegate
public abstract class AbstractDeployer {
AntBuilder ant
AntProjectPropertiesDelegate props
def mvn
def AbstractDeployer(AntBuilder ant, AntProjectPropertiesDelegate props) {
this.ant = ant
this.props = props
// Maven Ant Tasks
this.mvn = NamespaceBuilder.newInstance(ant, 'antlib:org.apache.maven.artifact.ant')
}
/**
* Copy files from {@code src} to {@code dst} recursively.
*/
def copyRecursive(String src, String dst) {
if (new File(src).exists()) {
ant.copy(todir:dst, overwrite:"true") {
fileset(dir:src)
}
} else {
echo("There is no ${dst}")
}
}
/**
* Get property. If there's no value for the key, this method throws Exception.
*/
String propOrDie(String key) {
def val = props[key]
if (val == null) {
throw new BuildException("No such property: '${key}'")
}
return val
}
/**
* Get property value for {@code key}.
*/
String prop(String key) {
return props[key]
}
/**
* Fetch {@code groupId}:{@code artifactId}:{@code version} and extract it into {@code webappOutput}.
*/
def fetchAndUnwar(String groupId, String artifactId, String version, String webappOutput) {
// fetch war
mvn.dependencies(
filesetId:'dependency.fileset.war',
versionsId:'dependency.versions.war',
useScope:'compile') {
dependency(
groupId:groupId,
artifactId:artifactId,
version:version,
type:'war',
scope:'compile')
}
ant.copy(todir:'target/artifacts') {
fileset(refid:"dependency.fileset.war")
chainedmapper {
mapper(
classname:"org.apache.maven.artifact.ant.VersionMapper",
from:'${dependency.versions.war}')
mapper(type:'flatten')
}
}
// extract war file
ant.unwar(
src:"target/artifacts/${artifactId}.war",
dest:webappOutput)
}
/**
* Display {@code message}.
*/
def echo(String message) {
ant.echo(message:message)
}
/**
* load properties from {@code srcFile}.
*/
def loadProps(String srcFile) {
if (new File(ant.project.baseDir, srcFile).exists()) {
echo("loading ${srcFile}")
ant.loadproperties(srcFile:srcFile)
} else {
echo("There is no ${srcFile}")
}
}
}
実際のビルドスクリプトは以下のような形式でやればよろしい。本来ならば、もっと DSL っぽく記述したほうがよいのだが、結局のところコンパイル時にある程度チェックできたほうが生産性が高まるので、@CompileStatic
を付けられることを重視してクラスを定義している。もっと綺麗に書く方法があれば教えていただきたい。
このコードを見れば、なにがどういう手順で行われるかは一目瞭然であり、カスタマイズも簡単である。
import groovy.transform.CompileStatic
import groovy.util.AntBuilder
import org.codehaus.groovy.ant.AntProjectPropertiesDelegate
import AbstractDeployer
@CompileStatic
class Deployer extends AbstractDeployer {
def Deployer(AntBuilder ant, AntProjectPropertiesDelegate properties) {
super(ant, properties)
}
def run() {
echo("building...")
fetchAndUnwar("org.glassfish.admingui", 'war', '10.0-b28', 'target/webapp')
// overwrite files
copyRecursive("resources", "target/webapp/resources")
}
}
new Deployer(ant, properties).run()
以上、簡単ですが ant から groovy を用いてコードを記述する方法の紹介でした。
DateTimeFormatter を利用して曜日などのフォーマットを行おうとすると、実際どういうふうにフォーマットされるのかよくわからんということになる。
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html をちらっと見ると、以下のように書いてあって、各シンボルが何を表すのかよくわからない。
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use
GGG
などのように、同じ文字列を複数回続けることによって任意の形式で出力することができるのだが、この様式は以下のようになっている。
Date fields: Pattern letters to output a date.
Pattern Count Equivalent builder methods
------- ----- --------------------------
G 1 appendText(ChronoField.ERA, TextStyle.SHORT)
GG 2 appendText(ChronoField.ERA, TextStyle.SHORT)
GGG 3 appendText(ChronoField.ERA, TextStyle.SHORT)
GGGG 4 appendText(ChronoField.ERA, TextStyle.FULL)
GGGGG 5 appendText(ChronoField.ERA, TextStyle.NARROW)
u 1 appendValue(ChronoField.YEAR, 1, 19, SignStyle.NORMAL);
uu 2 appendValueReduced(ChronoField.YEAR, 2, 2000);
uuu 3 appendValue(ChronoField.YEAR, 3, 19, SignStyle.NORMAL);
u..u 4..n appendValue(ChronoField.YEAR, n, 19, SignStyle.EXCEEDS_PAD);
y 1 appendValue(ChronoField.YEAR_OF_ERA, 1, 19, SignStyle.NORMAL);
yy 2 appendValueReduced(ChronoField.YEAR_OF_ERA, 2, 2000);
yyy 3 appendValue(ChronoField.YEAR_OF_ERA, 3, 19, SignStyle.NORMAL);
y..y 4..n appendValue(ChronoField.YEAR_OF_ERA, n, 19, SignStyle.EXCEEDS_PAD);
Y 1 append special localized WeekFields element for numeric week-based-year
YY 2 append special localized WeekFields element for reduced numeric week-based-year 2 digits;
YYY 3 append special localized WeekFields element for numeric week-based-year (3, 19, SignStyle.NORMAL);
Y..Y 4..n append special localized WeekFields element for numeric week-based-year (n, 19, SignStyle.EXCEEDS_PAD);
Q 1 appendValue(IsoFields.QUARTER_OF_YEAR);
QQ 2 appendValue(IsoFields.QUARTER_OF_YEAR, 2);
QQQ 3 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.SHORT)
QQQQ 4 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.FULL)
QQQQQ 5 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.NARROW)
q 1 appendValue(IsoFields.QUARTER_OF_YEAR);
qq 2 appendValue(IsoFields.QUARTER_OF_YEAR, 2);
qqq 3 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.SHORT_STANDALONE)
qqqq 4 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.FULL_STANDALONE)
qqqqq 5 appendText(IsoFields.QUARTER_OF_YEAR, TextStyle.NARROW_STANDALONE)
M 1 appendValue(ChronoField.MONTH_OF_YEAR);
MM 2 appendValue(ChronoField.MONTH_OF_YEAR, 2);
MMM 3 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT)
MMMM 4 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL)
MMMMM 5 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.NARROW)
L 1 appendValue(ChronoField.MONTH_OF_YEAR);
LL 2 appendValue(ChronoField.MONTH_OF_YEAR, 2);
LLL 3 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.SHORT_STANDALONE)
LLLL 4 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL_STANDALONE)
LLLLL 5 appendText(ChronoField.MONTH_OF_YEAR, TextStyle.NARROW_STANDALONE)
w 1 append special localized WeekFields element for numeric week-of-year
ww 1 append special localized WeekFields element for numeric week-of-year, zero-padded
W 1 append special localized WeekFields element for numeric week-of-month
d 1 appendValue(ChronoField.DAY_OF_MONTH)
dd 2 appendValue(ChronoField.DAY_OF_MONTH, 2)
D 1 appendValue(ChronoField.DAY_OF_YEAR)
DD 2 appendValue(ChronoField.DAY_OF_YEAR, 2)
DDD 3 appendValue(ChronoField.DAY_OF_YEAR, 3)
F 1 appendValue(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH)
E 1 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
EE 2 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
EEE 3 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
EEEE 4 appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL)
EEEEE 5 appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW)
e 1 append special localized WeekFields element for numeric day-of-week
ee 2 append special localized WeekFields element for numeric day-of-week, zero-padded
eee 3 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT)
eeee 4 appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL)
eeeee 5 appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW)
c 1 append special localized WeekFields element for numeric day-of-week
ccc 3 appendText(ChronoField.DAY_OF_WEEK, TextStyle.SHORT_STANDALONE)
cccc 4 appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL_STANDALONE)
ccccc 5 appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)
Time fields: Pattern letters to output a time.
Pattern Count Equivalent builder methods
------- ----- --------------------------
a 1 appendText(ChronoField.AMPM_OF_DAY, TextStyle.SHORT)
h 1 appendValue(ChronoField.CLOCK_HOUR_OF_AMPM)
hh 2 appendValue(ChronoField.CLOCK_HOUR_OF_AMPM, 2)
H 1 appendValue(ChronoField.HOUR_OF_DAY)
HH 2 appendValue(ChronoField.HOUR_OF_DAY, 2)
k 1 appendValue(ChronoField.CLOCK_HOUR_OF_DAY)
kk 2 appendValue(ChronoField.CLOCK_HOUR_OF_DAY, 2)
K 1 appendValue(ChronoField.HOUR_OF_AMPM)
KK 2 appendValue(ChronoField.HOUR_OF_AMPM, 2)
m 1 appendValue(ChronoField.MINUTE_OF_HOUR)
mm 2 appendValue(ChronoField.MINUTE_OF_HOUR, 2)
s 1 appendValue(ChronoField.SECOND_OF_MINUTE)
ss 2 appendValue(ChronoField.SECOND_OF_MINUTE, 2)
S..S 1..n appendFraction(ChronoField.NANO_OF_SECOND, n, n, false)
A 1 appendValue(ChronoField.MILLI_OF_DAY)
A..A 2..n appendValue(ChronoField.MILLI_OF_DAY, n)
n 1 appendValue(ChronoField.NANO_OF_SECOND)
n..n 2..n appendValue(ChronoField.NANO_OF_SECOND, n)
N 1 appendValue(ChronoField.NANO_OF_DAY)
N..N 2..n appendValue(ChronoField.NANO_OF_DAY, n)
Zone ID: Pattern letters to output ZoneId.
Pattern Count Equivalent builder methods
------- ----- --------------------------
VV 2 appendZoneId()
z 1 appendZoneText(TextStyle.SHORT)
zz 2 appendZoneText(TextStyle.SHORT)
zzz 3 appendZoneText(TextStyle.SHORT)
zzzz 4 appendZoneText(TextStyle.FULL)
Zone offset: Pattern letters to output ZoneOffset.
Pattern Count Equivalent builder methods
------- ----- --------------------------
O 1 appendLocalizedOffsetPrefixed(TextStyle.SHORT);
OOOO 4 appendLocalizedOffsetPrefixed(TextStyle.FULL);
X 1 appendOffset("+HHmm","Z")
XX 2 appendOffset("+HHMM","Z")
XXX 3 appendOffset("+HH:MM","Z")
XXXX 4 appendOffset("+HHMMss","Z")
XXXXX 5 appendOffset("+HH:MM:ss","Z")
x 1 appendOffset("+HHmm","+00")
xx 2 appendOffset("+HHMM","+0000")
xxx 3 appendOffset("+HH:MM","+00:00")
xxxx 4 appendOffset("+HHMMss","+0000")
xxxxx 5 appendOffset("+HH:MM:ss","+00:00")
Z 1 appendOffset("+HHMM","+0000")
ZZ 2 appendOffset("+HHMM","+0000")
ZZZ 3 appendOffset("+HHMM","+0000")
ZZZZ 4 appendLocalizedOffset(TextStyle.FULL);
ZZZZZ 5 appendOffset("+HH:MM:ss","Z")
Modifiers: Pattern letters that modify the rest of the pattern:
Pattern Count Equivalent builder methods
------- ----- --------------------------
[ 1 optionalStart()
] 1 optionalEnd()
p..p 1..n padNext(n)
これは、 https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatterBuilder.html#appendPattern-java.lang.String- のコピペである。DateTimeFormatter のフォーマットの仕方が DateTimeFormatterBuilder の方にあるので、たどりつくのが難しい。
実際どのようにフォーマットされるのかを確認する
実際にどのようにフォーマットされるのかを確認しておけば、後々便利であろう。
package datetimeformattersample;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
/**
*
* @author tokuhirom
*/
public class DateTimeFormatterSample {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String[][] patterns = new String[][]{
new String[]{"G", "appendText(ChronoField.ERA,"},
new String[]{"GG", "appendText(ChronoField.ERA,"},
new String[]{"GGG", "appendText(ChronoField.ERA,"},
new String[]{"GGGG", "appendText(ChronoField.ERA,"},
new String[]{"GGGGG", "appendText(ChronoField.ERA,"},
new String[]{"", ""},
new String[]{"u", "appendValue(ChronoField.YEAR,"},
new String[]{"uu", "appendValueReduced(ChronoField.YEAR,"},
new String[]{"uuu", "appendValue(ChronoField.YEAR,"},
new String[]{"uuuu", "appendValue(ChronoField.YEAR,"},
new String[]{"y", "appendValue(ChronoField.YEAR_OF_ERA,"},
new String[]{"yy", "appendValueReduced(ChronoField.YEAR_OF_ERA,"},
new String[]{"yyy", "appendValue(ChronoField.YEAR_OF_ERA,"},
new String[]{"yyyy", "appendValue(ChronoField.YEAR_OF_ERA,"},
new String[]{"Y", "append"},
new String[]{"YY", "append"},
new String[]{"YYY", "append"},
new String[]{"YYYY", "append"},
new String[]{"", ""},
new String[]{"Q", "appendValue(IsoFields.QUARTER_OF_YEAR);"},
new String[]{"QQ", "appendValue(IsoFields.QUARTER_OF_YEAR,"},
new String[]{"QQQ", "appendText(IsoFields.QUARTER_OF_YEAR,"},
new String[]{"QQQQ", "appendText(IsoFields.QUARTER_OF_YEAR,"},
new String[]{"QQQQQ", "appendText(IsoFields.QUARTER_OF_YEAR,"},
new String[]{"q", "appendValue(IsoFields.QUARTER_OF_YEAR);"},
new String[]{"qq", "appendValue(IsoFields.QUARTER_OF_YEAR,"},
new String[]{"qqq", "appendText(IsoFields.QUARTER_OF_YEAR,"},
new String[]{"qqqq", "appendText(IsoFields.QUARTER_OF_YEAR,"},
new String[]{"qqqqq", "appendText(IsoFields.QUARTER_OF_YEAR,"},
new String[]{"", ""},
new String[]{"M", "appendValue(ChronoField.MONTH_OF_YEAR);"},
new String[]{"MM", "appendValue(ChronoField.MONTH_OF_YEAR,"},
new String[]{"MMM", "appendText(ChronoField.MONTH_OF_YEAR,"},
new String[]{"MMMM", "appendText(ChronoField.MONTH_OF_YEAR,"},
new String[]{"MMMMM", "appendText(ChronoField.MONTH_OF_YEAR,"},
new String[]{"L", "appendValue(ChronoField.MONTH_OF_YEAR);"},
new String[]{"LL", "appendValue(ChronoField.MONTH_OF_YEAR,"},
new String[]{"LLL", "appendText(ChronoField.MONTH_OF_YEAR,"},
new String[]{"LLLL", "appendText(ChronoField.MONTH_OF_YEAR,"},
new String[]{"LLLLL", "appendText(ChronoField.MONTH_OF_YEAR,"},
new String[]{"", ""},
new String[]{"w", "append"},
new String[]{"ww", "append"},
new String[]{"W", "append"},
new String[]{"d", "appendValue(ChronoField.DAY_OF_MONTH)"},
new String[]{"dd", "appendValue(ChronoField.DAY_OF_MONTH,"},
new String[]{"D", "appendValue(ChronoField.DAY_OF_YEAR)"},
new String[]{"DD", "appendValue(ChronoField.DAY_OF_YEAR,"},
new String[]{"DDD", "appendValue(ChronoField.DAY_OF_YEAR,"},
new String[]{"F", "appendValue(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH)"},
new String[]{"E", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"EE", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"EEE", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"EEEE", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"EEEEE", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"e", "append"},
new String[]{"ee", "append"},
new String[]{"eee", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"eeee", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"eeeee", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"c", "append"},
new String[]{"ccc", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"cccc", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"ccccc", "appendText(ChronoField.DAY_OF_WEEK,"},
new String[]{"a", "appendText(ChronoField.AMPM_OF_DAY,"},
new String[]{"h", "appendValue(ChronoField.CLOCK_HOUR_OF_AMPM)"},
new String[]{"hh", "appendValue(ChronoField.CLOCK_HOUR_OF_AMPM,"},
new String[]{"H", "appendValue(ChronoField.HOUR_OF_DAY)"},
new String[]{"HH", "appendValue(ChronoField.HOUR_OF_DAY,"},
new String[]{"k", "appendValue(ChronoField.CLOCK_HOUR_OF_DAY)"},
new String[]{"kk", "appendValue(ChronoField.CLOCK_HOUR_OF_DAY,"},
new String[]{"K", "appendValue(ChronoField.HOUR_OF_AMPM)"},
new String[]{"KK", "appendValue(ChronoField.HOUR_OF_AMPM,"},
new String[]{"m", "appendValue(ChronoField.MINUTE_OF_HOUR)"},
new String[]{"mm", "appendValue(ChronoField.MINUTE_OF_HOUR,"},
new String[]{"s", "appendValue(ChronoField.SECOND_OF_MINUTE)"},
new String[]{"ss", "appendValue(ChronoField.SECOND_OF_MINUTE,"},
new String[]{"", ""},
new String[]{"SSSS", "appendFraction(ChronoField.NANO_OF_SECOND,"},
new String[]{"A", "appendValue(ChronoField.MILLI_OF_DAY)"},
new String[]{"AAAA", "appendValue(ChronoField.MILLI_OF_DAY,"},
new String[]{"n", "appendValue(ChronoField.NANO_OF_SECOND)"},
new String[]{"nnnn", "appendValue(ChronoField.NANO_OF_SECOND,"},
new String[]{"N", "appendValue(ChronoField.NANO_OF_DAY)"},
new String[]{"NNNN", "appendValue(ChronoField.NANO_OF_DAY,"},
new String[]{"VV", "appendZoneId()"},
new String[]{"z", "appendZoneText(TextStyle.SHORT)"},
new String[]{"zz", "appendZoneText(TextStyle.SHORT)"},
new String[]{"zzz", "appendZoneText(TextStyle.SHORT)"},
new String[]{"zzzz", "appendZoneText(TextStyle.FULL)"},
new String[]{"O", "appendLocalizedOffsetPrefixed(TextStyle.SHORT);"},
new String[]{"OOOO", "appendLocalizedOffsetPrefixed(TextStyle.FULL);"},
new String[]{"X", "appendOffset(\"+HHmm\",\"Z\")"},
new String[]{"XX", "appendOffset(\"+HHMM\",\"Z\")"},
new String[]{"XXX", "appendOffset(\"+HH:MM\",\"Z\")"},
new String[]{"XXXX", "appendOffset(\"+HHMMss\",\"Z\")"},
new String[]{"XXXXX", "appendOffset(\"+HH:MM:ss\",\"Z\")"},
new String[]{"x", "appendOffset(\"+HHmm\",\"+00\")"},
new String[]{"xx", "appendOffset(\"+HHMM\",\"+0000\")"},
new String[]{"xxx", "appendOffset(\"+HH:MM\",\"+00:00\")"},
new String[]{"xxxx", "appendOffset(\"+HHMMss\",\"+0000\")"},
new String[]{"xxxxx", "appendOffset(\"+HH:MM:ss\",\"+00:00\")"},
new String[]{"Z", "appendOffset(\"+HHMM\",\"+0000\")"},
new String[]{"ZZ", "appendOffset(\"+HHMM\",\"+0000\")"},
new String[]{"ZZZ", "appendOffset(\"+HHMM\",\"+0000\")"},
new String[]{"ZZZZ", "appendLocalizedOffset(TextStyle.FULL);"},
new String[]{"ZZZZZ", "appendOffset(\"+HH:MM:ss\",\"Z\")"}
};
ZonedDateTime zonedDateTime = LocalDateTime
.parse("2015-07-13T12:27:04")
.atZone(ZoneId.of("Asia/Tokyo"));
System.out.printf("%-7s : %-19s\t%-19s // %s\n", "Pattern", "English", "Japanese", "Comment");
for (String[] pattern : patterns) {
try {
DateTimeFormatter enFormatter = DateTimeFormatter.ofPattern(pattern[0], Locale.ENGLISH);
String en = zonedDateTime.format(enFormatter);
DateTimeFormatter jpFormatter = DateTimeFormatter.ofPattern(pattern[0], Locale.JAPAN);
String jp = zonedDateTime.format(jpFormatter);
System.out.printf("%-7s : %-19s\t%-19s // %s\n", pattern[0], en, jp, pattern[1]);
} catch (java.time.DateTimeException e) {
System.err.printf("cannot format '%s': %s", pattern[0], e.getMessage());
}
}
}
}
出力例は以下のようになる。
Pattern : English Japanese // Comment
G : AD 西暦 // appendText(ChronoField.ERA,
GG : AD 西暦 // appendText(ChronoField.ERA,
GGG : AD 西暦 // appendText(ChronoField.ERA,
GGGG : Anno Domini 西暦 // appendText(ChronoField.ERA,
GGGGG : A A // appendText(ChronoField.ERA,
: //
u : 2015 2015 // appendValue(ChronoField.YEAR,
uu : 15 15 // appendValueReduced(ChronoField.YEAR,
uuu : 2015 2015 // appendValue(ChronoField.YEAR,
uuuu : 2015 2015 // appendValue(ChronoField.YEAR,
y : 2015 2015 // appendValue(ChronoField.YEAR_OF_ERA,
yy : 15 15 // appendValueReduced(ChronoField.YEAR_OF_ERA,
yyy : 2015 2015 // appendValue(ChronoField.YEAR_OF_ERA,
yyyy : 2015 2015 // appendValue(ChronoField.YEAR_OF_ERA,
Y : 2015 2015 // append
YY : 15 15 // append
YYY : 2015 2015 // append
YYYY : 2015 2015 // append
: //
Q : 3 3 // appendValue(IsoFields.QUARTER_OF_YEAR);
QQ : 03 03 // appendValue(IsoFields.QUARTER_OF_YEAR,
QQQ : Q3 Q3 // appendText(IsoFields.QUARTER_OF_YEAR,
QQQQ : 3rd quarter 第3四半期 // appendText(IsoFields.QUARTER_OF_YEAR,
QQQQQ : 3 3 // appendText(IsoFields.QUARTER_OF_YEAR,
q : 3 3 // appendValue(IsoFields.QUARTER_OF_YEAR);
qq : 03 03 // appendValue(IsoFields.QUARTER_OF_YEAR,
qqq : 3 3 // appendText(IsoFields.QUARTER_OF_YEAR,
qqqq : 3 3 // appendText(IsoFields.QUARTER_OF_YEAR,
qqqqq : 3 3 // appendText(IsoFields.QUARTER_OF_YEAR,
: //
M : 7 7 // appendValue(ChronoField.MONTH_OF_YEAR);
MM : 07 07 // appendValue(ChronoField.MONTH_OF_YEAR,
MMM : Jul 7 // appendText(ChronoField.MONTH_OF_YEAR,
MMMM : July 7月 // appendText(ChronoField.MONTH_OF_YEAR,
MMMMM : J J // appendText(ChronoField.MONTH_OF_YEAR,
L : 7 7 // appendValue(ChronoField.MONTH_OF_YEAR);
LL : 07 07 // appendValue(ChronoField.MONTH_OF_YEAR,
LLL : 7 7 // appendText(ChronoField.MONTH_OF_YEAR,
LLLL : 7 7 // appendText(ChronoField.MONTH_OF_YEAR,
LLLLL : 7 7 // appendText(ChronoField.MONTH_OF_YEAR,
: //
w : 29 29 // append
cannot format 'DD': Field DayOfYear cannot be printed as the value 194 exceeds the maximum print width of 2
ww : 29 29 // append
W : 3 3 // append
d : 13 13 // appendValue(ChronoField.DAY_OF_MONTH)
dd : 13 13 // appendValue(ChronoField.DAY_OF_MONTH,
D : 194 194 // appendValue(ChronoField.DAY_OF_YEAR)
DDD : 194 194 // appendValue(ChronoField.DAY_OF_YEAR,
F : 6 6 // appendValue(ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH)
E : Mon 月 // appendText(ChronoField.DAY_OF_WEEK,
EE : Mon 月 // appendText(ChronoField.DAY_OF_WEEK,
EEE : Mon 月 // appendText(ChronoField.DAY_OF_WEEK,
EEEE : Monday 月曜日 // appendText(ChronoField.DAY_OF_WEEK,
EEEEE : M 月 // appendText(ChronoField.DAY_OF_WEEK,
e : 2 2 // append
ee : 02 02 // append
eee : Mon 月 // appendText(ChronoField.DAY_OF_WEEK,
eeee : Monday 月曜日 // appendText(ChronoField.DAY_OF_WEEK,
eeeee : M 月 // appendText(ChronoField.DAY_OF_WEEK,
c : 2 2 // append
ccc : Mon 月 // appendText(ChronoField.DAY_OF_WEEK,
cannot format 'AAAA': Field MilliOfDay cannot be printed as the value 44824000 exceeds the maximum print width of 4
cccc : Monday 月曜日 // appendText(ChronoField.DAY_OF_WEEK,
ccccc : 1 1 // appendText(ChronoField.DAY_OF_WEEK,
a : PM 午後 // appendText(ChronoField.AMPM_OF_DAY,
cannot format 'NNNN': Field NanoOfDay cannot be printed as the value 44824000000000 exceeds the maximum print width of 4
h : 12 12 // appendValue(ChronoField.CLOCK_HOUR_OF_AMPM)
hh : 12 12 // appendValue(ChronoField.CLOCK_HOUR_OF_AMPM,
H : 12 12 // appendValue(ChronoField.HOUR_OF_DAY)
HH : 12 12 // appendValue(ChronoField.HOUR_OF_DAY,
k : 12 12 // appendValue(ChronoField.CLOCK_HOUR_OF_DAY)
kk : 12 12 // appendValue(ChronoField.CLOCK_HOUR_OF_DAY,
K : 0 0 // appendValue(ChronoField.HOUR_OF_AMPM)
KK : 00 00 // appendValue(ChronoField.HOUR_OF_AMPM,
m : 27 27 // appendValue(ChronoField.MINUTE_OF_HOUR)
mm : 27 27 // appendValue(ChronoField.MINUTE_OF_HOUR,
s : 4 4 // appendValue(ChronoField.SECOND_OF_MINUTE)
ss : 04 04 // appendValue(ChronoField.SECOND_OF_MINUTE,
: //
SSSS : 0000 0000 // appendFraction(ChronoField.NANO_OF_SECOND,
A : 44824000 44824000 // appendValue(ChronoField.MILLI_OF_DAY)
n : 0 0 // appendValue(ChronoField.NANO_OF_SECOND)
nnnn : 0000 0000 // appendValue(ChronoField.NANO_OF_SECOND,
N : 44824000000000 44824000000000 // appendValue(ChronoField.NANO_OF_DAY)
VV : Asia/Tokyo Asia/Tokyo // appendZoneId()
z : JST JST // appendZoneText(TextStyle.SHORT)
zz : JST JST // appendZoneText(TextStyle.SHORT)
zzz : JST JST // appendZoneText(TextStyle.SHORT)
zzzz : Japan Standard Time 日本標準時 // appendZoneText(TextStyle.FULL)
O : GMT+9 GMT+9 // appendLocalizedOffsetPrefixed(TextStyle.SHORT);
OOOO : GMT+09:00 GMT+09:00 // appendLocalizedOffsetPrefixed(TextStyle.FULL);
X : +09 +09 // appendOffset("+HHmm","Z")
XX : +0900 +0900 // appendOffset("+HHMM","Z")
XXX : +09:00 +09:00 // appendOffset("+HH:MM","Z")
XXXX : +0900 +0900 // appendOffset("+HHMMss","Z")
XXXXX : +09:00 +09:00 // appendOffset("+HH:MM:ss","Z")
x : +09 +09 // appendOffset("+HHmm","+00")
xx : +0900 +0900 // appendOffset("+HHMM","+0000")
xxx : +09:00 +09:00 // appendOffset("+HH:MM","+00:00")
xxxx : +0900 +0900 // appendOffset("+HHMMss","+0000")
xxxxx : +09:00 +09:00 // appendOffset("+HH:MM:ss","+00:00")
Z : +0900 +0900 // appendOffset("+HHMM","+0000")
ZZ : +0900 +0900 // appendOffset("+HHMM","+0000")
ZZZ : +0900 +0900 // appendOffset("+HHMM","+0000")
ZZZZ : GMT+09:00 GMT+09:00 // appendLocalizedOffset(TextStyle.FULL);
ZZZZZ : +09:00 +09:00 // appendOffset("+HH:MM:ss","Z")
この一覧表を参考にしながらコーディングすれば、簡単にフォーマットできる。と思う。
先日公開した、Java8 Date and Time の cheat sheet にも追記した。
https://gist.github.com/tokuhirom/0b7441684653cb85cefa