最后
终极手撕架构师的学习笔记:分布式+微服务+开源框架+性能优化
断点看DateTime的对象数据,可以直接看到返回了:
- 当前时间
- 获得一周的第一天,默认为周一
- 包含时区
// 当前时间
DateTime date = DateUtil.date();
System.out.println(date.toString());
----------------------------
public String toString() {
return toString(this.timeZone);
}
-----------------------------------
/\*\*
\* 转为"yyyy-MM-dd HH:mm:ss" 格式字符串<br>
\* 如果时区不为{@code null},会转换为其时区对应的时间,否则转换为当前时间对应的时区
\*
\* @param timeZone 时区
\* @return "yyyy-MM-dd HH:mm:ss" 格式字符串
\* @since 4.1.14
\*/
public String toString(TimeZone timeZone) {
if (null != timeZone) {
return toString(DateUtil.newSimpleFormat(DatePattern.NORM_DATETIME_PATTERN, null, timeZone));
}
return toString(DatePattern.NORM_DATETIME_FORMAT);
}
DateTime重写了toString() 方法,格式化了时间,返回"yyyy-MM-dd HH:mm:ss" 格式字符串
从**DateTime(long timeMillis, TimeZone timeZone)中源码中,可看出,在Date(long date)**基础上,多加了timeZone的赋值。
传统写法一
获取当前时间
//获取当前时间
Date date = new Date();
System.out.println(date.toString());
//格式化时间
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间为: " + ft.format(date));
对比
hutool的写法对比传统写法会更加简洁一些,而且定义了DateTime对象,这个是hutool的DateUtil(时间工具类)的基础。
源码分析二
/\*\*
\* 当前时间
\*/
public DateTime() {
this(TimeZone.getDefault());
}
**TimeZone.getDefault()**是JDK自带的方法,所属包:java.util;方法返回此主机(程序运行的主机)的默认时区
使用默认时区是否有风险?
我们来看下默认时区是怎么取值的
1、java.util.TimeZone类中getDefault方法的源代码显示,它最终是会调用sun.util.calendar.ZoneInfo类的getTimeZone 方法。这个方法为需要的时间区域返回一个作为ID的String参数。
2、时间ID zoneID的获取方式:先从JVM中的user.timezone变量中读取,如果读不到,再读取系统的默认时区
zoneID = getSystemTimeZoneID(javaHome);
/\*\*
\* Gets the platform defined TimeZone ID.
\*\*/
private static native String getSystemTimeZoneID(String javaHome);
看到这个native ,说明已经挖到核心了,到了这一步,还是不清楚是怎么获取系统的默认时区的,那怎么办,JDK代码只能跟到这里。
转战OpenJDK,源码下载方式:https://gitee.com/mirrors/openjdk
3、如果再读不到,就用默认的 GMT_ID = “GMT”
避免风险最佳实践
JVM中的user.timezone变量中设置时区
什么是native
native是一个计算机函数,一个Native Method就是一个Java调用非Java代码的接口。方法的实现由非Java语言实现,比如C或C++。
native的源码怎么看呢
以**private static native String getSystemTimeZoneID(String javaHome)**为例
getSystemTimeZoneID方法所在的package java.util.TimeZone;
如图所示,找到TimeZone.c下的getSystemTimeZoneID方法
/\*
\* Gets the platform defined TimeZone ID
\*/
JNIEXPORT jstring JNICALL
Java\_java\_util\_TimeZone\_getSystemTimeZoneID(JNIEnv \*env, jclass ign,
jstring java_home, jstring country)
{
const char \*cname;
const char \*java_home_dir;
char \*javaTZ;
if (java_home == NULL)
return NULL;
java_home_dir = JNU\_GetStringPlatformChars(env, java_home, 0);
if (java_home_dir == NULL)
return NULL;
if (country != NULL) {
cname = JNU\_GetStringPlatformChars(env, country, 0);
/\* ignore error cases for cname \*/
} else {
cname = NULL;
}
/\*
\* Invoke platform dependent mapping function
\*/
javaTZ = findJavaTZ\_md(java_home_dir, cname);
free((void \*)java_home_dir);
if (cname != NULL) {
free((void \*)cname);
}
if (javaTZ != NULL) {
jstring jstrJavaTZ = JNU\_NewStringPlatform(env, javaTZ);
free((void \*)javaTZ);
return jstrJavaTZ;
}
return NULL;
}
重点:调用不同平台相关的映射函数
/*
* Invoke platform dependent mapping function
*/
javaTZ = findJavaTZ_md(java_home_dir, cname);
去查找findJavaTZ_md方法时,发现存在分别在solaris和windows两个目录下。
查了下这两个目录的差别:
因为OpenJDK里,Java标准库和部分工具的源码repo(jdk目录)里,BSD和Linux的平台相关源码都是在solaris目录里的。
原本Sun JDK的源码里平台相关的目录就是从solaris和windows这两个目录开始的,后来Unix系的平台相关代码全都放在solaris目录下了,共用大部分代码。
作者:RednaxelaFX
链接:https://www.zhihu.com/question/58982441/answer/170264788
来源:知乎
简单的理解就是:
window系统下,使用windows目录下编译的JDK代码
unix系的平台下,使用solaris目录下编译的JDK代码
了解不同系统下findJavaTZ_md方法执行
windows系统
/\*
\* Detects the platform time zone which maps to a Java time zone ID.
\*/
char \*findJavaTZ\_md(const char \*java_home_dir, const char \*country)
{
char winZoneName[MAX_ZONE_CHAR];
char winMapID[MAX_MAPID_LENGTH];
char \*std_timezone = NULL;
int result;
winMapID[0] = 0;
result = getWinTimeZone(winZoneName, winMapID);
if (result != VALUE_UNKNOWN) {
if (result == VALUE_GMTOFFSET) {
std_timezone = \_strdup(winZoneName);
} else {
std_timezone = matchJavaTZ(java_home_dir, result,
winZoneName, winMapID, country);
}
}
return std_timezone;
}
注释写得很清楚,获取“Time Zones”注册表中的当前时区
/\*
\* Gets the current time zone entry in the "Time Zones" registry.
\*/
static int getWinTimeZone(char \*winZoneName, char \*winMapID)
{
...
}
时区的设置方式:
那时区上的选择值是从哪取到的,上面有说了,是在注册表中取值
打开注册表 :Regedit–>
计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
unix系的平台
findJavaTz_md()方法的注释上写得很清楚了:将平台时区ID映射为Java时区ID
/\*
\* findJavaTZ\_md() maps platform time zone ID to Java time zone ID
\* using <java\_home>/lib/tzmappings. If the TZ value is not found, it
\* trys some libc implementation dependent mappings. If it still
\* can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm
\* form. `country', which can be null, is not used for UNIX platforms.
\*/
/\*ARGSUSED1\*/
char \*
findJavaTZ\_md(const char \*java_home_dir, const char \*country)
{
char \*tz;
char \*javatz = NULL;
char \*freetz = NULL;
tz = getenv("TZ");
#ifdef \_\_linux\_\_
if (tz == NULL) {
#else
#ifdef \_\_solaris\_\_
if (tz == NULL || \*tz == '\0') {
#endif
#endif
tz = getPlatformTimeZoneID();
freetz = tz;
}
/\*
\* Remove any preceding ':'
\*/
if (tz != NULL && \*tz == ':') {
tz++;
}
#ifdef \_\_solaris\_\_
if (strcmp(tz, "localtime") == 0) {
tz = getSolarisDefaultZoneID();
freetz = tz;
}
#endif
if (tz != NULL) {
#ifdef \_\_linux\_\_
/\*
\* Ignore "posix/" prefix.
\*/
if (strncmp(tz, "posix/", 6) == 0) {
tz += 6;
}
#endif
javatz = strdup(tz);
if (freetz != NULL) {
free((void \*) freetz);
}
}
return javatz;
}
步骤:
1、使用< Java home>/lib/tzmappings,。如果没有找到"TZ"变量,就进行第2步
2、 tz = getPlatformTimeZoneID(); 执行Linux特定的映射,如果找到,返回一个时区ID,否则返回null
【Linux】Centos7修改系统时区timezone方式:
timedatectl
修改时区
timedatectl set-timezone Asia/Shanghai
3、对比/etc/localtime与"/usr/share/zoneinfo目录下的文件,如果一致,就返回时区ID,没有则到第4步
4、返回到GMT
方法名称:DateUtil.dateSecond()
方法描述
当前时间,转换为{@link DateTime}对象,忽略毫秒部分
源码分析一
/\*\*
\* 当前时间,转换为{@link DateTime}对象,忽略毫秒部分
\*
\* @return 当前时间
\* @since 4.6.2
\*/
public static DateTime dateSecond() {
return beginOfSecond(date());
}
------------------------------------
public static DateTime beginOfSecond(Date date) {
return new DateTime(beginOfSecond(calendar(date)));
}
--------------------------------------
public static Calendar beginOfSecond(Calendar calendar) {
return truncate(calendar, DateField.SECOND);
}
到这里时,实际上要经过三步方法的调用
- calendar(date) 转换为Calendar对象
- beginOfSecond(Calendar calendar) 获取秒级别的开始时间,即忽略毫秒部分
- DateTime(Calendar calendar) Calendar对象转为DateTime对象
//CalendarUtil 类
/\*\*
\* 获取秒级别的开始时间,即忽略毫秒部分
\*
\* @param calendar 日期 {@link Calendar}
\* @return {@link Calendar}
\* @since 4.6.2
\*/
public static Calendar beginOfSecond(Calendar calendar) {
return truncate(calendar, DateField.SECOND);
}
---------------------------------
/\*\*
\* 修改日期为某个时间字段起始时间
\*
\* @param calendar {@link Calendar}
\* @param dateField 时间字段
\* @return 原{@link Calendar}
\*/
public static Calendar truncate(Calendar calendar, DateField dateField) {
return DateModifier.modify(calendar, dateField.getValue(), DateModifier.ModifyType.TRUNCATE);
}
---------------------------------
//DateModifier类
/\*\*
\* 修改日期
\*
\* @param calendar {@link Calendar}
\* @param dateField 日期字段,即保留到哪个日期字段
\* @param modifyType 修改类型,包括舍去、四舍五入、进一等
\* @return 修改后的{@link Calendar}
\*/
public static Calendar modify(Calendar calendar, int dateField, ModifyType modifyType) {
// AM\_PM上下午特殊处理
if (Calendar.AM_PM == dateField) {
boolean isAM = DateUtil.isAM(calendar);
switch (modifyType) {
case TRUNCATE:
calendar.set(Calendar.HOUR_OF_DAY, isAM ? 0 : 12);
break;
case CEILING:
calendar.set(Calendar.HOUR_OF_DAY, isAM ? 11 : 23);
break;
case ROUND:
int min = isAM ? 0 : 12;
int max = isAM ? 11 : 23;
int href = (max - min) / 2 + 1;
int value = calendar.get(Calendar.HOUR_OF_DAY);
calendar.set(Calendar.HOUR_OF_DAY, (value < href) ? min : max);
break;
}
// 处理下一级别字段
return modify(calendar, dateField + 1, modifyType);
}
// 循环处理各级字段,精确到毫秒字段
for (int i = dateField + 1; i <= Calendar.MILLISECOND; i++) {
if (ArrayUtil.contains(IGNORE_FIELDS, i)) {
// 忽略无关字段(WEEK\_OF\_MONTH)始终不做修改
continue;
}
// 在计算本周的起始和结束日时,月相关的字段忽略。
if (Calendar.WEEK_OF_MONTH == dateField || Calendar.WEEK_OF_YEAR == dateField) {
if (Calendar.DAY_OF_MONTH == i) {
continue;
}
} else {
// 其它情况忽略周相关字段计算
if (Calendar.DAY_OF_WEEK == i) {
continue;
}
}
modifyField(calendar, i, modifyType);
}
return calendar;
}
循环处理各级字段:
1、// 忽略无关字段(WEEK_OF_MONTH)始终不做修改
/\*\* 忽略的计算的字段 \*/
private static final int[] IGNORE_FIELDS = new int[] { //
Calendar.HOUR_OF_DAY, // 与HOUR同名
Calendar.AM_PM, // 此字段单独处理,不参与计算起始和结束
Calendar.DAY_OF_WEEK_IN_MONTH, // 不参与计算
Calendar.DAY_OF_YEAR, // DAY\_OF\_MONTH体现
Calendar.WEEK_OF_MONTH, // 特殊处理
Calendar.WEEK_OF_YEAR // WEEK\_OF\_MONTH体现
};
2、// 在计算本周的起始和结束日时,月相关的字段忽略。
3、modifyField(calendar, i, modifyType); i=14(含义Calendar.MILLISECOND)
4、calendar.set(field, DateUtil.getBeginValue(calendar, field));
/**
* 获取指定日期字段的最小值,例如分钟的最小值是0
*
* @param calendar {@link Calendar}
* @param dateField {@link DateField}
* @return 字段最小值
* @see Calendar#getActualMinimum(int)
* @since 4.5.7
*/
public static int getBeginValue(Calendar calendar, int dateField) {
if (Calendar.DAY_OF_WEEK == dateField) {
return calendar.getFirstDayOfWeek();
}
return calendar.getActualMinimum(dateField);
}
所以就得到我们想要的,忽略毫秒
calendar.set(field, DateUtil.getBeginValue(calendar, field));
-->calendar.set(field,0);//field=14(含义Calendar.MILLISECOND)
方法名称:DateUtil.now()
方法描述
当前时间,格式 yyyy-MM-dd HH:mm:ss
源码分析一
/\*\*
\* 当前时间,格式 yyyy-MM-dd HH:mm:ss
\*
\* @return 当前时间的标准形式字符串
\*/
public static String now() {
return formatDateTime(new DateTime());
}
------------------------------
/\*\*
\* 格式化日期时间<br>
\* 格式 yyyy-MM-dd HH:mm:ss
\*
\* @param date 被格式化的日期
\* @return 格式化后的日期
\*/
public static String formatDateTime(Date date) {
if (null == date) {
return null;
}
return DatePattern.NORM_DATETIME_FORMAT.format(date);
}
Docker步步实践
目录文档:
①Docker简介
②基本概念
③安装Docker
④使用镜像:
⑤操作容器:
⑥访问仓库:
⑦数据管理:
⑧使用网络:
⑨高级网络配置:
⑩安全:
⑪底层实现:
⑫其他项目:
[外链图片转存中…(img-AJK95NNB-1715766135193)]
④使用镜像:
[外链图片转存中…(img-fnKLFFuL-1715766135193)]
⑤操作容器:
[外链图片转存中…(img-d6NWzPhf-1715766135194)]
⑥访问仓库:
[外链图片转存中…(img-DRupRzyP-1715766135195)]
⑦数据管理:
[外链图片转存中…(img-rlYK4zBo-1715766135195)]
⑧使用网络:
[外链图片转存中…(img-FCqWKuTl-1715766135196)]
⑨高级网络配置:
[外链图片转存中…(img-1nymKPEh-1715766135196)]
⑩安全:
[外链图片转存中…(img-8YJ0AuXj-1715766135197)]
⑪底层实现:
[外链图片转存中…(img-TbWeUxyK-1715766135197)]
⑫其他项目:
[外链图片转存中…(img-8TGGx3H9-1715766135198)]