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

8wtpewkr  于 2023-10-21  发布在  Java
关注(0)|答案(1)|浏览(112)

我们有一个从字母数字字符串中获取日期的方法。假设字符串是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的属性,当我们试图将这个类的对象转换为JSON时,日期会减少1天!

Object转JSON字符串

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

用户信息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));
}

输出

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

是的,我们已经使用了过时的方法,但它只是为了测试。即使我们试图减少1秒的时间,问题也会出现。

Edit-1(通过将时区设置为格式化程序找到另一个修复)

我们可以通过另一种方式解决这个问题,即在getDateFromGITID方法中将DateFormat的时区设置为GSTJava SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") gives timezone as IST)。

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

所以问题是
1.为什么会这样?
1.第二种方法是不是更好?
1.还有更好的解决办法吗?

1dkrff03

1dkrff031#

您正在使用有严重缺陷的日期-时间类,这些类在几年前就被JSR 310中定义的现代 java.time 类所取代。
具体来说,我希望你的问题是由于使用java.util.Date#toString方法来生成文本。这种方法对你撒谎。java.util.Date值表示从UTC时间子午线偏移0小时-分钟-秒的日期和时间。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.所评论的那样,也许你的意思是在一个特定的时区看到一天的第一个时刻。

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

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

相关问题