DateFormat
Java时间设置和计算的职责给了Calendar,把格式化的职责分配给了DateFormat,DateFormat呢,也是抽象类,创建对象的方式,也是getInstance()。创建出来什么样的对象,也是跟环境有关。而且,它不但可以格式化,还可以把字符串转化解析为Date对象。蛋疼的问题出现了,它格式化,只能格式化Date对象,而不能格式化Calendar对象,用之前,还要转化一下~~(⊙o⊙)…,可是,Date类没有时区啊!!
也就稍微试一下吧
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.CHINESE).format(new Date()));
输出:
2017年3月14日 星期二 下午01时51分35秒 CST
感觉DateFormat设计的,额,有点蛋疼,设计了很多getInstance,却让我觉得,都不怎么好用,也许是我太笨了。。。
SimpleDateFormat
SimpleDateFormat的对象创建,需要指定一个格式化(解析)的模板,也可以专门指定一个Locale对象。当然,你也可以什么都不指定,让它给你默认的模式。
SimpleDateFormat fmtChina = new SimpleDateFormat("E",Locale.SIMPLIFIED_CHINESE);
SimpleDateFormat fmtUS = new SimpleDateFormat("E",Locale.US);
System.out.println(fmtChina.format(new Date()));
System.out.println(fmtUS.format(new Date()));
System.out.println(new SimpleDateFormat().format(new Date()));
输出:
星期四
Thu
17-3-16 上午9:30
这里没什么好说的,以下重点讲一下字母模板。
字母模板
SimpleDateFormat使用一个字符串表示日期格式化和字符串解析的规则。就是我们之前常用的new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")。其中y表示年,M表示月,d表示日,注意的是,这里是区分大小写的,很多情况下,大小写的表示的意义是不同的。像y,M,d这些字母,被称之为字母模板( pattern letters )。
详细列表:
字母
|
日期或时间元素
|
类别
|
示例
|
G
|
Era 标志符
|
Text
|
AD
|
Y
|
weekYear
|
Year
|
2009,09
|
y
|
年
|
Year
|
1996; 96
|
M
|
年中的月份
|
Month
|
July; Jul; 07
|
w
|
一年的第几周
|
Number
|
27
|
W
|
一个月的第几周(weekYear算法)
|
Number
|
2
|
D
|
一年中第几天
|
Number
|
189
|
d
|
月份中的几日
|
Number
|
10
|
F
| 一个月中第几个七天 |
Number
|
2
|
E
| 星期几 |
Text
|
Tuesday; Tue
|
a
|
Am/pm 标记
|
Text
|
PM
|
H
|
一天中的小时数(0-23)
|
Number
|
0
|
k
|
一天中的小时数(1-24)
|
Number
|
24
|
K
|
am/pm 中的小时数(0-11)
|
Number
|
0
|
h
|
am/pm 中的小时数(1-12)
|
Number
|
12
|
m
|
分钟
|
Number
|
30
|
s
|
秒
|
Number
|
55
|
S
|
毫秒
|
Number
|
978
|
z
|
时区
|
General time zone
|
Pacific Standard Time; PST; GMT-08:00
|
Z
|
时区
|
RFC 822 time zone
|
-0800
|
字母模板被划分了多种类型:
Text:对于格式化来说,如果字母模板的数量大于等于4,则使用完全形式;否则,在可用的情况下使用短形式或缩写形式。对于解析来说,两种形式都是可接受的,与模式字母的数量无关。比如星期几,在美国的语言环境下,在一个E下简写成Sun,四个,全写为Sunday,在中文模式下嘛,都是星期日~
Number:对于格式化来说,模式字母的数量表示至少有几位,如果数位不够,则用 0 填充以达到此数量。对于解析来说,模式字母的数量被忽略,除非必须分开两个相邻字段。
Year:yy就是两个数字来格式化,其他则全写。解析的时候同理,yy特殊解析,在解析缩写年份模式("y" 或 "yy")时,SimpleDateFormat 必须相对于某个世纪来解释缩写的年份。这通过将日期调整为 SimpleDateFormat 实例创建之前的 80 年和之后 20 年范围内来完成。例如,在 "MM/dd/yy" 模式下,如果 SimpleDateFormat 实例是在 1997 年 1 月 1 日创建的,则字符串 "01/11/12" 将被解释为 2012 年 1 月 11 日,而字符串 "05/04/64" 将被解释为 1964 年 5 月 4 日。在解析时,只有恰好由两位数字组成的字符串(如 Character.isDigit(char) 所定义的)被解析为默认的世纪。其他任何数字字符串将照字面意义进行解释,例如单数字字符串,3 个或更多数字组成的字符串,或者不都是数字的两位数字字符串(例如"-1")。因此,在相同的模式下, "01/02/3" 或 "01/02/003" 解释为公元 3 年 1 月 2 日。同样,"01/02/-3" 解析为公元前 4 年 1 月 2 日。这里说的是高里历,其他历法,我也不知道~
Month: 如果模式字母的数量为 3 或大于 3,则将月份解释为 text;否则解释为 number。
置于时区,我懒得想了~╮(╯▽╰)╭
举个栗子:
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",Locale.SIMPLIFIED_CHINESE);
Calendar cld = Calendar.getInstance();
cld.set(2017, Calendar.MARCH, 14, 12, 0, 0);
cld.clear(Calendar.MILLISECOND);
Date d = cld.getTime();
String[] patterns = {"yyyy-MM-dd HH:mm:ss","G-E-a","GGGG-EEEE-aaaa",
"yyyy-MM-dd HH:mm:ss:SSS,是yy年第D天,M月的第W周第FF个七天,EEEE, 时间是aaaaKK时mm分ss秒"
};
for(String p:patterns){
fmt.applyPattern(p);
System.out.println(p + ": " + fmt.format(d));
}
输出:
yyyy-MM-dd HH:mm:ss: 2017-03-14 12:00:00
G-E-a: 公元-星期二-下午
GGGG-EEEE-aaaa: 公元-星期二-下午
yyyy-MM-dd HH:mm:ss:SSS,是yy年中的第w周的第D天,M月的第W周第FF个七天,EEEE, 时间是aaaaKK时mm分ss秒: 2017-03-14 12:00:00:000,是17年中的第73天,3月的第3周第02个七天,星期二, 时间是下午00时00分00秒
这里值得注意的是,字母模板是大小写敏感的,大小写敏感的,大小写敏感的,敏感的!!!!!
说实话,好烦,记呢,谁记得住哇~~,用时候再翻吧~,这里举几个例子强调一下重要性!
SimpleDateFormat fmt = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss",Locale.SIMPLIFIED_CHINESE);
Calendar cld = Calendar.getInstance();
cld.set(2015, Calendar.DECEMBER, 31, 0, 0, 0);
System.out.println(fmt.format(cld.getTime()));
cld.set(2016, Calendar.DECEMBER, 31, 0, 0, 0);
System.out.println(fmt.format(cld.getTime()));
注意啦!!!这里格式化是用的大写的YYYY,对照上表中,可以知道,这个是weekYear,上一节中,已经讲过概念,就是每一年的第一周的第一天,算是这一年的开始。也可以说,年末的最后一周,如果不满7天,归到下一年。查一下日历,算一下结果:
2016-12-31 00:00:00
2016-12-31 00:00:00
惊不惊喜!!!!2015年12月31日和2016年12月31日居然格式化成了同一天!!!!因为这两天在weekYear中,都是2016年的,所以,就格式化成了这个鬼样子!!!
蛋是
千万不要以为这是个bug,这是我们用错啦!!!你使用weekYear,那么就没有月日的概念,你使用月日,就是常规年,不要用YYYY!!!!
千万不要以为这是个bug,这是我们用错啦!!!你使用weekYear,那么就没有月日的概念,你使用月日,就是常规年,不要用YYYY!!!!
我们应该这么用:
SimpleDateFormat fmtCorrect = new SimpleDateFormat("YYYY第w周E",Locale.SIMPLIFIED_CHINESE);
cld.set(2015, Calendar.DECEMBER, 31, 0, 0, 0);
System.out.println(fmtCorrect.format(cld.getTime()));
cld.set(2016, Calendar.DECEMBER, 31, 0, 0, 0);
System.out.println(fmtCorrect.format(cld.getTime()));
这回在看结果:
2016年第1周星期四
2016年第53周星期六
使用weekYear,那么概念就是,某某年第XX周星期X,这样来表示时间的!!
既然都有weekYear的概念了,那么一周的开始时间是周几,必然需要设置,怎么设置呢。好,蛋疼的地方来了,SimpleDateFormat对象内置了一个calendar对象,通过设置calendar对象的firstDayOfWeek,来设置一周的第一天是星期几~╮(╯▽╰)╭,用起来是不是有点,蛋蛋的忧伤。
SimpleDateFormat fmt = new SimpleDateFormat("YYYY第w周E");
Calendar defaultCld = fmt.getCalendar();
defaultCld.setFirstDayOfWeek(Calendar.MONDAY);
Calendar calendar = Calendar.getInstance();
calendar.set(2016, Calendar.DECEMBER, 31);
Date d = calendar.getTime();
System.out.println(fmt.format(d));
defaultCld.setFirstDayOfWeek(Calendar.SUNDAY);
System.out.println(fmt.format(d));
输出:
2017第1周星期六
2016第53周星期六
另外
如果格式化中有英文字符,请用单引号包围起来。
解析
解析和格式化的字母模板的定义差不多,细微的差别,以上已经讲过了,只说两个问题:
1.lenient
上一节我们讲过lenient,这里也是一样,默认的是TRUE,是宽松的,1月32日会被解析为2月1日。
SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
System.out.println(fmt.format(fmt.parse("2017-01-32")));
fmt.setLenient(false);
System.out.println(fmt.format(fmt.parse("2017-01-32")));
输出:
2017-02-01
Exception in thread bla bla bla
2.时区
相同的字符串,在不同的时区表示的意思显然是不同的!!!如果我们真的需要考虑时区问题,那么你解析的时候,必须指定时区,在SimpleDateFormat对象中。栗子之前Calendar夏令时的时候,已经有了~~