我们有一个从字母数字字符串获取日期的方法.假设字符串是Y04051000547GF,则结果日期将是Tue May 04 00:00:00 IST 2010.

public static Date getDateFromGITID(String gitId) {
    
    Date date = null;
    try {

        String dateInGit = nid.substring(1, 7); //  six characters from the second character

        DateFormat format = new SimpleDateFormat("ddMMyy", Locale.ENGLISH);
        date = format.parse(dateInGit);
    } catch (ParseException e) {
        
    }
    
    return date;
}

现在,这个日期被分配给一个类UserInfo的属性,当我们try 将这个类的对象转换为JSON时,日期会递减1天!

Object to JSON string

public static String asJsonString(final Object obj) {
    try {
        return new ObjectMapper().writeValueAsString(obj);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

User Info Bean

public class UserInfo {
    
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    Date date;
}

这是主要的方法

public static void main(String[] args) {
    
    UserInfo obj = new UserInfo();
    obj.date = getDateFromGITID("Y04051000547GF");
    
    System.out.println(obj.date);

    System.out.println(asJsonString(obj));
}

这是控制台的输出

Tue May 04 00:00:00 IST 2010
{"dob":"2010-05-03"}

您可以看到日期是2010年5月4日,但在JSON中它会减少1天.我们能够找出导致问题的原因,日期打印在IST,与UST相差5小时30分钟.因此,我们添加了JSON转换之前的时间,转换后的时间没有变化.

public static void main(String[] args) {
    
    UserInfo obj = new UserInfo();
    obj.dob = getDOBFromNID("Y04051000547GF");
    
    System.out.println(obj.dob);

    obj.dob.setHours(5);
    obj.dob.setMinutes(30);
    obj.dob.setSeconds(00);

    System.out.println(asJsonString(obj));
}

Output

Tue May 04 00:00:00 IST 2010
{"dob":"2010-05-04"}

是的,我们使用了不推荐使用的方法,但它仅用于测试.即使我们try 将时间减少1秒,问题也会出现.

Edit-1 (Found another fix by setting the time zone to the formatter)

当我们将getDateFromGITID方法中DateFormat的时区设置为GST(Java SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") gives timezone as IST)时,我们能够以另一种方式修复此问题.

format.setTimeZone(TimeZone.getTimeZone("GMT"));

所以问题是

  1. 为什么会发生这种事?
  2. 第二种方式是更好的解决办法吗?
  3. 有没有其他更好的解决办法?

推荐答案

您正在使用有严重缺陷的Date-Time类,这些类在几年前就被JSR310中定义的java.time个现代类所取代.

具体地说,我认为您的问题是由于使用java.util.Date#toString方法生成文本造成的.这种方法取决于你.java.util.Date值表示日期和时间,与UTC的时间子午线的偏移量为零小时-分钟-秒.toString方法在生成文本结果时应用JVM的当前默认时区.

虽然用意是好的,但这种反功能给Java开发人员带来了无尽的困惑和痛苦.避免上这门课.避免所有遗留的日期-时间类!

使用业界领先的java.time个课程来完成您所有的约会工作.

String original = "Y04051000547GF" ;
String input = original.substring( 1 , 7 ) ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "ddMMuu" ) ;
LocalDate ld = LocalDate.parse( input , f ) ;

如果您必须与尚未更新到java.time的旧代码进行互操作,请使用添加到旧类中的新方法进行转换.

从一个日期到某个时刻,即时间线上的特定时间点,需要一天中的某个时间,并且需要一个时区.我将假设您想要该日期的第一个时刻,如UTC所示(偏移量为零).

ZonedDateTime zdt = ld.atStartOfDay( ZoneOffset.UTC ) ;
Instant instant = zdt.toInstant() ;
java.util.Date d = java.util.Date.from( instant ) ;

我必须指出,使用带有时间和偏移量的日期类型(如java.util.Date)来表示仅限日期的值是多么不明智.如果可能,请考虑重新编写代码库以使用正确的类型.对于仅限日期的值,请使用java.time.LocalDate类.LocalDate由Java 8+、JDBC 4.2+、Hibernate、Jackson等支持.

或者,Ole V.V.的as Commented,也许你的意思是获得在特定时区看到的一天中的第一个时刻.

ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;

最好的解决方案是教育数据的发布者使用标准的ISO 8601格式进行日期-时间值的数据交换,而不是发明自己的格式.

Java相关问答推荐

我可以在regex中的字符类中放置断言吗?

Java应用程序崩溃时试图读取联系人从电话

我想了解Java中的模块化.编译我的应用程序时,我有一个ResolutionException

Spring Batch 5-不要让它在数据库中自动创建表

使用JdkClientHttpRequestFactory通过Spring RestClient和Wiemock读取时达到EOF

@org.springframework.beans.factory.annotation.Autowired(required=true)-注入点有以下注释:-SpringBoot

对运行在GraalVM-21上的JavaFX应用程序使用分代ZGC会警告不支持JVMCI,为什么?

JavaFX Maven Assembly插件一直打包到错误的JDK版本

为什么JAVA&S清洁器使用链表而不是并发HashSet?

Java:使用Class.cast()将对象转换为原始数组

在Java中如何从Executors.newFixedThreadPool(MAX_THREAD_COUNT())迁移到虚拟线程

由于在生成器模式中使用泛型,lambda表达式中的返回类型错误

如何在我的世界中为互动增加冷却时间?

Lombok@Nonnull是否也对供应商有影响?

try 使用Spring集成和MySQL实现发件箱模式时,锁定等待超时

寻找Thread.sky()方法的清晰度

在缺少字段时使用Jackson With Options生成Optional.Empty()

如何通过用户ID向用户发送私信

转换为JSON字符串时,日期按天递减-Java

声纳覆盖范围为 0%,未生成 jacoco.xml