Java에서 모든 날짜 구문 분석


79

나는이 질문이 꽤 많이 요구된다는 것을 알고 있으며 분명히 임의의 날짜를 구문 분석 할 수 없습니다. 그러나 python-dateutil 라이브러리는 내가 던지는 모든 날짜를 구문 분석 할 수 있으며 날짜 형식 문자열을 알아내는 데 전혀 노력을 기울이지 않아도됩니다. Joda 시간은 항상 훌륭한 Java 날짜 파서로 판매되지만 형식을 선택하거나 직접 작성하기 전에 날짜 형식을 결정해야합니다. DateFormatter.parse (mydate)를 호출하고 마술처럼 Date 객체를 다시 가져올 수는 없습니다.

예를 들어 "Wed Mar 04 05:09:06 GMT-06 : 00 2009"날짜는 python-dateutil로 올바르게 구문 분석됩니다.

import dateutil.parser
print dateutil.parser.parse('Wed Mar 04 05:09:06 GMT-06:00 2009')

그러나 다음 Joda 시간 호출은 작동하지 않습니다.

    String date = "Wed Mar 04 05:09:06 GMT-06:00 2009";
    DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
    DateTime dt = fmt.parseDateTime(date);
    System.out.println(date);

고유 한 DateTimeFormatter를 만드는 것은 올바른 형식 문자열과 함께 SimpleDateFormatter를 사용하는 것과 동일한 것처럼 보이기 때문에 목적에 맞지 않습니다.

python-dateutil과 같이 Java에서 날짜를 구문 분석하는 비슷한 방법이 있습니까? 나는 오류에 대해 신경 쓰지 않고 대부분 완벽하기를 원합니다.

답변:


107

가장 좋은 방법은 날짜 형식 패턴과 일치하거나 무차별 대입을 수행하기 위해 정규식에 도움을 요청하는 것입니다.

몇 년 전에 나는 그 일을 하는 약간 어리석은 DateUtil수업 을 썼다 . 관련성 추출은 다음과 같습니다.

private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {{
    put("^\\d{8}$", "yyyyMMdd");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
    put("^\\d{12}$", "yyyyMMddHHmm");
    put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
    put("^\\d{14}$", "yyyyMMddHHmmss");
    put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
}};

/**
 * Determine SimpleDateFormat pattern matching with the given date string. Returns null if
 * format is unknown. You can simply extend DateUtil with more formats if needed.
 * @param dateString The date string to determine the SimpleDateFormat pattern for.
 * @return The matching SimpleDateFormat pattern, or null if format is unknown.
 * @see SimpleDateFormat
 */
public static String determineDateFormat(String dateString) {
    for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
        if (dateString.toLowerCase().matches(regexp)) {
            return DATE_FORMAT_REGEXPS.get(regexp);
        }
    }
    return null; // Unknown format.
}

(기침, 이중 중괄호 초기화, 기침, 100 자 최대 길이에 모두 맞추기위한 것이 었습니다.))

새로운 regex 및 dateformat 패턴을 사용하여 쉽게 확장 할 수 있습니다.


3
모호한 날짜로 무엇을합니까? 예를 들어 03/04/20102010 년 4 월 3 일 또는 2010 년 3 월 4 일은 무엇을 의미합니까?
Jesper

3
나는 하나 또는 다른 (구성 가능) 가정합니다
Bozho

3
@Jesper : /구분 기호는 일반적으로 표시하는 데 사용됩니다 MM/dd/yyyy(주로 미국 / 영어 로케일에서 사용됨). -세퍼레이터 일반적 나타내는 데 사용된다 dd-MM-yyyy(주로 유럽 로케일에서 사용).
BalusC 2010 년

3
@Jesper 예, 형식을 사용하여 한 달 또는 하루 사이에서 결정해야합니다. 그렇지 않으면 아무데도 얻을 수 없습니다.
Max

3
@kittylyst : 맞습니다. 더군다나 이에 대한 방탄 접근법은 존재하지 않습니다. :)
BalusC

52

귀하의 목적에 맞는 Natty 라는 멋진 라이브러리가 있습니다 .

Natty는 Java로 작성된 자연어 날짜 파서입니다. 날짜 표현식이 주어지면 natty는 표준 언어 인식 및 번역 기술을 적용하여 선택적 구문 분석 및 구문 정보와 함께 해당 날짜 목록을 생성합니다.

온라인으로 시도 할 수도 있습니다 !


감사합니다! 정말 좋은 선택 인 것 같습니다.
Raju Penumatsa

와! 모든 형식의 날짜를 구문 분석 할 수있는이 라이브러리의 기능에 깊은 인상을 받았습니다. 파싱 ​​시간에 약간의 도움이 필요하지만 SoftwareRecs.SE의이 게시물에서 언급했습니다. softwarerecs.stackexchange.com/questions/26556/…
Michael Plautz

1
이 손 최고의 라이브러리 아래로, 심지어 같은 것들을 시도했습니다입니다 : "크리스마스 2,012 전날"그것은 올바르게 구문 분석
JJJ

5
"13/02/2002"로 실패하면 2 월 22 일이됩니다.
Ricardo Freitas

3
예, 놀랍게도 Natty는 일-월-년 형식을 처리 할 수 ​​없습니다.
ConorD55

7

내가 본 것은 몇 가지 일반적인 날짜 형식을 포함하는 Date util 클래스입니다. 따라서 DateUtil.parse (date)가 호출되면 내부적으로 각 날짜 형식으로 날짜를 구문 분석하려고 시도하고 내부 형식 중 어느 것도 구문 분석 할 수없는 경우에만 예외를 발생시킵니다.

기본적으로 문제에 대한 무차별 대입 접근 방식입니다.


이것이 가장 간단하고 이해하기 쉬운 접근이라고 생각합니다. 알 수없는 형식의 날짜 문자열은 의도적으로 모호하기 때문에 형식을 인식하려는 시도에 너무 많은 "지능"을 적용하면 더 "놀라운"결과를 얻을 수 있습니다.
Erich Kitzmueller

예,하지만 큰 룩업 테이블없이 대부분의 정상적인 날짜를 올바르게 구문 분석하기 위해 약간의 시작 정보 (날짜 / 월 / 년 순서)가 주어지면 몇 가지 가정을 할 수 있다고 생각합니다.
Max

Max, 그것은 사실이며, 당신이 찾고있는 제한된 날짜 형식 세트가있을 가능성이 큽니다. 완전한 날짜 구문 분석 엔진을 작성하지 않고 일과 월의 순서에 대해 거의 가정을 할 수 없습니다. 사람들을 올바른 방향으로 안내하는 데 도움이 될 수 있기 때문에 이에 대한 구체적인 사용 사례가 있습니까? 예를 들어 다양한 소셜 미디어 서비스의 대부분의 날짜 형식은 약 10 개의 인기 형식에 적합합니다.
Robert Diana

아마도 저는 사용성 측면에 더 관심이 있습니다. "형식 문자열을 다시 처리하지 않고 대부분의 날짜를 구문 분석합니다." 나는 정말로 자바에서 python-dateutil과 같은 라이브러리를보고 싶다고 생각한다. 그렇게 원한다면 내가 그것을 만들어야한다는 것을 의미한다고 생각한다!
Max

사용성에 대한 우리의 정의도 다른 것 같습니다. 내가 본 날짜 클래스는 약 30 개의 다른 웹 서비스에서 날짜를 구문 분석 할 수있었습니다. 날짜 클래스를 사용하는 것은 parse (date)만큼 간단했기 때문에 유틸리티 사용자로서 날짜 형식에 대해 걱정할 필요가 없습니다. 유틸리티 작성자가 저를 걱정했습니다.
Robert Diana

6

dateparser를 시도해 볼 수 있습니다.

모든 문자열을 자동으로 인식 하고 Date , Calendar , LocalDateTime , OffsetDateTime으로 정확하고 빠르게 파싱 ​​할 수 있습니다 ( 1us~1.5us).

natural language analyzer또는 SimpleDateFormat또는을 기반으로하지 않습니다 regex.Pattern.

이를 통해 yyyy-MM-dd'T'HH:mm:ss.SSSZ또는 yyyy-MM-dd'T'HH:mm:ss.SSSZZ다음 과 같은 적절한 패턴을 준비 할 필요가 없습니다 .

Date date = DateParserUtils.parseDate("2015-04-29T10:15:00.500+0000");
Calendar calendar = DateParserUtils.parseCalendar("2015-04-29T10:15:00.500Z");
LocalDateTime dateTime = DateParserUtils.parseDateTime("2015-04-29 10:15:00.500 +00:00");

모든 것이 잘 작동합니다. 즐기십시오.


방금 살펴 보았습니다. 다양한 형식을 다루는 것 같습니다
Sankalp

0

이 구문 분석에 대해 파이썬에서 수행하는 방법에 대해 전혀 모릅니다. 자바에서는 이렇게 할 수 있습니다.

SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
  java.util.Date normalDate = null;
  java.sql.Date sqlDate = null;
  normalDate = sdf1.parse(date);
  sqlDate = new java.sql.Date(normalDate.getTime());
  System.out.println(sqlDate);

나는 자바처럼 미리 정의 된 일부 함수가 파이썬에있을 것이라고 생각합니다. 이 방법을 따를 수 있습니다. 이 메서드는 문자열 날짜를 Sql 날짜 (dd-MM-yyyy)로 구문 분석합니다.

import java.text.SimpleDateFormat;
import java.text.ParseException;
public class HelloWorld{
     public static void main(String []args){
        String date ="26-12-2019";
         SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
        java.util.Date normalDate = null;
        java.sql.Date sqlDate = null;
        if( !date.isEmpty()) {
            try {
                normalDate = sdf1.parse(date);
                sqlDate = new java.sql.Date(normalDate.getTime());
                System.out.println(sqlDate);
            } catch (ParseException e) {
            }
        }
     }
} 

이것을 실행하십시오!


1
어린 아이들에게 길고 낡고 악명 높은 SimpleDateFormat수업 을 사용하도록 가르치지 마십시오 . 적어도 첫 번째 옵션은 아닙니다. 그리고 예약 없이는 아닙니다. 오늘날 우리는 java.time, 최신 Java 날짜 및 시간 APIDateTimeFormatter.
Ole VV

문제를 해결하는 방법을 안다면 최신 업데이트를 살펴볼 것입니다. 이제 우리는 해결책을 얻었으며 훨씬 더 나은 해결책을 얻으려고 노력할 것입니다. 어쨌든 업데이트 주셔서 감사합니다!
Shashidhar Reddy

1
분을 나타내는 mm 오타가 있습니다. 월을 나타내는 MM을 사용해야합니다.
Shashidhar Reddy

0
//download library:   org.ocpsoft.prettytime.nlp.PrettyTimeParser
String str = "2020.03.03";
Date date = new PrettyTimeParser().parseSyntax(str).get(0).getDates().get(0);
System.out.println(date)

1
코드를 붙여 넣는 대신 항상 컨텍스트에 답하십시오. 자세한 내용은 여기 를 참조하십시오.
gehbiszumeis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.