参考资料:
1.https://blog.csdn.net/u011212112/article/details/90478610
2.https://blog.csdn.net/qq_36761831/article/details/79691119
3.https://blog.csdn.net/l871243720/article/details/121801987
1.Date中保存的是什么?
在java中,只要我们执行
Date date = new Date();
就可以得到当前时间。如:
import java.util.Date;
public class Demo {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date); // 输出当前时间
}
}
输出结果是:
Fri Sep 30 00:10:45 CST 2022
也就是我执行上述代码的时刻:2022年9月30日00点10分45秒 周五。是不是Date对象里存了年月日时分秒呢?不是的,Date对象里存的只是一个long型的变量,其值为自1970年1月1日0点至Date对象所记录时刻经过的毫秒数,调用Date对象getTime()方法就可以返回这个毫秒数,如下代码:
import java.util.Date;
public class Demo {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.getTime()); // 输出当前毫秒数
}
}
输出如下:
1664470419695
即上述程序执行的时刻距离1970年1月1日0点有1664470419695毫秒。
2.时区
(代码种有关SimpleDateFormat的详细说明参考后续5)
全球分为24个时区,相邻时区时间相差1个小时。比如北京处于东八时区,东京处于东九时区,北京时间比东京时间晚1个小时。比如此刻北京时间是2022年9月30日00点10分45秒,则东京时间是2022年9月30日01点10分45秒。
既然Date里存放的是当前时刻距1970年1月1日0点时刻的毫秒数,如果此刻在北京、东京有2个程序员同时执行如下语句:
Date date = new Date();
那这2个date对象里存的毫秒数是相同的吗?还是北京的比东京的小3600000(北京时间比东京时间晚1小时,1小时为3600秒即3600000毫秒)?答案是,这2个Date里的毫秒数是完全一样的。确切的说,Date对象里存的是自格林威治时间( GMT)1970年1月1日0点至Date对象所表示时刻所经过的毫秒数。所以,如果某一时刻遍布于世界各地的程序员同时执行new Date语句,这些Date对象所存的毫秒数是完全一样的。也就是说,Date里存放的毫秒数是与时区无关的。
继续上述例子,如果上述2个程序员调用那一刻的时间是北京时间2022年9月30日00点10分45秒,他们继续调用
System.out.println(date);
那么北京的程序员将会打印出2022年9月30日00点10分45秒,而东京的程序员会打印出2022年9月30日01点10分45秒。既然Date对象只存了一个毫秒数,为什么这3个毫秒数完全相同的Date对象,可以打印出不同的时间呢?这是因为Sysytem.out.println函数在打印时间时,会取操作系统当前所设置的时区,然后根据这个时区将同毫秒数解释成该时区的时间。当然我们也可以手动设置时区,以将同一个Date对象按不同的时区输出。可以做如下实验验证:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Demo {
public static void main(String[] args) {
Date date = new Date(); // 获取当前程序执行时距离自格林威治时间( GMT)1970年1月1日0点所经过的毫秒数
SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 北京
bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置北京时区
SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 东京
tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo")); // 设置东京时区
System.out.println("毫秒数:" + date.getTime() + ", 北京时间:" + bjSdf.format(date));
System.out.println("毫秒数:" + date.getTime() + ", 东京时间:" + tokyoSdf.format(date));
}
}
输出结果是:
毫秒数:1664471019469, 北京时间:2022-09-30 01:03:39
毫秒数:1664471019469, 东京时间:2022-09-30 02:03:39
可以看出,同一个Date对象,按不同的时区来格式化,将得到不同时区的时间。
如何获取当前程序所在时区(默认时区)呢?
使用TimeZone timeZone = TimeZone.getDefault();就可以获取到程序当前运行所在的时区
示例如下:
import java.util.TimeZone;
public class Demo {
public static void main(String[] args){
TimeZone timeZone = TimeZone.getDefault();
System.out.println(timeZone.getDisplayName());
System.out.println(timeZone.getID());
}
}
运行结果如下:
中国标准时间
Asia/Shanghai
获取默认时区后进行时区设置
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Demo {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date); // 默认输出
SimpleDateFormat format = new SimpleDateFormat("EE M d HH:mm:ss z yyyy");
TimeZone timeZone = TimeZone.getDefault(); // 初始化
System.out.println(timeZone.getID());
format.setTimeZone(TimeZone.getTimeZone(timeZone.getID())); // 设置时区:程序所在时区
System.out.println(format.format(date));
format.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置时区:Asia/Shanghai
System.out.println(format.format(date)); // 输出时间
}
}
运行结果如下:
Sat Oct 01 11:01:31 CST 2022
Asia/Shanghai
星期六 10 1 11:01:31 CST 2022
星期六 10 1 11:01:31 CST 2022
从结果可以看出,System.out.println(date); 的时区与默认时区相同;默认时区与Asia/Shanghai相同,因为当前程序在中国运行。
3.从字符串中读取时间
(代码种有关SimpleDateFormat的详细说明参考后续5)
有时我们会遇到从一个字符串中读取时间的要求,即从字符串中解析时间并得到一个Date对象,比如将"2022-9-30 01:17:10""解析为一个Date对象。现在问题来了,这个时间到底指的是北京时间的2022-9-30 01:17:10",还是东京时间的2022-9-30 01:17:10"?如果指的是北京时间,那么这个时间对应的东京时间2022-9-30 02:17:10";如果指的是东京时间,那么这个时间对应的北京时间就是2022-9-30 00:17:10"。因此,只说年月日时分秒而不说是哪个时区的,是有歧义的,没有歧义的做法是,给出一个时间字符串,同时指明这是哪个时区的时间。
从字符串中解析时间的正确作法是:指定时区来解析。示例如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Demo {
public static void main(String[] args) throws ParseException {
String timeStr = "2022-9-30 01:17:10"; // 字面时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置北京时区
Date d = sdf.parse(timeStr);
System.out.println(sdf.format(d) + ", " + d.getTime());
}
}
输出为:
2022-09-30 01:17:10, 1664471830000
将一个时间字符串按不同时区来解释,得到的Date对象的值是不同的。验证如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Demo {
public static void main(String[] args) throws ParseException {
String timeStr = "2022-9-30 01:17:10"; // 字面时间
SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
Date bjDate = bjSdf.parse(timeStr); // 解析
System.out.println("字面时间: " + timeStr + ",按北京时间来解释:" + bjSdf.format(bjDate) + ", " + bjDate.getTime());
SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 东京
tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo")); // 设置东京时区
Date tokyoDate = tokyoSdf.parse(timeStr); // 解析
System.out.println("字面时间: " + timeStr + ",按东京时间来解释:" + tokyoSdf.format(tokyoDate) + ", " + tokyoDate.getTime());
}
}
输出为:
字面时间: 2022-9-30 01:17:10,按北京时间来解释:2022-09-30 01:17:10, 1664471830000
字面时间: 2022-9-30 01:17:10,按东京时间来解释:2022-09-30 01:17:10, 1664468230000
可以看出,对于"2022-9-30 01:17:10"这个字符串,按北京时间来解释得到Date对象的毫秒数是
1664471830000;而按东京时间来解释得到的毫秒数是1664468230000,前者正好比后者大于3600000毫秒即1个小时,正好是北京时间和东京时间的时差。
4.将字符串表示的时间转换成另一个时区的时间字符串
(代码种有关SimpleDateFormat的详细说明参考后续5)
综合以上分析,如果给定一个时间字符串,并告诉你这是某个时区的时间,要将它转换为另一个时区的时间并输出,正确的做法是:
1.将字符串按原时区转换成Date对象;
2.将Date对象格式化成目标时区的时间。
比如,将北京时间"2022-9-30 01:17:10"输出成东京时间,代码为:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Demo {
public static void main(String[] args) throws ParseException {
String timeStr = "2022-9-30 01:17:10"; // 字面时间
SimpleDateFormat bjSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
bjSdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
Date date = bjSdf.parse(timeStr); // 将字符串时间按北京时间解析成Date对象
SimpleDateFormat tokyoSdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 东京
tokyoSdf.setTimeZone(TimeZone.getTimeZone("Asia/Tokyo")); // 设置东京时区
System.out.println("北京时间: " + timeStr + "对应的东京时间为:" + tokyoSdf.format(date));
}
}
输出为:
北京时间: 2022-9-30 01:17:10对应的东京时间为:2022-09-30 02:17:10
5.SimpleDateFormat 格式化日期
包含在Java的 java.text.SimpleDateFormat;包中
日期和时间格式由 日期和时间模式字符串 指定。在 日期和时间模式字符串 中,未加引号的字母 'A' 到 'Z' 和 'a' 到 'z' 被解释为模式字母,用来表示日期或时间字符串元素。文本可以使用单引号 (') 引起来,以免进行解释。所有其他字符均不解释;只是在格式化时将它们简单复制到输出字符串
白话文的讲:这些A——Z,a——z这些字母(不被单引号包围的)会被特殊处理替换为对应的日期时间,其他的字符串还是原样输出。
日期和时间模式(注意大小写,代表的含义是不同的)
yyyy:年
MM:月
dd:日
hh:1~12小时制(1-12)
HH:24小时制(0-23)
mm:分
ss:秒
S:毫秒
E:星期几
D:一年中的第几天
F:一月中的第几个星期(会把这个月总共过的天数除以7)
w:一年中的第几个星期
W:一月中的第几星期(会根据实际情况来算)
a:上下午标识
k:和HH差不多,表示一天24小时制(1-24)。
K:和hh差不多,表示一天12小时制(0-11)。
z:表示时区
简单验证代码如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo {
public static void main(String[] args) throws ParseException {
Date ss = new Date();
System.out.println("一般日期输出:" + ss);
System.out.println("时间戳:" + ss.getTime());
SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = format0.format(ss.getTime());//这个就是把时间戳经过处理得到期望格式的时间
System.out.println("格式化结果0:" + time);
SimpleDateFormat format1 = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
time = format1.format(ss.getTime());
System.out.println("格式化结果1:" + time);
}
}
运行结果:
一般日期输出:Fri Sep 30 01:32:47 CST 2022
时间戳:1664472767341
格式化结果0:2022-09-30 01:32:47
格式化结果1:2022年09月30日01时32分47秒
上面的日期和时间模式 是按我们常用的年月日时分秒来放的,下面是专业的图,供参考。
定义了以下模式字母(所有其他字符 'A' 到 'Z' 和 'a' 到 'z' 都被保留):
一个月中的第几个星期, F 这个出来的结果,不靠谱,后面的那个 W 靠谱。
测试程序如下:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Demo {
public static void main(String[] args) throws ParseException {
int year = 2022; // 可自行修改年月
int month = 9;
int daySum = 30; // 一个月的总天数
System.out.print(year + "年" + month + "月\nF:一月中的第几个星期(会把这个月总共过的天数除以7)\n");
for (int i = 1; i <= daySum; i++) {
String timeStr = year + "-" + month + "-" + i + " 01:17:10"; // 字面时间
SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
format0.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
Date date = format0.parse(timeStr); // 将字符串时间按北京时间解析成Date对象
SimpleDateFormat F = new SimpleDateFormat("F");
F.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
System.out.print(F.format(date));
}
System.out.print("\nW:一月中的第几星期(会根据实际情况来算)\n");
for (int i = 1; i <= daySum; i++) {
String timeStr = year + "-" + month + "-" + i + " 01:17:10"; // 字面时间
SimpleDateFormat format0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
format0.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
Date date = format0.parse(timeStr); // 将字符串时间按北京时间解析成Date对象
SimpleDateFormat W = new SimpleDateFormat("W");
W.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
System.out.print(W.format(date));
}
}
}
运行结果:
2022年9月
F:一月中的第几个星期(会把这个月总共过的天数除以7)
111111122222223333333444444455
W:一月中的第几星期(会根据实际情况来算)
111222222233333334444444555555
日历截图如下:
6.延时函数使用
在Java中有时候需要使程序暂停一点时间,称为延时。普通延时用Thread.sleep(int)方法,这很简单。把它将当前线程挂起指定的毫秒数。如
try
{
Thread.sleep(1000);//单位:毫秒
} catch (Exception e) {
}
注意:Thread.sleep(int)不能直接用,要做异常处理,try{}catch{}.
sleep()方法并不能够让程序"严格"的沉睡指定的时间。例如当使用5000作为sleep()方法的参数时,线程可能在实际被挂起5000.001毫秒后才会继续运行。当然,对于一般的应用程序来说,sleep()方法对时间控制的精度足够了。
import java.util.Date;
public class Demo {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
try {
Thread.sleep(2000);//单位:毫秒
} catch (Exception e) {
System.out.println("延时异常");
}
date = new Date();
System.out.println(date);
}
}
运行结果:
Sat Oct 01 12:58:04 CST 2022
Sat Oct 01 12:58:06 CST 2022
从结果中可以看出两次输出时间差2秒。