JAVA学习(十)| 日期与时间、正则表达式
日期与时间⁍
计算机通过Locale来针对当地用户习惯格式化日期、时间、数字、货币等。
Locale由语言_国家的字母缩写构成,例如,zh_CN表示中文+中国,en_US表示英文+美国。语言使用小写,国家使用大写。
对于日期来说,不同的Locale,例如,中国和美国的表示方式如下:
- zh_CN:2016-11-30
- en_US:11/30/2016
Java有两套日期和时间的API:
- 旧的Date、Calendar和TimeZone;
- 新的LocalDateTime、ZonedDateTime、ZoneId等。
分别位于java.util和java.time包中。
⭐ 从Java 8开始,java.time包提供了新的日期和时间API,主要涉及的类型有:
- 本地日期和时间:
LocalDateTime,LocalDate,LocalTime; - 带时区的日期和时间:
ZonedDateTime; - 时刻:
Instant; - 时区:
ZoneId,ZoneOffset; - 时间间隔:
Duration。
以及一套新的用于取代SimpleDateFormat的格式化类型DateTimeFormatter。
Date和Calendar(旧API)⁍
计算机表示的时间是以整数表示的时间戳存储的,即Epoch Time,Java使用long型来表示以毫秒为单位的时间戳,通过System.currentTimeMillis()获取当前时间戳。
Epoch Time是计算从1970年1月1日零点(格林威治时区/GMT+00:00)到现在所经历的秒数
Date⁍
1 | |
自定义格式输出:
1 | |
假设当前月份是9月:
M:输出9MM:输出09MMM:输出SepMMMM:输出September
Calendar⁍
和Date比,主要多了一个可以做简单的日期和时间运算的功能。
Calendar的基本用法:
1 | |
TimeZone⁍
Calendar和Date相比,它提供了时区转换的功能。时区用TimeZone对象表示
利用Calendar进行时区转换的步骤是:
- 清除所有字段;
- 设定指定时区;
- 设定日期和时间;
- 创建
SimpleDateFormat并设定目标时区; - 格式化获取的
Date对象(注意Date对象无时区信息,时区信息存储在SimpleDateFormat中)。
LocalDateTime⁍
import java.time.*;
获取日期和时间⁍
- 当前日期:
LocalDate d = LocalDate.now();//2022-02-15 - 当前时间:
LocalTime t = LocalTime.now();//08:51:53.807637953 - 当前日期和时间:
LocalDateTime dt = LocalDateTime.now();//2022-02-15T08:51:53.807670493 - 用标准输出会严格按照ISO 8601格式打印
如果要在同一个时刻分别输出当前日期和时间,可以采用以下方法:(如果按上述分开写,每一行程序的执行时间可能会带来影响)
1 | |
创建指定日期和时间⁍
1 | |
ISO 8601规定的日期和时间分隔符是T。标准格式如下:
- 日期:yyyy-MM-dd
- 时间:HH:mm:ss
- 带毫秒的时间:HH:mm:ss.SSS
- 日期和时间:yyyy-MM-dd’T’HH:mm:ss
- 带毫秒的日期和时间:yyyy-MM-dd’T’HH:mm:ss.SSS
DateTimeFormatter⁍
import java.time.format.*;
1 | |
时间加减⁍
加5天减3小时:
1 | |
减一个月
1 | |
时间调整⁍
对日期和时间进行调整则使用withXxx()方法,例如:withHour(15)会把10:11:12改为15:11:12:
- 调整年:withYear()
- 调整月:withMonth()
- 调整日:withDayOfMonth()
- 调整时:withHour()
- 调整分:withMinute()
- 调整秒:withSecond()
其他,例如
1 | |
时间比较⁍
可以使用isBefore()、isAfter()方法
1 | |
Duration和Period⁍
-
Duration表示两个时刻之间的时间间隔 -
Period表示两个日期之间的天数1
2Duration d = Duration.between(start_dt, end_dt);// PT1235H10M30S
Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9)); //P1M21D -
利用
ofXxx()或者parse()方法也可以直接创建Duration:1
2Duration d1 = Duration.ofHours(10); // 10 hours
Duration d2 = Duration.parse("P1DT2H3M"); // 1 day, 2 hours, 3 minutes
ZonedDateTime⁍
获得不同时区的当前时间⁍
1 | |
表示同一时间
给当前时间附加不同时区⁍
1 | |
表示不同时间
时区转换⁍
将一个ZonedDateTime对象,通过withZoneSameInstant()将关联时区转换到另一个时区
涉及到时区时,千万不要自己计算时差,否则难以正确处理夏令时。
1 | |
ZonedDateTime转换为本地时间:
1 | |
DateTimeFormatter⁍
和SimpleDateFormat不同的是,DateTimeFormatter不但是不变对象,它还是线程安全的。
1 | |
Instant⁍
用Instant.now()获取当前时间戳,效果和System.currentTimeMillis()类似,精度更高
可以和ZonedDateTime以及long互相转换。
1 | |
最佳实践——新旧API处理⁍
处理日期和时间时,尽量使用新的java.time包;
在数据库中存储时间戳时,尽量使用long型时间戳,它具有省空间,效率高,不依赖数据库的优点。
| 数据库 | 对应Java类(旧) | 对应Java类(新) |
|---|---|---|
| DATETIME | java.util.Date | LocalDateTime |
| DATE | java.sql.Date | LocalDate |
| TIME | java.sql.Time | LocalTime |
| TIMESTAMP | java.sql.Timestamp | LocalDateTime |
正则表达式⁍
匹配规则⁍
单个字符的匹配规则如下:
| 正则表达式 | 规则 | 可以匹配 |
|---|---|---|
A |
指定字符 | A |
\u548c |
指定Unicode字符 | 和 |
. |
任意字符 | a,b,&,0 |
\d |
数字0~9 | 0~9 |
\w |
大小写字母,数字和下划线 | a~z, A~Z, 0~9,_ |
\s |
空格、Tab键 | 空格,Tab |
\D |
非数字 | a,A,&,_,…… |
\W |
非\w | &,@,中,…… |
\S |
非\s | a,A,&,_,…… |
多个字符的匹配规则如下:
| 正则表达式 | 规则 | 可以匹配 |
|---|---|---|
A* |
任意个数字符 | 空,A,AA,AAA,…… |
A+ |
至少1个字符 | A,AA,AAA,…… |
A? |
0个或1个字符 | 空,A |
A{3} |
指定个数字符 | AAA |
A{2,3} |
指定范围个数字符 | AA,AAA |
A{2,} |
至少n个字符 | AA,AAA,AAAA,…… |
A{0,3} |
最多n个字符 | 空,A,AA,AAA |
复杂匹配规则⁍
| 正则表达式 | 规则 | 可以匹配 |
|---|---|---|
^ |
开头 | 字符串开头 |
$ |
结尾 | 字符串结束 |
[ABC] |
[…]内任意字符 | A,B,C |
[A-F0-9xy] |
指定范围的字符 | A,……,F,0,……,9,x,y |
[^A-F] |
指定范围外的任意字符 | 非A~F |
AB|CD|EF |
AB或CD或EF | AB,CD,EF |
分组匹配⁍
引入java.util.regex包,用Pattern对象匹配,匹配后获得一个Matcher对象,如果匹配成功,就可以直接从Matcher.group(index)返回子串
正则表达式用(...)分组可以通过Matcher对象快速提取子串:
group(0)表示匹配的整个字符串;group(1)表示第1个子串,group(2)表示第2个子串,以此类推
1 | |
非贪婪匹配⁍
正则表达式匹配默认使用贪婪匹配,可以使用?表示对某一规则进行非贪婪匹配。
例:给定一个字符串表示的数字,判断该数字末尾0的个数。例如:
"123000":3个0"10100":2个0"1001":0个0
正则表达式(\d+?)(0*),后面?表示非贪婪匹配,让\d+尽量少匹配,让0*尽量多匹配
搜索和替换⁍
使用正则表达式可以:
-
分割字符串:
String.split() -
搜索子串:
Matcher.find() -
替换字符串:
String.replaceAll()-
反向引用 可以使用
$1、$2来反向引用匹配到的子串1
2
3
4
5
6
7
8
9
10
11String s = "the quick brown fox jumps over the lazy dog.";
String r = s.replaceAll("\\s([a-z]{4})\\s", " <b>$1</b> ");
//the quick brown fox jumps <b>over</b> the <b>lazy</b> dog.
[Matcher (Java SE 11 & JDK 11 ) (runoob.com)](https://www.runoob.com/manual/jdk11api/java.base/java/util/regex/Matcher.html)
> #### 模板引擎
>
> 模板引擎是指,定义一个字符串作为模板:
>
-
Hello, ${name}! You are learning ${lang}!
1
2
3
4
5
6
7
8
9
10
11
12
其中,以`${key}`表示的是变量,也就是将要被替换的内容
当传入一个`Map<String, String>`给模板后,需要把对应的key替换为Map的value。
例如,传入`Map`为:
```json
{
"name": "Bob",
"lang": "Java"
}然后,
${name}被替换为Map对应的值"Bob”,${lang}被替换为Map对应的值"Java",最终输出的结果为:
1Hello, Bob! You are learning Java!用正则表达式实现:
Matcher.appendReplacement (Java SE 11 & JDK 11 ) (runoob.com)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32package com.itranswarp.learnjava;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Learn Java from https://www.liaoxuefeng.com/
*
* @author liaoxuefeng
*/
public class Template {
final String template;
final Pattern pattern = Pattern.compile("\\$\\{(\\w+)\\}");
public Template(String template) {
this.template = template;
}
public String render(Map<String, Object> data) {
Matcher m = pattern.matcher(template);
// TODO:
StringBuilder sb = new StringBuilder();
while (m.find()) {
m.appendReplacement(sb,data.get(m.group(1)).toString());
}
m.appendTail(sb);
return sb.toString();
}
}





