ResultSet.getDate() 返回错误的日期
ResultSet.getDate() returning wrong date
我在使用 ResultSet.getDate()
方法时遇到问题。我在 MySQL 中有一个日期字段,当我尝试获取该值时,获取的日期是今天的日期,而不是指定的 table 中的日期。我不知道是什么导致了这个错误,我搜索了其他帖子,但是 getDate()
的其他错误是不同的,比如解析或数据不匹配错误或其他类型的错误。可能是时区错误,因为日期的值是昨天的,但是有一行日期是两天前的,它也返回今天的日期。
代码如下:
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.joda.time.LocalDate;
import model.Paciente;
import teste.ConnectionFactory;
public class PacienteDao {
// a conexão com o banco de dados
private Connection connection;
public PacienteDao() {
this.connection = new ConnectionFactory().getConnection();
}
public void adiciona(Paciente paciente) {
String sql = "insert into paciente" +
" (nome_paciente,cpf_paciente,rg_paciente,data_nasc)" +
"values (?,?,?,?)";
try {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, paciente.getNome_paciente());
stmt.setString(2, paciente.getCpf());
stmt.setString(3, paciente.getRg());
java.sql.Date data_nasc = new java.sql.Date(paciente.getData_nasc().toDate().getTime());
stmt.setDate(4, data_nasc);
stmt.execute();
stmt.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public List<Paciente> listaPacientes() {
List<Paciente> pacientes = new ArrayList<Paciente>();
try {
PreparedStatement stmt = this.connection.prepareStatement("select * from paciente");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Paciente paciente = new Paciente();
paciente.setId_paciente(rs.getInt("id_paciente"));
paciente.setNome_paciente(rs.getString("nome_paciente"));
paciente.setCpf(rs.getString("cpf_paciente"));
paciente.setRg(rs.getString("rg_paciente"));
LocalDate dt = new LocalDate();
dt.fromDateFields(rs.getDate("data_nasc"));
paciente.setData_nasc(dt);
pacientes.add(paciente);
}
rs.close();
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
return pacientes;
}
这是应返回的数据 (CSV):
- "1","Lucas","1111111111","12222222","2017-12-19"
- "2","Lucas","1111111111","12222222","2017-12-20"
- "3","Lucas","1111111111","12222222","2017-12-20"
- "4","Leandro","2321","21232","2017-12-20"
这是返回的数据 (StackTrace):
编号:1
姓名:卢卡斯
公积金:1111111111
手机:12222222
纳西门托数据:2017-12-21
编号:2
姓名:卢卡斯
公积金:1111111111
手机:12222222
纳西门托数据:2017-12-21
编号:3
姓名:卢卡斯
公积金:1111111111
手机:12222222
纳西门托数据:2017-12-21
编号:4
姓名:莱安德罗
中央公积金:2321
RG:21232
纳西门托数据:2017-12-21
就像我说的,其中一行的日期是两天前,但它也显示今天的日期,所以我认为这不是时区错误。
PS: 变量和方法的名称是葡萄牙语,因为应用程序也是葡萄牙语。
问题就在这里
LocalDate dt = new LocalDate();
dt.fromDateFields(rs.getDate("data_nasc"));
第一个语句创建一个新的 LocalDate
设置为今天。第二条语句是对 static
方法 fromDateFields
的调用,您的 IDE an/or 编译器应该将其标记为警告。此方法 returns 一个 new LocalDate
对象,您已将其丢弃,并且不会修改 dt
。上面应该是:
LocalDate dt = LocalDate.fromDateFields(rs.getDate("data_nasc"));
是正确的。下面看到的更简单、更直观的代码可以避免那个特定的错误。
此外,您是:
- 在确定日期时忽略时区的关键问题。
- 使用来自建议您迁移到其现代替代品的项目的旧库 类。
tl;博士
使用 java.time 类 替换了 Joda-Time。
myResultSet().getObject( … , Instant.class ) // Extract a moment on the timeline in UTC, an `Instant` object.
.atZone( ZoneId.of( "Asia/Kolkata" ) ) // Adjust into a time zone, to determine a date, rendering a `ZonedDateTime` object.
.toLocalDate() // Extract a date-only object, a `LocalDate` without time-of-day and without a time zone.
避免遗留问题 类
您不应该使用 PreparedStatement::getDate()
。避免所有麻烦的旧日期时间 类 与 Java 的最早版本捆绑在一起,例如 Date、Calendar 和相关的 java.sql 类型。这些完全被 java.time 类 和 JDBC 4.2 驱动程序所取代。
同样,Joda-Time 项目现在处于维护模式。它的团队建议迁移到他们在 JSR 310 中启发、定义和实现的 java.time。
java.time
使用 getObject
和 setObject
方法。
LocalDate ld = myResultSet().getObject( … , LocalDate.class ) ; // For retrieving a standard SQL `DATE` column.
还有……
myPrepatedStatement.setObject( … , ld ) ;
上面的代码用于标准 SQL DATE
列,它是一个仅限日期的值。
但听起来您好像存储了一个时刻,也许 MySQL 类型 TIMESTAMP
似乎与标准 SQL TIMESTAMP WITH TIME ZONE
类型一致。任何提供的偏移量或时区信息都用于在提交到数据库时将值调整为 UTC,MySQL,精度为微秒。
因此 Java 中的等效类型是 Instant
,对于 UTC 时间轴中的一个点,但具有更精细的纳秒分辨率。
Instant instant = myResultSet.getObject( … , Instant.class ) ;
还有……
myPreparedStatement.setObject( … , instant ) ;
请记住,Instant
始终采用 UTC。但是确定日期需要时区。对于任何给定时刻,日期因区域而异。
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
从那里提取似乎是您目标的仅限日期的对象。
LocalDate ld = zdt.toLocalDate() ;
我在使用 ResultSet.getDate()
方法时遇到问题。我在 MySQL 中有一个日期字段,当我尝试获取该值时,获取的日期是今天的日期,而不是指定的 table 中的日期。我不知道是什么导致了这个错误,我搜索了其他帖子,但是 getDate()
的其他错误是不同的,比如解析或数据不匹配错误或其他类型的错误。可能是时区错误,因为日期的值是昨天的,但是有一行日期是两天前的,它也返回今天的日期。
代码如下:
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.joda.time.LocalDate;
import model.Paciente;
import teste.ConnectionFactory;
public class PacienteDao {
// a conexão com o banco de dados
private Connection connection;
public PacienteDao() {
this.connection = new ConnectionFactory().getConnection();
}
public void adiciona(Paciente paciente) {
String sql = "insert into paciente" +
" (nome_paciente,cpf_paciente,rg_paciente,data_nasc)" +
"values (?,?,?,?)";
try {
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, paciente.getNome_paciente());
stmt.setString(2, paciente.getCpf());
stmt.setString(3, paciente.getRg());
java.sql.Date data_nasc = new java.sql.Date(paciente.getData_nasc().toDate().getTime());
stmt.setDate(4, data_nasc);
stmt.execute();
stmt.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public List<Paciente> listaPacientes() {
List<Paciente> pacientes = new ArrayList<Paciente>();
try {
PreparedStatement stmt = this.connection.prepareStatement("select * from paciente");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
Paciente paciente = new Paciente();
paciente.setId_paciente(rs.getInt("id_paciente"));
paciente.setNome_paciente(rs.getString("nome_paciente"));
paciente.setCpf(rs.getString("cpf_paciente"));
paciente.setRg(rs.getString("rg_paciente"));
LocalDate dt = new LocalDate();
dt.fromDateFields(rs.getDate("data_nasc"));
paciente.setData_nasc(dt);
pacientes.add(paciente);
}
rs.close();
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
return pacientes;
}
这是应返回的数据 (CSV):
- "1","Lucas","1111111111","12222222","2017-12-19"
- "2","Lucas","1111111111","12222222","2017-12-20"
- "3","Lucas","1111111111","12222222","2017-12-20"
- "4","Leandro","2321","21232","2017-12-20"
这是返回的数据 (StackTrace):
编号:1 姓名:卢卡斯 公积金:1111111111 手机:12222222 纳西门托数据:2017-12-21
编号:2 姓名:卢卡斯 公积金:1111111111 手机:12222222 纳西门托数据:2017-12-21
编号:3 姓名:卢卡斯 公积金:1111111111 手机:12222222 纳西门托数据:2017-12-21
编号:4 姓名:莱安德罗 中央公积金:2321 RG:21232 纳西门托数据:2017-12-21
就像我说的,其中一行的日期是两天前,但它也显示今天的日期,所以我认为这不是时区错误。
PS: 变量和方法的名称是葡萄牙语,因为应用程序也是葡萄牙语。
问题就在这里
LocalDate dt = new LocalDate();
dt.fromDateFields(rs.getDate("data_nasc"));
第一个语句创建一个新的 LocalDate
设置为今天。第二条语句是对 static
方法 fromDateFields
的调用,您的 IDE an/or 编译器应该将其标记为警告。此方法 returns 一个 new LocalDate
对象,您已将其丢弃,并且不会修改 dt
。上面应该是:
LocalDate dt = LocalDate.fromDateFields(rs.getDate("data_nasc"));
此外,您是:
- 在确定日期时忽略时区的关键问题。
- 使用来自建议您迁移到其现代替代品的项目的旧库 类。
tl;博士
使用 java.time 类 替换了 Joda-Time。
myResultSet().getObject( … , Instant.class ) // Extract a moment on the timeline in UTC, an `Instant` object.
.atZone( ZoneId.of( "Asia/Kolkata" ) ) // Adjust into a time zone, to determine a date, rendering a `ZonedDateTime` object.
.toLocalDate() // Extract a date-only object, a `LocalDate` without time-of-day and without a time zone.
避免遗留问题 类
您不应该使用 PreparedStatement::getDate()
。避免所有麻烦的旧日期时间 类 与 Java 的最早版本捆绑在一起,例如 Date、Calendar 和相关的 java.sql 类型。这些完全被 java.time 类 和 JDBC 4.2 驱动程序所取代。
同样,Joda-Time 项目现在处于维护模式。它的团队建议迁移到他们在 JSR 310 中启发、定义和实现的 java.time。
java.time
使用 getObject
和 setObject
方法。
LocalDate ld = myResultSet().getObject( … , LocalDate.class ) ; // For retrieving a standard SQL `DATE` column.
还有……
myPrepatedStatement.setObject( … , ld ) ;
上面的代码用于标准 SQL DATE
列,它是一个仅限日期的值。
但听起来您好像存储了一个时刻,也许 MySQL 类型 TIMESTAMP
似乎与标准 SQL TIMESTAMP WITH TIME ZONE
类型一致。任何提供的偏移量或时区信息都用于在提交到数据库时将值调整为 UTC,MySQL,精度为微秒。
因此 Java 中的等效类型是 Instant
,对于 UTC 时间轴中的一个点,但具有更精细的纳秒分辨率。
Instant instant = myResultSet.getObject( … , Instant.class ) ;
还有……
myPreparedStatement.setObject( … , instant ) ;
请记住,Instant
始终采用 UTC。但是确定日期需要时区。对于任何给定时刻,日期因区域而异。
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
从那里提取似乎是您目标的仅限日期的对象。
LocalDate ld = zdt.toLocalDate() ;