Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ public Date read(JsonReader in) throws IOException {
in.nextNull();
return null;
case NUMBER:
return new Date(in.nextInt() * 1000);
// 微信返回的是秒级时间戳,需转为毫秒;此处必须用 long 读取并运算,
// 否则 in.nextInt() * 1000 会发生 int 溢出,导致解析出错误的时间
return new Date(in.nextLong() * 1000L);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 在换算前校验秒级时间戳范围

当输入是 nextLong() 可接受、但绝对值超过毫秒换算范围的秒值时(例如 9223372036854776),这里的乘法会发生 long 溢出并静默生成完全错误的 Date;旧实现会因超出 int 范围而拒绝该输入。请在乘法前校验边界或使用 Math.multiplyExact 并将溢出转换为解析异常,避免异常响应被当作有效日期。

AGENTS.md reference: AGENTS.md:L47-L48

Useful? React with 👍 / 👎.

default:
throw new JsonParseException("Expected NUMBER but was " + peek);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package me.chanjar.weixin.common.util.json;

import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import org.testng.annotations.Test;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Date;

import static org.assertj.core.api.Assertions.assertThat;

/**
* WxDateTypeAdapter 的单元测试.
*
* @author liyong
*/
public class WxDateTypeAdapterTest {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 将新增测试加入 TestNG suite

weixin-java-common/pom.xml 将 Surefire 固定配置为运行 src/test/resources/testng.xml,而该 suite 只显式列出了原有的六个测试类,没有包含 WxDateTypeAdapterTest;因此 CI 的 mvn clean test -Dmaven.test.skip=false 不会执行这里新增的五个测试,日期溢出回归也不会被门禁发现。请把该类加入 suite,或调整配置以启用测试类自动发现。

AGENTS.md reference: AGENTS.md:L63-L65

Useful? React with 👍 / 👎.


private final WxDateTypeAdapter adapter = new WxDateTypeAdapter();

private Date read(String json) throws IOException {
JsonReader reader = new JsonReader(new StringReader(json));
reader.setLenient(true);
return adapter.read(reader);
}

private String write(Date date) throws IOException {
StringWriter out = new StringWriter();
JsonWriter writer = new JsonWriter(out);
writer.setLenient(true);
adapter.write(writer, date);
return out.toString();
}

/**
* 秒级时间戳转成毫秒时如果按 int 运算会溢出,导致解析出的时间错误(甚至早于 1970 年)。
*/
@Test
public void testReadCurrentTimestamp() throws IOException {
long seconds = 1481013459L;
Date date = read(String.valueOf(seconds));
assertThat(date).isNotNull();
assertThat(date.getTime()).isEqualTo(seconds * 1000L);
}

@Test
public void testReadTimestampAfterYear2038() throws IOException {
long seconds = 4102444800L;
Date date = read(String.valueOf(seconds));
assertThat(date).isNotNull();
assertThat(date.getTime()).isEqualTo(seconds * 1000L);
}

@Test
public void testReadNull() throws IOException {
assertThat(read("null")).isNull();
}

@Test
public void testWrite() throws IOException {
long seconds = 1481013459L;
assertThat(write(new Date(seconds * 1000L))).isEqualTo(String.valueOf(seconds));
assertThat(write(null)).isEqualTo("null");
}

/**
* 序列化与反序列化应当可以互相还原。
*/
@Test
public void testWriteThenRead() throws IOException {
Date now = new Date(1600000000L * 1000L);
assertThat(read(write(now))).isEqualTo(now);
}
}
Loading