Date
Java에서 객체 를 생성하는 가장 명백한 방법 이 더 이상 사용되지 않고 관대 한 달력을 사용하기에는 그렇게 분명하지 않은 것으로 "대체"된 것으로 보이는 것이 궁금합니다 .
일, 월, 연도의 조합으로 제공된 날짜가 유효한 날짜인지 어떻게 확인합니까?
예를 들어 2008-02-31 (예 : yyyy-mm-dd)은 잘못된 날짜입니다.
Date
Java에서 객체 를 생성하는 가장 명백한 방법 이 더 이상 사용되지 않고 관대 한 달력을 사용하기에는 그렇게 분명하지 않은 것으로 "대체"된 것으로 보이는 것이 궁금합니다 .
일, 월, 연도의 조합으로 제공된 날짜가 유효한 날짜인지 어떻게 확인합니까?
예를 들어 2008-02-31 (예 : yyyy-mm-dd)은 잘못된 날짜입니다.
답변:
현재 방법은 캘린더 클래스를 사용하는 것입니다. 예제에서와 같이 범위를 벗어난 경우 날짜 및 throw 및 예외를 확인 하는 setLenient 메서드가 있습니다.
추가하는 것을 잊었습니다 : 달력 인스턴스를 얻고 날짜를 사용하여 시간을 설정하는 경우 이것이 유효성 검사를받는 방법입니다.
Calendar cal = Calendar.getInstance();
cal.setLenient(false);
cal.setTime(yourDate);
try {
cal.getTime();
}
catch (Exception e) {
System.out.println("Invalid date");
}
java.util.Date
, java.util.Calendar
그리고 java.text.SimpleDateFormat
지금 기존 에 의해 대체, java.time의 나중에 자바 8에 내장 된 클래스. Oracle의 Tutorial 을 참조하십시오 .
키는 df.setLenient (false); . 이것은 단순한 경우에 충분합니다. 좀 더 강력한 (의심 스러운 ) 및 / 또는 joda-time과 같은 대체 라이브러리를 찾고 있다면 "tardate"사용자 의 답변 을 살펴보십시오.
final static String DATE_FORMAT = "dd-MM-yyyy";
public static boolean isDateValid(String date)
{
try {
DateFormat df = new SimpleDateFormat(DATE_FORMAT);
df.setLenient(false);
df.parse(date);
return true;
} catch (ParseException e) {
return false;
}
}
setLenient
여부는 : SimpleDateFormat
항상 이렇게 당신이 얻을, 패턴이 일치 될 때까지 구문 분석하고 문자열의 나머지 부분을 무시 201
해있다.
java.util.Date
, java.util.Calendar
그리고 java.text.SimpleDateFormat
지금 기존 에 의해 대체, java.time의 나중에 자바 8에 내장 된 클래스. Oracle의 Tutorial 을 참조하십시오 .
@Maglob에서 볼 수 있듯이 기본 접근 방식은 SimpleDateFormat.parse를 사용하여 문자열에서 날짜로의 변환을 테스트하는 것 입니다. 이는 2008-02-31과 같은 잘못된 일 / 월 조합을 포착합니다.
그러나 실제로는 SimpleDateFormat.parse가 매우 자유롭기 때문에 충분하지 않습니다. 우려 할 수있는 두 가지 동작이 있습니다.
날짜 문자열의 잘못된 문자 놀랍게도 2008-02-2x는 로케일 형식이 "yyyy-MM-dd"인 유효한 날짜로 "통과"됩니다. isLenient == false 인 경우에도 마찬가지입니다.
연도 : 2, 3 또는 4 자리 숫자? 기본 SimpleDateFormat 동작을 허용하는 대신 4 자리 연도를 적용 할 수도 있습니다 (형식이 "yyyy-MM-dd"인지 "yy-MM-dd"인지에 따라 "12-02-31"을 다르게 해석합니다.) )
따라서 전체 문자열 대 날짜 테스트는 다음과 같이 보일 수 있습니다. 정규식 일치의 조합과 강제 날짜 변환입니다. 정규식의 트릭은 로케일 친화적으로 만드는 것입니다.
Date parseDate(String maybeDate, String format, boolean lenient) {
Date date = null;
// test date string matches format structure using regex
// - weed out illegal characters and enforce 4-digit year
// - create the regex based on the local format string
String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");
reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");
if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {
// date string matches format structure,
// - now test it can be converted to a valid date
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();
sdf.applyPattern(format);
sdf.setLenient(lenient);
try { date = sdf.parse(maybeDate); } catch (ParseException e) { }
}
return date;
}
// used like this:
Date date = parseDate( "21/5/2009", "d/M/yyyy", false);
정규식은 형식 문자열에 일, 월, 연도 및 구분 문자 만 포함되어 있다고 가정합니다. 그 외에도 "d / MM / yy", "yyyy-MM-dd"등 모든 로케일 형식으로 형식을 지정할 수 있습니다. 현재 로케일의 형식 문자열은 다음과 같이 얻을 수 있습니다.
Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();
최근 에 joda time 에 대해 듣고 비교해 볼까 생각했습니다. 두 가지 사항 :
사용하기는 매우 간단합니다.
import org.joda.time.format.*;
import org.joda.time.DateTime;
org.joda.time.DateTime parseDate(String maybeDate, String format) {
org.joda.time.DateTime date = null;
try {
DateTimeFormatter fmt = DateTimeFormat.forPattern(format);
date = fmt.parseDateTime(maybeDate);
} catch (Exception e) { }
return date;
}
Date
, SimpleDateFormat
, 등) 지금 현대에 의해 대체된다 java.time의 클래스. 마찬가지로 Joda-Time 프로젝트는 유지 관리 모드에 있으며 java.time 클래스 로의 마이그레이션을 권장 합니다.
SimpleDateFormat 을 사용할 수 있습니다.
예를 들면 다음과 같습니다.
boolean isLegalDate(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
return sdf.parse(s, new ParsePosition(0)) != null;
}
java.util.Date
, java.util.Calendar
그리고 java.text.SimpleDateFormat
지금 기존 에 의해 대체, java.time의 나중에 자바 8에 내장 된 클래스. Oracle의 Tutorial 을 참조하십시오 .
사용 엄격 모드 에서 java.time.DateTimeFormatter
구문 분석 A와 LocalDate
. 에 대한 트랩 DateTimeParseException
.
LocalDate.parse( // Represent a date-only value, without time-of-day and without time zone.
"31/02/2000" , // Input string.
DateTimeFormatter // Define a formatting pattern to match your input string.
.ofPattern ( "dd/MM/uuuu" )
.withResolverStyle ( ResolverStyle.STRICT ) // Specify leniency in tolerating questionable inputs.
)
구문 분석 후 합리적인 값을 확인할 수 있습니다. 예를 들어, 지난 100 년 이내의 생년월일입니다.
birthDate.isAfter( LocalDate.now().minusYears( 100 ) )
Java의 초기 버전과 함께 제공되는 성가신 이전 날짜-시간 클래스를 사용하지 마십시오. 이제 java.time 클래스로 대체되었습니다 .
LocalDate
& DateTimeFormatter
&ResolverStyle
LocalDate
클래스는 시간이 하루의 시간 영역없이없이 날짜 만 값을 나타냅니다.
String input = "31/02/2000";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu" );
try {
LocalDate ld = LocalDate.parse ( input , f );
System.out.println ( "ld: " + ld );
} catch ( DateTimeParseException e ) {
System.out.println ( "ERROR: " + e );
}
java.time.DateTimeFormatter
클래스는에 정의 된 세 가지 관용의 모든 모드 문자열을 구문 분석하도록 설정할 수 있습니다 ResolverStyle
열거. 각 모드를 시도하기 위해 위 코드에 줄을 삽입합니다.
f = f.withResolverStyle ( ResolverStyle.LENIENT );
결과 :
ResolverStyle.LENIENT
ResolverStyle.SMART
ResolverStyle.STRICT
ResolverStyle.LENIENT
모드에서 유효하지 않은 날짜가 동일한 일 수만큼 앞으로 이동하는 것을 볼 수 있습니다 . 에ResolverStyle.SMART
모드 (기본), 논리적 인 결정은 개월 이내에 해당 월의 마지막 가능한 하루가는 날짜를 유지하기 위해 만든, 윤년 년 2 월 29 일, 그 달에는 31 일이 없기 때문에. ResolverStyle.STRICT
모드는 예외가 그런 일이 없다고 불평 발생합니다.
이 세 가지 모두 비즈니스 문제와 정책에 따라 합리적입니다. 귀하의 경우에는 엄격 모드가 유효하지 않은 날짜를 조정하는 대신 거부하도록 원하는 것처럼 들립니다.
java.time의 프레임 워크는 나중에 자바 8에 내장되어 있습니다. 이 클래스는 까다로운 기존에 대신 기존 과 같은 날짜 - 시간의 수업을 java.util.Date
,Calendar
, SimpleDateFormat
.
자세한 내용은 Oracle Tutorial을 참조하십시오. . 그리고 많은 예제와 설명을 위해 Stack Overflow를 검색하십시오. 사양은 JSR 310 입니다.
Joda 타임 지금 프로젝트, 유지 관리 모드는 의에 마이그레이션을 조언 java.time의 클래스.
java.time 객체를 데이터베이스와 직접 교환 할 수 있습니다 . JDBC 4.2 와 호환 되는 JDBC 드라이버 사용 이상을 . 문자열이나 java.sql.*
클래스 가 필요하지 않습니다 .
java.time 클래스는 어디서 구할 수 있습니까?
ThreeTen - 추가 프로젝트 추가 클래스와 java.time를 확장합니다. 이 프로젝트는 java.time에 향후 추가 될 수있는 가능성을 입증하는 근거입니다. 당신은 여기에 몇 가지 유용한 클래스와 같은 찾을 수 있습니다 Interval
, YearWeek
, YearQuarter
, 그리고 더 .
으로 날짜 및 시간 API ( java.time의 클래스) 자바 8에 내장 된 이후, 당신은 사용할 수있는 LocalDate
클래스를.
public static boolean isDateValid(int year, int month, int day) {
boolean dateIsValid = true;
try {
LocalDate.of(year, month, day);
} catch (DateTimeException e) {
dateIsValid = false;
}
return dateIsValid;
}
ResolverStyle.SMART
예외를 발생시키는 대신 결과 값을 유효한 날짜로 조정하는 방법을 사용 합니다. 따라서이 코드는 질문의 목표를 달성하지 못합니다. 예를 들어 내 답변 을 참조 하고ResolverStyle.STRICT
.
표준 라이브러리를 사용하는 또 다른 엄격한 솔루션은 다음을 수행하는 것입니다.
1) 패턴을 사용하여 엄격한 SimpleDateFormat 만들기
2) 형식 개체를 사용하여 사용자가 입력 한 값 구문 분석 시도
3) 성공하면 동일한 날짜 형식 ((1)에서)을 사용하여 (2)에서 발생한 날짜를 다시 형식화하십시오.
4) 형식이 변경된 날짜를 사용자가 입력 한 원래 값과 비교합니다. 같으면 입력 한 값이 패턴과 엄격하게 일치합니다.
이렇게하면 복잡한 정규식을 만들 필요가 없습니다. 제 경우에는 일, 월 및 연도와 같은 특정 유형으로 제한되는 대신 SimpleDateFormat의 모든 패턴 구문을 지원해야했습니다.
바탕 라빈의 대답 문제가 지적 해결하기 위해 자신의 의견에 ceklock , 나는이 있는지 확인하는 방법을 추가 dateString
유효하지 않은 문자가 포함되어 있지 않습니다.
방법은 다음과 같습니다.
private boolean isDateCorrect(String dateString) {
try {
Date date = mDateFormatter.parse(dateString);
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return matchesOurDatePattern(dateString); //added my method
}
catch (ParseException e) {
return false;
}
}
/**
* This will check if the provided string matches our date format
* @param dateString
* @return true if the passed string matches format 2014-1-15 (YYYY-MM-dd)
*/
private boolean matchesDatePattern(String dateString) {
return dateString.matches("^\\d+\\-\\d+\\-\\d+");
}
가장 간단한 방법은 문자열을 날짜 개체로 변환하고 다시 문자열로 변환하는 것입니다. 두 문자열이 여전히 일치하면 주어진 날짜 문자열은 괜찮습니다.
public boolean isDateValid(String dateString, String pattern)
{
try
{
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
if (sdf.format(sdf.parse(dateString)).equals(dateString))
return true;
}
catch (ParseException pe) {}
return false;
}
둘 다 문자열 (그렇지 않으면 이미 유효한 날짜 임)이라고 가정하면 다음과 같은 한 가지 방법이 있습니다.
package cruft;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateValidator
{
private static final DateFormat DEFAULT_FORMATTER;
static
{
DEFAULT_FORMATTER = new SimpleDateFormat("dd-MM-yyyy");
DEFAULT_FORMATTER.setLenient(false);
}
public static void main(String[] args)
{
for (String dateString : args)
{
try
{
System.out.println("arg: " + dateString + " date: " + convertDateString(dateString));
}
catch (ParseException e)
{
System.out.println("could not parse " + dateString);
}
}
}
public static Date convertDateString(String dateString) throws ParseException
{
return DEFAULT_FORMATTER.parse(dateString);
}
}
내가 얻는 출력은 다음과 같습니다.
java cruft.DateValidator 32-11-2010 31-02-2010 04-01-2011
could not parse 32-11-2010
could not parse 31-02-2010
arg: 04-01-2011 date: Tue Jan 04 00:00:00 EST 2011
Process finished with exit code 0
보시다시피 두 경우 모두 훌륭하게 처리합니다.
이것은 나를 위해 잘 작동합니다. Ben이 위에서 제안한 접근 방식.
private static boolean isDateValid(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
try {
Date d = asDate(s);
if (sdf.format(d).equals(s)) {
return true;
} else {
return false;
}
} catch (ParseException e) {
return false;
}
}
SimpleDateFormat 이 setLenient (false) 후에도 패턴을 엄격하게 검사하지 않는 것처럼 보입니다 . 방법이 적용되므로 입력 한 날짜가 유효한 날짜인지 여부를 확인하기 위해 아래 방법을 사용했습니다.
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
public boolean isValidFormat(String dateString, String pattern) {
boolean valid = true;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
try {
formatter.parse(dateString);
} catch (DateTimeParseException e) {
valid = false;
}
return valid;
}
SimpleDateFormat 사용에 대한 두 가지 의견.
정적 액세스로 선언 된 경우 정적 인스턴스로 선언해야합니다. 스레드로부터 안전하지 않으므로 동기화해야합니다.
날짜의 각 구문 분석에 대해 인스턴스를 인스턴스화하는 것보다 더 나은 IME.
위의 날짜 구문 분석 방법은 훌륭합니다. 포맷터를 사용하여 변환 된 날짜를 원래 날짜로 다시 확인하는 기존 방법에 새로운 검사를 추가 했으므로 거의 모든 경우에 대해 확인했습니다. 예 : 2013 년 2 월 29 일은 유효하지 않은 날짜입니다. 주어진 함수는 현재 허용되는 날짜 형식에 따라 날짜를 구문 분석합니다. 날짜가 성공적으로 구문 분석되지 않으면 true를 반환합니다.
public final boolean validateDateFormat(final String date) {
String[] formatStrings = {"MM/dd/yyyy"};
boolean isInvalidFormat = false;
Date dateObj;
for (String formatString : formatStrings) {
try {
SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
sdf.applyPattern(formatString);
sdf.setLenient(false);
dateObj = sdf.parse(date);
System.out.println(dateObj);
if (date.equals(sdf.format(dateObj))) {
isInvalidFormat = false;
break;
}
} catch (ParseException e) {
isInvalidFormat = true;
}
}
return isInvalidFormat;
}
다음은 외부 라이브러리를 사용하지 않는 노드 환경에서 수행 한 작업입니다.
Date.prototype.yyyymmdd = function() {
var yyyy = this.getFullYear().toString();
var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
var dd = this.getDate().toString();
return zeroPad([yyyy, mm, dd].join('-'));
};
function zeroPad(date_string) {
var dt = date_string.split('-');
return dt[0] + '-' + (dt[1][1]?dt[1]:"0"+dt[1][0]) + '-' + (dt[2][1]?dt[2]:"0"+dt[2][0]);
}
function isDateCorrect(in_string) {
if (!matchesDatePattern) return false;
in_string = zeroPad(in_string);
try {
var idate = new Date(in_string);
var out_string = idate.yyyymmdd();
return in_string == out_string;
} catch(err) {
return false;
}
function matchesDatePattern(date_string) {
var dateFormat = /[0-9]+-[0-9]+-[0-9]+/;
return dateFormat.test(date_string);
}
}
그리고 그것을 사용하는 방법은 다음과 같습니다.
isDateCorrect('2014-02-23')
true
// to return valid days of month, according to month and year
int returnDaysofMonth(int month, int year) {
int daysInMonth;
boolean leapYear;
leapYear = checkLeap(year);
if (month == 4 || month == 6 || month == 9 || month == 11)
daysInMonth = 30;
else if (month == 2)
daysInMonth = (leapYear) ? 29 : 28;
else
daysInMonth = 31;
return daysInMonth;
}
// to check a year is leap or not
private boolean checkLeap(int year) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}
다음은 날짜 형식을 확인합니다.
public static boolean checkFormat(String dateTimeString) {
return dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}") || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}")
|| dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}") || dateTimeString
.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z") ||
dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}Z");
}
public static String detectDateFormat(String inputDate, String requiredFormat) {
String tempDate = inputDate.replace("/", "").replace("-", "").replace(" ", "");
String dateFormat;
if (tempDate.matches("([0-12]{2})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMddyyyy";
} else if (tempDate.matches("([0-31]{2})([0-12]{2})([0-9]{4})")) {
dateFormat = "ddMMyyyy";
} else if (tempDate.matches("([0-9]{4})([0-12]{2})([0-31]{2})")) {
dateFormat = "yyyyMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([0-12]{2})")) {
dateFormat = "yyyyddMM";
} else if (tempDate.matches("([0-31]{2})([a-z]{3})([0-9]{4})")) {
dateFormat = "ddMMMyyyy";
} else if (tempDate.matches("([a-z]{3})([0-31]{2})([0-9]{4})")) {
dateFormat = "MMMddyyyy";
} else if (tempDate.matches("([0-9]{4})([a-z]{3})([0-31]{2})")) {
dateFormat = "yyyyMMMdd";
} else if (tempDate.matches("([0-9]{4})([0-31]{2})([a-z]{3})")) {
dateFormat = "yyyyddMMM";
} else {
return "Pattern Not Added";
//add your required regex
}
try {
String formattedDate = new SimpleDateFormat(requiredFormat, Locale.ENGLISH).format(new SimpleDateFormat(dateFormat).parse(tempDate));
return formattedDate;
} catch (Exception e) {
//
return "";
}
}
'레거시'날짜 형식을 사용하면 결과의 형식을 지정하고 소스와 다시 비교할 수 있습니다.
public boolean isValidFormat(String source, String pattern) {
SimpleDateFormat sd = new SimpleDateFormat(pattern);
sd.setLenient(false);
try {
Date date = sd.parse(source);
return date != null && sd.format(date).equals(source);
} catch (Exception e) {
return false;
}
}
이 발췌문은 '01 .01.2004 '패턴으로 source = 01.01.04에'false '라고 말합니다.
엄격한 유효성 검사를 원하면 setLenient를 false로 설정하십시오.
public boolean isThisDateValid(String dateToValidate, String dateFromat){
if(dateToValidate == null){
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat(dateFromat);
sdf.setLenient(false);
try {
//if not valid, it will throw ParseException
Date date = sdf.parse(dateToValidate);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
return false;
}
return true;
}