2015-06-13 15 views
9

tôi cần phải phân tích số ngày (Đức) có sẵn trong các hình thức sau đây:Làm thế nào để phân tích tên tháng phi tiêu chuẩn với DateTimeFormatter

10. Jan. 18:14 
8. Feb. 19:02 
1. Mär. 19:40 
4. Apr. 18:55 
2. Mai 21:55 
5. Juni 08:25 
5. Juli 20:09 
1. Aug. 13:42 
[...] 

Như bạn có thể thấy, các tên tháng được cắt nếu tháng đó có nhiều hơn 4 ký tự. Thậm chí hơn nữa, đừng hỏi tôi tại sao, tháng 3 được rút ngắn thành Mär. mặc dù toàn bộ tên là März. Làm cách nào để phân tích cú pháp này với java.time? (Các ngày được định dạng dựa trên nội địa hóa các thiết bị Android mà tạo ra danh sách các ngày Tuy nhiên, tôi không phân tích nó trên Android.)

Tiếp cận của tôi là tạo ra một DateTimeFormatter như thế này:

DateTimeFormatter.ofPattern("d. MMMM HH:mm").withLocale(Locale.GERMAN); 
// or 
DateTimeFormatter.ofPattern("d. MMMMM HH:mm").withLocale(Locale.GERMAN); 

Nhưng không phải MMMM cũng không phải mẫu MMMMM phù hợp với những ngày được rút ngắn. Tôi có thể, tất nhiên, có các mẫu sau d. MMM. HH:mm để phù hợp với những tháng rút ngắn, nhưng sau đó tôi không thể phù hợp với 3 và 4 ký tự tháng. Tôi biết rằng tôi có thể có hai định dạng (MMM. and MMMMM) nhưng tôi muốn có một giải pháp mà tôi chỉ có một trình định dạng và có thể là một ngôn ngữ tùy chỉnh hoặc một cái gì đó như thế này.

+0

Nếu bạn có quyền kiểm soát ứng dụng Android đó, nó sẽ là tốt hơn rất nhiều để có nó gửi các ngày trong một định dạng tiêu chuẩn. Các định dạng được bản địa hóa chỉ nên được sử dụng cho tương tác của người dùng chứ không phải để trao đổi dữ liệu. Nếu không, tôi nghĩ bạn có thể chỉ cần loại bỏ các ký tự trước khi không gian trước khi phân tích cú pháp. – RealSkeptic

+0

Rất tiếc, tôi không có quyền kiểm soát ứng dụng Android, nếu không tôi sẽ truyền dữ liệu dưới dạng có cấu trúc và sử dụng dấu thời gian unix cho ngày :-). Chỉ cần loại bỏ các ký tự befor không gian, tức là các dấu chấm, không giúp đỡ bởi vì nó vẫn còn lại cho tôi trong một kết hợp giữa tên tháng rút gọn và tên đầy đủ. – rob

+0

Không, nếu bạn loại bỏ các ký tự trước khi không gian nó cũng rút ngắn Juli và Juni đến tháng Bảy và tháng Sáu – RealSkeptic

Trả lời

6

Câu trả lời cho vấn đề là lớp DateTimeFormatterBuilder và phương pháp appendText(TemporalField, Map).Nó cho phép bất kỳ văn bản được liên kết với một giá trị khi định dạng hoặc phân tích cú pháp, trong đó giải quyết vấn đề một cách hiệu quả và thanh lịch:

Map<Long, String> monthNameMap = new HashMap<>(); 
map.put(1L, "Jan."); 
map.put(2L, "Feb."); 
map.put(3L, "Mar."); 
DateTimeFormatter fmt = new DateTimeFormatterBuilder() 
    .appendPattern("d. ") 
    .appendText(ChronoField.MONTH_OF_YEAR, monthNameMap) 
    .appendPattern(" HH:mm") 
    .parseDefaulting(ChronoField.YEAR, 2016) 
    .toFormatter(); 

System.out.println(LocalDateTime.parse("10. Jan. 18:14", fmt)); 
System.out.println(LocalDateTime.parse("8. Feb. 19:02", fmt)); 

Một số lưu ý:

  • Các monthNameMap phải được phổ biến với tất cả 12 tháng
  • Trình định dạng thường được gán cho hằng số tĩnh cuối cùng, thay vì được tạo ra mọi lúc
  • parseDefaulting(YEAR, 2016) đã được thêm vào để LocalDateTime.parse(String, DateTimeFormatter) có thể được sử dụng trực tiếp. Nếu không có nó, sẽ không có năm, và do đó không có gì hơn một TemporalAccessor có thể được phân tích cú pháp (năm phải là một năm nhuận, trong trường hợp ngày 29 tháng 2 được phân tích cú pháp)
+0

Đây chính xác là những gì tôi đang tìm kiếm, cảm ơn bạn. Bằng cách này tôi có thể lưu trữ nội dung của bản đồ ở một nơi khác và có thể dễ dàng thêm các trường hợp mới lệch khỏi ngôn ngữ chuẩn. – rob

1

Bạn có thể regex thay thế các phần tháng vì vậy nó luôn luôn dài 3 ký tự trước khi phân tích nó bằng "d MMM HH:. Mm"

text = text.replaceFirst("(\\S+\\s\\S{3})\\S", "$1") 

Giải thích cho phần regex: Tìm 1 hoặc nhiều phi khoảng trắng (\ S +) theo sau là 1 khoảng trắng (\ s) theo sau là ba khoảng trắng (\ S {3}) theo sau là một khoảng trắng, và thay thế bằng phần bên trong khung đầu tiên ($ 1)

10. Jan. 18:14 sẽ trở thành 10. Jan 18:145. Juni 08:25 sẽ trở thành 5. Jun 08:25

2

Như đã chỉ ra, việc sử dụng định dạng chuẩn và nhất quán sẽ dễ dàng hơn - ở đây bạn đang trộn tên tháng dài và ngắn.

Một lựa chọn (viết tắt của việc sử dụng một DateTimeFormatterBuilder) là để xử lý cả hai trường hợp riêng biệt:

private static final DateTimeFormatter SHORT_MONTH = DateTimeFormatter.ofPattern("d. MMM. HH:ss", Locale.GERMAN); 
private static final DateTimeFormatter LONG_MONTH = DateTimeFormatter.ofPattern("d. MMMM HH:ss", Locale.GERMAN); 
private static TemporalAccessor parse(String s) { 
    try { 
    return SHORT_MONTH.parse(s); 
    } catch (DateTimeParseException e) { 
    return LONG_MONTH.parse(s); 
    } 
} 
+0

Tôi biết về giải pháp này như tôi đã viết trong câu hỏi. Tôi đang nhìn vào 'DateTimeFormatterBuilder' ngay bây giờ. Làm thế nào bạn sẽ sử dụng nó để đạt được điều này? – rob

+0

@rob Tôi chưa từng sử dụng trước khi thành thật ... – assylias

+0

Ok, tôi sẽ nghiên cứu tài liệu chi tiết hơn :) – rob

6

Bạn có thể sử dụng một DateTimeFormatterBuilder:

private static final DateTimeFormatter formatter = new DateTimeFormatterBuilder() 
      .appendOptional(DateTimeFormatter.ofPattern("d. MMM. HH:ss")) 
      .appendOptional(DateTimeFormatter.ofPattern("d. MMMM HH:ss")) 
      .toFormatter(Locale.GERMAN); 

Chạy nó về điều này:

Stream.of(("10. Jan. 18:14\n" + 
      "8. Feb. 19:02\n" + 
      "1. Mär. 19:40\n" + 
      "4. Apr. 18:55\n" + 
      "2. Mai 21:55\n" + 
      "5. Juni 08:25\n" + 
      "5. Juli 20:09\n" + 
      "1. Aug. 13:42").split("\n")) 
     .map(formatter::parse) 
     .forEach(System.out::println); 

bạn nhận được:

{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=10, MonthOfYear=1, MilliOfSecond=0, SecondOfMinute=14, HourOfDay=18},ISO 
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=8, MonthOfYear=2, MilliOfSecond=0, SecondOfMinute=2, HourOfDay=19},ISO 
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=1, MonthOfYear=3, MilliOfSecond=0, SecondOfMinute=40, HourOfDay=19},ISO 
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=4, MonthOfYear=4, MilliOfSecond=0, SecondOfMinute=55, HourOfDay=18},ISO 
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=2, MonthOfYear=5, MilliOfSecond=0, SecondOfMinute=55, HourOfDay=21},ISO 
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=5, MonthOfYear=6, MilliOfSecond=0, SecondOfMinute=25, HourOfDay=8},ISO 
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=5, MonthOfYear=7, MilliOfSecond=0, SecondOfMinute=9, HourOfDay=20},ISO 
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=1, MonthOfYear=8, MilliOfSecond=0, SecondOfMinute=42, HourOfDay=13},ISO 
+0

Thanh lịch. Tôi tin rằng 'DateTimeFormatter.ofPattern (" d. [MMM.] [MMMM] HH: ss ", Locale.GERMAN)' sẽ làm. Các dấu ngoặc vuông biểu thị các phần tùy chọn. –

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