2010-10-26 55 views
34

Tôi đang cố gắng phân tích cú pháp một số ngày sắp ra khỏi tài liệu. Có vẻ như người dùng đã nhập những ngày này theo định dạng tương tự nhưng không chính xác.Cách phân tích ngày theo nhiều định dạng bằng cách sử dụng SimpleDateFormat

đây là các định dạng:

9/09 
9/2009 
09/2009 
9/1/2009 
9-1-2009 

cách tốt nhất để đi về cố gắng để phân tích tất cả trong số này là gì? Đây có vẻ là phổ biến nhất, nhưng tôi đoán những gì đang treo tôi lên là nếu tôi có một mô hình "M/yyyy" wont mà luôn luôn bắt trước khi "MM/yyyy" Tôi có phải thiết lập của tôi thử/bắt khối lồng nhau trong một cách hạn chế nhất để hạn chế nhất? nó có vẻ như nó chắc chắn sẽ mất rất nhiều bản sao mã để có được quyền này.

+0

Vâng, bạn chỉ có thể có danh sách các mẫu và sau đó là một vòng lặp đơn giản để tiếp tục cố gắng cho đến khi bạn nhấn vào hoạt động. – Pointy

+0

Và sau đó chỉ cần phá vỡ vòng lặp? – Derek

Trả lời

61

Bạn sẽ cần phải sử dụng một đối tượng SimpleDateFormat khác nhau cho từng mẫu khác nhau. Điều đó nói rằng, bạn không cần phải có nhiều người khác nhau, thanks to this:

Số: Đối với định dạng, số lượng chữ mô hình là số lượng tối thiểu của các chữ số và số ngắn hơn là zero-đệm để số tiền này . Để phân tích cú pháp, số lượng mẫu chữ được bỏ qua trừ khi cần phải tách hai trường liền kề.

Vì vậy, bạn sẽ cần những định dạng:

  • "M/y" (bao gồm 9/09, 9/2009, và 09/2009)
  • "M/d/y" (bao gồm 9/1/2009)
  • "M-d-y" (bao gồm 9-1-2009)

Vì vậy, lời khuyên của tôi sẽ được để viết một phương pháp mà làm việc gì đó như thế này (chưa được kiểm tra):

// ... 
List<String> formatStrings = Arrays.asList("M/y", "M/d/y", "M-d-y"); 
// ... 

Date tryParse(String dateString) 
{ 
    for (String formatString : formatStrings) 
    { 
     try 
     { 
      return new SimpleDateFormat(formatString).parse(dateString); 
     } 
     catch (ParseException e) {} 
    } 

    return null; 
} 
+0

"M/d/y" này cũng bao gồm '12/16/1999' hoặc' 1/16/1999' vv ... – Jaikrat

+0

thx, rất hữu ích – vlasevich

+0

@Matt: Cách phân tích cú pháp 'yyyy-MM-dd' và 'dd-MM-yy' và những người khác nữa. Tôi có thể trừ một trong hai thứ này. Nếu tôi sử dụng cách tiếp cận trên cho cả hai người trong số họ của nó cùng một định dạng. Bạn có thể giúp tôi không. –

9

cách tiếp cận của Matt trên là tốt, nhưng hãy lưu ý rằng bạn sẽ chạy vào các vấn đề nếu bạn sử dụng nó để phân biệt giữa các ngày có định dạng y/M/dd/M/y. Ví dụ: một trình định dạng được khởi tạo với y/M/d sẽ chấp nhận một ngày như 01/01/2009 và cung cấp cho bạn một ngày mà rõ ràng không phải là những gì bạn muốn. Tôi đã khắc phục vấn đề như sau, nhưng tôi có thời gian giới hạn và tôi không hài lòng với giải pháp vì 2 lý do chính:

  1. Vi phạm một trong những nguyên tắc của Josh Bloch, cụ thể là 'không sử dụng ngoại lệ để xử lý chương trình lưu lượng'.
  2. Tôi có thể thấy phương pháp getDateFormat() trở thành một cơn ác mộng nếu bạn cần nó để xử lý nhiều định dạng ngày khác.

Nếu tôi phải làm một thứ có thể xử lý rất nhiều định dạng ngày khác nhau và cần phải có hiệu suất cao, tôi nghĩ tôi sẽ sử dụng phương pháp tạo một enum liên kết từng ngày tháng khác nhau với định dạng của nó . Sau đó, sử dụng MyEnum.values() để lặp qua enum và thử nghiệm với if(myEnum.getPattern().matches(date)) thay vì bắt một nhận dạng ngày.

Anway, đó đang được nói, những điều sau đây có thể xử lý ngày của các định dạng 'y/M/d' 'y-M-d' 'y M d' 'd/M/y' 'd-M-y' 'd M y' và tất cả các biến thể khác của những người trong đó bao gồm các định dạng thời gian cũng như:

import java.text.ParseException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 

public class DateUtil { 
    private static final String[] timeFormats = {"HH:mm:ss","HH:mm"}; 
    private static final String[] dateSeparators = {"/","-"," "}; 

    private static final String DMY_FORMAT = "dd{sep}MM{sep}yyyy"; 
    private static final String YMD_FORMAT = "yyyy{sep}MM{sep}dd"; 

    private static final String ymd_template = "\\d{4}{sep}\\d{2}{sep}\\d{2}.*"; 
    private static final String dmy_template = "\\d{2}{sep}\\d{2}{sep}\\d{4}.*"; 

    public static Date stringToDate(String input){ 
    Date date = null; 
    String dateFormat = getDateFormat(input); 
    if(dateFormat == null){ 
     throw new IllegalArgumentException("Date is not in an accepted format " + input); 
    } 

    for(String sep : dateSeparators){ 
     String actualDateFormat = patternForSeparator(dateFormat, sep); 
     //try first with the time 
     for(String time : timeFormats){ 
     date = tryParse(input,actualDateFormat + " " + time); 
     if(date != null){ 
      return date; 
     } 
     } 
     //didn't work, try without the time formats 
     date = tryParse(input,actualDateFormat); 
     if(date != null){ 
     return date; 
     } 
    } 

    return date; 
    } 

    private static String getDateFormat(String date){ 
    for(String sep : dateSeparators){ 
     String ymdPattern = patternForSeparator(ymd_template, sep); 
     String dmyPattern = patternForSeparator(dmy_template, sep); 
     if(date.matches(ymdPattern)){ 
     return YMD_FORMAT; 
     } 
     if(date.matches(dmyPattern)){ 
     return DMY_FORMAT; 
     } 
    } 
    return null; 
    } 

    private static String patternForSeparator(String template, String sep){ 
    return template.replace("{sep}", sep); 
    } 

    private static Date tryParse(String input, String pattern){ 
    try{ 
     return new SimpleDateFormat(pattern).parse(input); 
    } 
    catch (ParseException e) {} 
    return null; 
    } 


} 
6

Trong Apache commons lang, DateUtils lớp chúng ta có một phương pháp được gọi là parseDate. Chúng ta có thể sử dụng nó để phân tích ngày tháng.

Đồng thời thư viện Joda-time khác cũng có phương thức là parse ngày.

3

Giải pháp này kiểm tra tất cả các định dạng có thể trước khi ném ngoại lệ. Giải pháp này thuận tiện hơn nếu bạn đang cố thử nghiệm nhiều định dạng ngày.

Date extractTimestampInput(String strDate){ 
    final List<String> dateFormats = Arrays.asList("yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd");  

    for(String format: dateFormats){ 
     SimpleDateFormat sdf = new SimpleDateFormat(format); 
     try{ 
      return sdf.parse(strDate); 
     } catch (ParseException e) { 
      //intentionally empty 
     } 
    } 
     throw new IllegalArgumentException("Invalid input for date. Given '"+strDate+"', expecting format yyyy-MM-dd HH:mm:ss.SSS or yyyy-MM-dd."); 

} 
14

Còn về việc chỉ định nhiều mẫu? Họ có thể đến từ một tập tin cấu hình chứa mẫu nổi tiếng, cứng mã hoá nó đọc như:

List<SimpleDateFormat> knownPatterns = new ArrayList<SimpleDateFormat>(); 
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")); 
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm.ss'Z'")); 
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")); 
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss")); 
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")); 

for (SimpleDateFormat pattern : knownPatterns) { 
    try { 
     // Take a try 
     return new Date(pattern.parse(candidate).getTime()); 

    } catch (ParseException pe) { 
     // Loop on 
    } 
} 
System.err.println("No known Date format found: " + candidate); 
return null; 
+0

Mã đơn giản và thanh lịch hoạt động! – xybrek

1

Nếu làm việc trong Java 1.8, bạn có thể tận dụng DateTimeFormatterBuilder

public static boolean isTimeStampValid(String inputString) 
{ 
    DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder() 
      .append(DateTimeFormatter.ofPattern("" + "[yyyy-MM-dd'T'HH:mm:ss.SSSZ]" + "[yyyy-MM-dd]")); 

    DateTimeFormatter dateTimeFormatter = dateTimeFormatterBuilder.toFormatter(); 

    try { 
     dateTimeFormatter.parse(inputString); 
     return true; 
    } catch (DateTimeParseException e) { 
     return false; 
    } 
} 

Xem bài: Java 8 Date equivalent to Joda's DateTimeFormatterBuilder with multiple parser formats?

1

Đối câu trả lời hiện đại Tôi bỏ qua yêu cầu sử dụng SimpleDateFormat. Trong khi sử dụng lớp này để phân tích cú pháp là một ý tưởng hay trong năm 2010 khi câu hỏi này được hỏi, hiện tại nó đã lỗi thời. Việc thay thế, DateTimeFormatter, ra mắt vào năm 2014. Ý tưởng sau đây là khá nhiều giống như trong câu trả lời được chấp nhận.

private static DateTimeFormatter[] parseFormatters = Stream.of("M/yy", "M/y", "M/d/y", "M-d-y") 
     .map(DateTimeFormatter::ofPattern) 
     .toArray(DateTimeFormatter[]::new); 

public static YearMonth parseYearMonth(String input) { 
    for (DateTimeFormatter formatter : parseFormatters) { 
     try { 
      return YearMonth.parse(input, formatter); 
     } catch (DateTimeParseException dtpe) { 
      // ignore, try next format 
     } 
    } 
    throw new IllegalArgumentException("Could not parse " + input); 
} 

Điều này phân tích từng chuỗi đầu vào từ câu hỏi vào tháng năm 2009-09. Điều quan trọng là hãy thử năm hai chữ số đầu tiên vì "M/y" cũng có thể phân tích cú pháp 9/09, nhưng thay vào đó, thành 0009-09.

Giới hạn của mã ở trên là bỏ qua ngày trong tháng từ các chuỗi có một, như 9/1/2009. Có thể không sao cho hầu hết các định dạng chỉ có tháng và năm. Để nhận nó, chúng tôi phải thử LocalDate.parse() thay vì YearMonth.parse() cho các định dạng bao gồm d trong chuỗi mẫu. Chắc chắn nó có thể được thực hiện.

0

Đây là ví dụ hoàn chỉnh (với phương thức chính) có thể được thêm làm lớp tiện ích trong dự án của bạn. Tất cả định dạng được đề cập trong SimpleDateFormate API được hỗ trợ theo phương pháp bên dưới.

import java.text.ParseException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 

import org.apache.commons.lang.time.DateUtils; 

public class DateUtility { 

    public static Date parseDate(String inputDate) { 

     Date outputDate = null; 
     String[] possibleDateFormats = 
       { 
        "yyyy.MM.dd G 'at' HH:mm:ss z", 
        "EEE, MMM d, ''yy", 
        "h:mm a", 
        "hh 'o''clock' a, zzzz", 
        "K:mm a, z", 
        "yyyyy.MMMMM.dd GGG hh:mm aaa", 
        "EEE, d MMM yyyy HH:mm:ss Z", 
        "yyMMddHHmmssZ", 
        "yyyy-MM-dd'T'HH:mm:ss.SSSZ", 
        "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", 
        "YYYY-'W'ww-u", 
        "EEE, dd MMM yyyy HH:mm:ss z", 
        "EEE, dd MMM yyyy HH:mm zzzz", 
        "yyyy-MM-dd'T'HH:mm:ssZ", 
        "yyyy-MM-dd'T'HH:mm:ss.SSSzzzz", 
        "yyyy-MM-dd'T'HH:mm:sszzzz", 
        "yyyy-MM-dd'T'HH:mm:ss z", 
        "yyyy-MM-dd'T'HH:mm:ssz", 
        "yyyy-MM-dd'T'HH:mm:ss", 
        "yyyy-MM-dd'T'HHmmss.SSSz", 
        "yyyy-MM-dd", 
        "yyyyMMdd", 
        "dd/MM/yy", 
        "dd/MM/yyyy" 
       }; 

     try { 

      outputDate = DateUtils.parseDate(inputDate, possibleDateFormats); 
      System.out.println("inputDate ==> " + inputDate + ", outputDate ==> " + outputDate); 

     } catch (ParseException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     return outputDate; 

    } 

    public static String formatDate(Date date, String requiredDateFormat) { 
     SimpleDateFormat df = new SimpleDateFormat(requiredDateFormat); 
     String outputDateFormatted = df.format(date); 
     return outputDateFormatted; 
    } 

    public static void main(String[] args) { 

     DateUtility.parseDate("20181118"); 
     DateUtility.parseDate("2018-11-18"); 
     DateUtility.parseDate("18/11/18"); 
     DateUtility.parseDate("18/11/2018"); 
     DateUtility.parseDate("2018.11.18 AD at 12:08:56 PDT"); 
     System.out.println(""); 
     DateUtility.parseDate("Wed, Nov 18, '18"); 
     DateUtility.parseDate("12:08 PM"); 
     DateUtility.parseDate("12 o'clock PM, Pacific Daylight Time"); 
     DateUtility.parseDate("0:08 PM, PDT"); 
     DateUtility.parseDate("02018.Nov.18 AD 12:08 PM"); 
     System.out.println(""); 
     DateUtility.parseDate("Wed, 18 Nov 2018 12:08:56 -0700"); 
     DateUtility.parseDate("181118120856-0700"); 
     DateUtility.parseDate("2018-11-18T12:08:56.235-0700"); 
     DateUtility.parseDate("2018-11-18T12:08:56.235-07:00"); 
     DateUtility.parseDate("2018-W27-3"); 
    } 

} 
Các vấn đề liên quan