问题背景
我们在使用jenkins的过程中有没有发现 历史任务 那里的构建时间的格式 是不是怪怪的?
例如设置的晚上凌晨0点的 定时任务, 显示的时间会是 2020-10-22 上午12:00
看着不习惯,感觉很怪。感觉显示的是12小时制的格式,但是又不太符合我们中国的风格?(这都是国外设计的,也怪不得我们.)
这里有个 issue 提问 , jenkins 简体中文插件中的, 参考这个 https://github.com/jenkinsci/localization-zh-cn-plugin/issues/79
由这个引发了以下的一些思考,特记录下来分享给大家.
问题1
这个日期时间格式在哪里控制的呢?
在这个文件中控制的 core/src/main/resources/hudson/widgets/HistoryWidget/entry.jelly
在48行左右: <i:formatDate value="${build.timestamp.time}" type="both" dateStyle="medium" timeStyle="medium" /> ${h.getUserTimeZonePostfix()}
jenkins 通过 这个 tag "<i:formatDate>" 来格式化日期的,
标签中有 datestyle 和 timestyle 来分别控制 日期 时间 显示的格式.
而且这个显示的格式还和本地浏览器语言设置有点关系的.
下面分别对 不同的 dateStyle 和 timeStyle 设置不同的值 做了对比:
dateStyle="medium" timeStyle="medium" will show 2020-8-19 17:27:01
dateStyle="medium" timeStyle="short" will show 2020-8-19 下午5:27
dateStyle="medium" timeStyle="long" will show 2020-8-19 下午05时27分01秒
dateStyle="long" timeStyle="long" will show 2020年8月19日 下午05时27分01秒
dateStyle="short" timeStyle="long" will show 20-8-19 下午05时27分01秒
火狐中文语言
火狐英式英语
火狐美式英语
通过设置浏览器不同的语言环境 得到的日期时间格式是不一样的。
之所以这么做,而不是统一的设置日期时间 格式为一个固定的 也是考虑到了本地化的 需求. 常见的本地化就是语言字符串翻译的.
问题2
这个 formatDate 来自哪里呢
<i:formatDate> 这个标签是 在jelly文件中的, jenkins的页面设计用的比较多的 都是 jelly 文件,
这个很类似JSP页面, 里面有各种标签可以使用, 循环的, 判断的,变量赋值的等等.
也类似我们常常听说的 EL 表达式 等等.
这个 标签 对应的java 实现又在哪里呢? 哪里可以找到它的源码呢?
这个 <i:formatDate> 是 commons-jelly 提供的. 参考 https://github.com/apache/commons-jelly
源码见 https://github.com/apache/commons-jelly/blob/master/jelly-tags/fmt/src/main/java/org/apache/commons/jelly/tags/fmt/FormatDateTag.java
问题3
这个设置的timeStyle为short为什么显示那样的格式呢?
下面我们来解决这个问题, 我们从代码来分析.
为什么
dateStyle="medium" timeStyle="medium" will show 2020-8-19 17:27:01
dateStyle="medium" timeStyle="short" will show 2020-8-19 下午5:27
medium 和 short 显示的是那样的格式, 而不是这样的格式.....
short 字面意思是短,感觉这里应该是想显示个短格式的时间的,那你把秒去掉不就得了,干嘛显示个中文 何来 短 呢? 感觉更长了呢...
总之我们最初的 问题是 哪个short 格式的为啥 显示的那么怪呢?下面我们看代码.
直接看 https://github.com/apache/commons-jelly/blob/master/jelly-tags/fmt/src/main/java/org/apache/commons/jelly/tags/fmt/FormatDateTag.java 中的代码.
主入口 应该在 public void doTag(XMLOutput output) throws JellyTagException { 这个方法上.
分析发现 在哪里做格式化日期时间呢? 在 152 行 DateFormat formatter = createFormatter(locale);
下面我们看 createFormatter 方法, 其中的locale 参数取值就是和浏览器语言对应的. 中文简体 zh_CN 这个. 中文台湾 zh_TW, 中文香港 zh_HK
private DateFormat createFormatter(Locale loc) throws JellyTagException {
DateFormat formatter = null;
if ((etype == null) || DATE.equalsIgnoreCase(etype)) {
formatter = DateFormat.getDateInstance(
getStyle(edateStyle, "FORMAT_DATE_INVALID_DATE_STYLE"),
loc);
} else if (TIME.equalsIgnoreCase(etype)) {
formatter = DateFormat.getTimeInstance(
getStyle(etimeStyle, "FORMAT_DATE_INVALID_TIME_STYLE"),
loc);
} else if (DATETIME.equalsIgnoreCase(etype)) {
formatter = DateFormat.getDateTimeInstance(
getStyle(edateStyle, "FORMAT_DATE_INVALID_DATE_STYLE"),
getStyle(etimeStyle, "FORMAT_DATE_INVALID_TIME_STYLE"),
loc);
} else {
throw new JellyTagException("Date format invalue");
}
return formatter;
}
这里if else 什么的 分了 3 类, 只 格式 date的, 只格式 time的, 格式date和time的. 我们这里看 date和time的.
formatter = DateFormat.getDateTimeInstance(
getStyle(edateStyle, "FORMAT_DATE_INVALID_DATE_STYLE"),
getStyle(etimeStyle, "FORMAT_DATE_INVALID_TIME_STYLE"),
loc);
这里 getStyle() 把 标签 <i:formatDate>中设置的 dateStyle 和 timeStyle 做个转换, 由字符串类型 转换成 对应的 DateFormat.SHORT, 其实是个整数.
其实 dateStyle="medium" timeStyle="short" 就会转换成 DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT, locale)
其实 代码 走到这里 这个 格式 已经 和 这个 commons-jelly 没关系了, 对应的 转到 JDK 中的 代码了.
下面我们先做几组实验, 测试测试 medium, short 等等格式在不同语言下面的效果.
medium + short 格式的 | 格式化之后 | medium + medium 格式的 | 格式化之后 | |
---|---|---|---|---|
Thu Oct 22 10:56:52 CST 2020 | Thu Oct 22 10:55:15 CST 2020 | |||
medium_short_zh | 2020-10-22 上午10:56 | medium_medium_zh | 2020-10-22 10:55:15 | |
medium_short_zh_CN | 2020-10-22 上午10:56 | medium_medium_zh_CN | 2020-10-22 10:55:15 | |
medium_short_zh_TW | 2020/10/22 上午 10:56 | medium_medium_zh_TW | 2020/10/22 上午 10:55:15 | |
medium_short_zh_HK | 2020年10月22日 上午10:56 |