[英]Why does parsing “0000:00:00 00:00:00” into a Date return -0001-11-28T00:00:00Z?
為什么下面的代碼 output -0001-11-28T00:00:00Z
而不是0000-00-00T00:00:00Z
?
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Date;
import java.util.TimeZone;
class Main
{
public static void main (String[] args) throws ParseException
{
DateFormat parser = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
parser.setTimeZone(TimeZone.getTimeZone("GMT"));
Date date = parser.parse("0000:00:00 00:00:00");
System.out.println(date.toInstant());
}
}
我的第一個想法是這是一個時區問題,但 output 比預期日期早了 34 天。
這是一個第 3 方庫,因此我實際上無法修改代碼,但如果我能理解它為什么返回此值,那么也許我可以調整輸入以獲得所需的 output。
如果您想知道, 0000:00:00 00:00:00
來自圖像或視頻的EXIF 元數據。
請注意,舊版 API 中的年份和年份之間沒有區別。 年份0
實際上是1 BC
。 月0
和日0
是無效值,但SimpleDateFormat
不會拋出異常,而是錯誤地解析它們。
月份轉換為11
的原因:
SimpleDateFormat
將文本中的月份數字減1
,因為java.util.Date
從0
開始。 換句話說,月1
被SimpleDateFormat
解析為0
,即java.util.Date
的Jan
。 同樣,月, 0
被SimpleDateFormat
解析為-1
。 現在,負月份由java.util.Date
處理如下:
month = CalendarUtils.mod(month, 12);
並且CalendarUtils#mod
定義如下:
public static final int mod(int x, int y) {
return (x - y * floorDivide(x, y));
}
public static final int floorDivide(int n, int d) {
return ((n >= 0) ?
(n / d) : (((n + 1) / d) - 1));
}
因此, CalendarUtils.mod(-1, 12)
返回11
。
java.util.Date
和SimpleDateFormat
充滿了這樣的驚喜。 建議完全停止使用它們並切換到現代日期時間 API 。
現代日期時間 API 分別使用y
和u
區分時代和年份。
y
指定時代的年份(時代指定為AD
或BC
)並且始終為正數,而u
指定年份,它是帶符號的 (+/-) 數字。
通常,我們不使用+
號來寫入正數,但我們總是用-
號指定負數。 同樣的規則適用於一年。 只要您要使用時代的年份, AD
, y
和u
都會給您相同的數字。 但是,當您使用時代的年份時,您會得到不同的數字,例如BC
年份, 1 BC
指定為year , 0
; year-of-era , 2 BC
被指定為year , -1
等等。
您可以通過以下演示更好地理解它:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class Testing {
public static void main(String[] args) {
System.out.println(LocalDate.of(-1, 1, 1).format(DateTimeFormatter.ofPattern("u M d")));
System.out.println(LocalDate.of(-1, 1, 1).format(DateTimeFormatter.ofPattern("y M d")));
System.out.println(LocalDate.of(-1, 1, 1).format(DateTimeFormatter.ofPattern("yG M d")));
System.out.println();
System.out.println(LocalDate.of(0, 1, 1).format(DateTimeFormatter.ofPattern("u M d")));
System.out.println(LocalDate.of(0, 1, 1).format(DateTimeFormatter.ofPattern("y M d")));
System.out.println(LocalDate.of(0, 1, 1).format(DateTimeFormatter.ofPattern("yG M d")));
System.out.println();
System.out.println(LocalDate.of(1, 1, 1).format(DateTimeFormatter.ofPattern("u M d")));
System.out.println(LocalDate.of(1, 1, 1).format(DateTimeFormatter.ofPattern("y M d")));
System.out.println(LocalDate.of(1, 1, 1).format(DateTimeFormatter.ofPattern("yG M d")));
}
}
Output:
-1 1 1
2 1 1
2BC 1 1
0 1 1
1 1 1
1BC 1 1
1 1 1
1 1 1
1AD 1 1
0000:00:00 00:00:00
?import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
class Main {
public static void main(String[] args) {
DateTimeFormatter parser = DateTimeFormatter.ofPattern("uuuu:MM:dd HH:mm:ss")
.withZone(ZoneOffset.UTC)
.withLocale(Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse("0000:00:00 00:00:00", parser);
}
}
Output:
Exception in thread "main" java.time.format.DateTimeParseException: Text '0000:00:00 00:00:00' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 0
....
使用DateTimeFormatter#withResolverStyle(ResolverStyle.LENIENT)
:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss", Locale.ENGLISH)
.withResolverStyle(ResolverStyle.LENIENT);
String str = "0000-00-00 00:00:00";
LocalDateTime ldt = LocalDateTime.parse(str, dtf);
System.out.println(ldt);
}
}
Output:
-0001-11-30T00:00
正如其他答案所解釋的,這是使用未進行正確驗證的遺留 class ( SimpleDateFormat
)處理無效時間戳(無效的年、月和日值)的結果。
簡而言之...垃圾進,垃圾出1 。
解決方案:
重寫使用SimpleDateFormat
的代碼,以使用 Java 8 中引入的新日期/時間類。(如果必須使用 Java 7 及更早版本,則使用反向端口。)
在嘗試將字符串作為日期處理之前,通過測試此特定情況來解決此問題。
從上下文看來,“0000:00:00 00:00:00”是 EXIF 表示“沒有這樣的日期時間”的方式。 如果是這種情況,那么試圖將其視為日期時間似乎適得其反。 而是將其視為特殊情況。
如果您無法重寫代碼或解決問題,請針對(第 3 方)庫提交錯誤報告和/或補丁,並希望獲得最好的結果......
1 - 為什么差異恰好是 1 年 34 天有點神秘,但我相信您可以通過深入研究源代碼來找出解釋。 IMO,這不值得努力。 但是,我無法想象為什么格里高利轉變會牽涉其中……
這是因為第 0 年是無效的,它不存在。 https://en.m.wikipedia.org/wiki/Year_zero
月、日也為 0 無效。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.