=Start=
缘由:
整理一下Java中校验一个日期是否正确且合法的方法,网上关于这类功能的代码很多,但质量参差不齐,且不保证正确性,所以自己在此整理一下自己验证过的内容,方便需要的时候参考和使用。
正文:
参考解答:
可行的方法:
- 自己按照原理进行编写
- SimpleDateFormat + setLenient(false)
- LocalDate (需要Java 8及以上版本)
- Apache Commons Validator 包
package com.example.learn;
import org.apache.commons.validator.GenericValidator;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
public class DateUtils {
public static String dateArray[] = {"2020-02-28","2020-02-29","2020-02-30","2020-02-31","2020-01-31","2019-02-29"};
public static String datePattern = "yyyy-MM-dd";
// Key is setLenient(false);
public static boolean isValidDate(String dateString, String datePattern) {
try {
DateFormat df = new SimpleDateFormat(datePattern);
df.setLenient(false);
df.parse(dateString);
return true;
} catch (ParseException e) {
return false;
}
}
// The java.time framework is built into Java 8 and later.
public static boolean isValidDate2(String dateString) {
boolean dateIsValid = true;
try {
int year = Integer.parseInt(dateString.substring(0,4));
int month = Integer.parseInt(dateString.substring(5,7));
int day = Integer.parseInt(dateString.substring(8,10));
// System.out.println(String.format("%d\t%d\t%d", year, month, day));
LocalDate.of(year, month, day);
} catch (DateTimeException e) {
dateIsValid = false;
}
return dateIsValid;
}
// The java.time framework is built into Java 8 and later.
// Error case:
// 2020-02-30: true
// 2020-02-31: true
// 2019-02-29: true
public static boolean isValidDateFormat(String dateString, String pattern) {
boolean valid = true;
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern).withResolverStyle(ResolverStyle.STRICT);
try {
formatter.parse(dateString);
} catch (DateTimeParseException e) {
valid = false;
}
return valid;
}
static int MAX_VALID_YR = 9999;
static int MIN_VALID_YR = 1900;
// Judge is 闰年(leap year) or not ?
static boolean isLeap(int year) {
// Return true if year is a multiple of 4 and not multiple of 100.
// OR year is multiple of 400.
return (((year % 4 == 0) &&
(year % 100 != 0)) ||
(year % 400 == 0));
}
// Returns true if given year+month+day is valid, or false(not valid).
static boolean isValidDate3(int y, int m, int d) {
// If year, month and day // are not in given range
if (y > MAX_VALID_YR || y < MIN_VALID_YR)
return false;
if (m < 1 || m > 12)
return false;
if (d < 1 || d > 31)
return false;
// Handle February month with leap year
if (m == 2) {
if (isLeap(y))
return (d <= 29);
else
return (d <= 28);
}
// Months of April, June, Sept and Nov must have
// number of days less than or equal to 30.
if (m == 4 || m == 6 || m == 9 || m == 11)
return (d <= 30);
return true;
}
public static void main(String[] args) {
System.out.println("SimpleDateFormat-setLenient");
for (String dateStr: dateArray) {
System.out.println(String.format("%s: %b", dateStr, isValidDate(dateStr, datePattern)));
}
System.out.println("\njava.time.LocalDate");
for (String dateStr: dateArray) {
System.out.println(String.format("%s: %b", dateStr, isValidDate2(dateStr)));
}
System.out.println("\ncommons-validator");
for (String dateStr: dateArray) {
System.out.println(String.format("%s: %b", dateStr, GenericValidator.isDate(dateStr, datePattern, true)));
}
System.out.println("\nvia default judgement");
for (String dateStr: dateArray) {
int year = Integer.parseInt(dateStr.substring(0,4));
int month = Integer.parseInt(dateStr.substring(5,7));
int day = Integer.parseInt(dateStr.substring(8,10));
System.out.println(String.format("%s: %b", dateStr, isValidDate3(year, month, day)));
}
System.out.println("\nDateTimeFormatter");
for (String dateStr: dateArray) {
System.out.println(String.format("%s: %b", dateStr, isValidDateFormat(dateStr, datePattern)));
}
}
}
参考链接:
Program to check if a date is valid or not
https://www.geeksforgeeks.org/program-check-date-valid-not/
Check If a String Is a Valid Date in Java
https://www.baeldung.com/java-string-valid-date
https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-datetime-string
https://mvnrepository.com/artifact/commons-validator/commons-validator/1.6
Java中如何校验一个日期是否正确?
https://stackoverflow.com/questions/226910/how-to-sanity-check-a-date-in-java
https://stackoverflow.com/a/4528094
https://stackoverflow.com/a/23438158
https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html#setLenient(boolean)
How to check if date is valid in Java
https://mkyong.com/java/how-to-check-if-date-is-valid-in-java/
=END=
《 “Java中如何校验一个日期是否正确且合法” 》 有 3 条评论
面试官一步一步的套路你,为什么SimpleDateFormat不是线程安全的
https://mp.weixin.qq.com/s/m74S_AZ6fBO9Z-abCo334g
`
面试官:SimpleDateFormat用过吗?
面试官:能写一下你是如何使用的吗?
面试官:为什么要使用ThreadLocal包装SimpleDateFormat?
小小白:如果不使用ThreadLocal包装一下,直接创建一个SimpleDateFormat共享实例对象,在多线程并发的情况下使用这个对象的方法是线程不安全的,可能会抛出NumberFormatException或其它异常。使用ThreadLocal包装一下,每个线程都有自己的SimpleDateFormat实例对象,这样多线程并发的情况下就不会出现线程不安全的问题了。
面试官:那为什么SimpleDateFormat作为共享变量会出现线程不安全的问题?
面试官:那既然是因为SimpleDateFormat类中的calendar是共享变量导致的,可以将SimpleDateFormat类型的变量设置成方法私有的,每次要用的时候new一个不就完了,或者使用同步锁控制一下并发访问,为什么还要使用ThreadLocal这么麻烦?
小小白:方法私有和同步锁并发控制确实可以解决问题,但是他们有各自的缺点。方法私有,每次都需要创建一个新对象,这样占用内存不说,还需要不断回收。同步锁并发控制,多线程高并发的情况下,性能会严重下降。使用ThreadLocal包装,每个线程只需要创建一个SimpleDateFormat对象实例,既解决不断创建新对象的问题,又解决了并发的问题。
面试官:那为什么最开始你写的代码中,在finally方法中使用了df.remove(),这是为什么?
小小白:线程用完了SimpleDateFormat,如果不调用remove方法将其清除,可能会引发因使用ThreadLocal而导致的内存泄漏。
面试官:使用ThreadLocal为什么可能会导致内存泄漏?
`
Java Time Zone When Parsing DateFormat
https://stackoverflow.com/questions/4542679/java-time-zone-when-parsing-dateformat
`
OffsetDateTime odt = OffsetDateTime.parse( “2010-12-27T10:50:44.000-08:00” );
Instant instant = odt.toInstant();
# ISO 8601
The input string format is defined in the ISO 8601 standard, a family of date-time formats.
`
Converting ISO 8601-compliant String to java.util.Date
https://stackoverflow.com/questions/2201925/converting-iso-8601-compliant-string-to-java-util-date
日期和时间格式符号
https://docs.oracle.com/cloud/help/zh_CN/enterprise-data-management-cloud/DMCAA/date_time_formatting_reference_102x5fccd953.htm
`
符号 日期或时间组成部分 示例
G 纪元标志符 AD
y 年份 1996; 96
Y 基于周的年份 (week year) 2009; 09
M 年中的月份 七月;7 月;07
w 年中的第几周 27
W 月中的第几周 2
D 年中的第几天 10
d 月中的第几天 2
E 星期几(名称) 星期二;周二
u 星期几(编号)(1 = 星期一,7 = 星期天) 1
a AM/PM 标记 PM
H 一天中的小时 (0-23) 0
k 一天中的小时 (1-24) 24
K AM/PM 中的小时 (0-11) 0
h AM/PM 中的小时 (1-12) 12
m 小时中的分钟 30
s 分钟中的秒 55
S 毫秒 978
z 时区 太平洋标准时间;PST;GMT-08:00
Z 时区 -800
X 时区 -08; -0800; -08:00
`
Java 日期时间
https://www.runoob.com/java/java-date-time.html
Java时间格式化原来这么多玩法
https://juejin.cn/post/7050329304189648926