索引目录
源码分析目的
项目引用
方法名称:DateUtil.date()
方法描述
源码分析一
传统写法一
对比
源码分析二
使用默认时区是否有风险?
避免风险最佳实践
什么是native
native的源码怎么看呢
了解不同系统下findJavaTZ_md方法执行
windows系统
unix系的平台
方法名称:DateUtil.dateSecond()
方法描述
源码分析一
方法名称:DateUtil.now()
方法描述
源码分析一
传统写法一
对比
方法名称:DateUtil.today()
方法描述
源码分析一
源码分析目的
知其然,知其所以然
项目引用
此博文的依据:hutool-5.6.5版本源码
cn.hutool
hutool-core
5.6.5
1
2
3
4
5
方法名称:DateUtil.date()
方法描述
当前时间,转换为{@link DateTime}对象
源码分析一
源码:新new了一个DateTime对象,此对象是hutool定义的时间对象,DateTime对象继承了Date对象
看到这个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)**为例查了下这两个目录的差别:
因为OpenJDK里,Java标准库和部分工具的源码repo(jdk目录)里,BSD和Linux的平台相关源码都是在solaris目录里的。 原本Sun JDK的源码里平台相关的目录就是从solaris和windows这两个目录开始的,后来Unix系的平台相关代码全都放在solaris目录下了,共用大部分代码。 作者:RednaxelaFX 链接:https://www.zhihu.com/question/58982441/answer/170264788 来源:知乎
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; }
//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; }