1hutool源码分析:DateUtil(时间工具类)-当前时间和当前时间戳

}


/**

  • 转为"yyyy-MM-dd HH:mm:ss" 格式字符串

  • 如果时区不为{@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" 格式字符串

image-20210706210932679

从**DateTime(long timeMillis, TimeZone timeZone)中源码中,可看出,在Date(long date)**基础上,多加了timeZone的赋值。

image-20210706213821993

传统写法一

获取当前时间

//获取当前时间

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;方法返回此主机(程序运行的主机)的默认时区

使用默认时区是否有风险?

我们来看下默认时区是怎么取值的

image-202107085554049

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方法

image-20210706233905997

image-20210706234052425

/*

  • 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两个目录下。

image-20210706234905448

查了下这两个目录的差别:

因为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)

{

}

时区的设置方式:

image-202107086550950

那时区上的选择值是从哪取到的,上面有说了,是在注册表中取值

打开注册表 :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

image-202107086455780

修改时区

timedatectl set-timezone Asia/Shanghai

image-2021070864438866

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);

}

到这里时,实际上要经过三步方法的调用

  1. calendar(date) 转换为Calendar对象

  2. beginOfSecond(Calendar calendar) 获取秒级别的开始时间,即忽略毫秒部分

  3. 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;

}

image-202107087711643

循环处理各级字段:

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)

写在最后

作为一名即将求职的程序员,面对一个可能跟近些年非常不同的 2019 年,你的就业机会和风口会出现在哪里?在这种新环境下,工作应该选择大厂还是小公司?已有几年工作经验的老兵,又应该如何保持和提升自身竞争力,转被动为主动?

就目前大环境来看,跳槽成功的难度比往年高很多。一个明显的感受:今年的面试,无论一面还是二面,都很考验Java程序员的技术功底。

最近我整理了一份复习用的面试题及面试高频的考点题及技术点梳理成一份“Java经典面试问题(含答案解析).pdf和一份网上搜集的“Java程序员面试笔试真题库.pdf”(实际上比预期多花了不少精力),包含分布式架构、高可扩展、高性能、高并发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多个知识点高级进阶干货!

由于篇幅有限,为了方便大家观看,这里以图片的形式给大家展示部分的目录和答案截图!

Java经典面试问题(含答案解析)

阿里巴巴技术笔试心得

_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)

写在最后

作为一名即将求职的程序员,面对一个可能跟近些年非常不同的 2019 年,你的就业机会和风口会出现在哪里?在这种新环境下,工作应该选择大厂还是小公司?已有几年工作经验的老兵,又应该如何保持和提升自身竞争力,转被动为主动?

就目前大环境来看,跳槽成功的难度比往年高很多。一个明显的感受:今年的面试,无论一面还是二面,都很考验Java程序员的技术功底。

最近我整理了一份复习用的面试题及面试高频的考点题及技术点梳理成一份“Java经典面试问题(含答案解析).pdf和一份网上搜集的“Java程序员面试笔试真题库.pdf”(实际上比预期多花了不少精力),包含分布式架构、高可扩展、高性能、高并发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、Mycat、Netty、Kafka、Mysql、Zookeeper、Tomcat、Docker、Dubbo、Nginx等多个知识点高级进阶干货!

由于篇幅有限,为了方便大家观看,这里以图片的形式给大家展示部分的目录和答案截图!
[外链图片转存中…(img-wXSeZ9X6-1714234257512)]

Java经典面试问题(含答案解析)

[外链图片转存中…(img-R0Oea348-1714234257513)]

阿里巴巴技术笔试心得

[外链图片转存中…(img-rb7wvl6q-1714234257514)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值