2017-09-21 15 views
10

Joda-Time에서 Java 8 java.time으로 기존 응용 프로그램을 이식하고 있습니다.DateTimeFormatter 평일은 하나 씩 밖에 보이지 않습니다.

'주중 값'값이 포함 된 날짜/시간 문자열을 구문 분석 할 때 단위 테스트에서 예외가 발생하는 문제가 발생했습니다.

하는 구문 분석 할 때 :

2016년 12월 21일 20시 50분 25초 수요일에게 12월 0000 3

을 형식 사용 :

yyyy'-'MM'-'dd' 'HH':'mm':'ss' 'EEEE' 'MMMM' 'ZZ' 'e 

를 내가 얻을 :

java.time.format.DateTimeParseException: 
Text '2016-12-21 20:50:25 Wednesday December +0000 3' 
could not be parsed: Conflict found: 
Field DayOfWeek 3 differs from DayOfWeek 2 derived from 2016-12-21 

lett 오링 DateTimeFormatter은 무엇을 기대 나타냅니다

String logline  = "2016-12-21 20:50:25 Wednesday December +0000"; 
String format  = "yyyy'-'MM'-'dd' 'HH':'mm':'ss' 'EEEE' 'MMMM' 'ZZ"; 
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format).withLocale(Locale.ENGLISH);; 
ZonedDateTime dateTime = formatter.parse(logline, ZonedDateTime::from); 

format  = "yyyy'-'MM'-'dd' 'HH':'mm':'ss' 'EEEE' 'MMMM' 'ZZ' 'e"; 
formatter = DateTimeFormatter.ofPattern(format).withLocale(Locale.ENGLISH); 
System.out.println(formatter.format(dateTime)); 

내가 지금이 출력을 얻을 :

2016-12-21 20:50:25 Wednesday December +0000 4 

그래서 효과적으로 문제의 근본 원인은 Joda 타임의 e 플래그가 1 일 월요일을 고려한다는 것입니다 Java 8 java.time은 월요일을 0으로 간주합니다. java.time.DateTimeFormatter 내가 모두 the Oracle documentation와 JSR-310이에서 찾을 지원하는 패턴 이제

:

e/c  localized day-of-week  number/text  2; 02; Tue; Tuesday; T 

이 명시 적 2의 예와 '화요일'믿고 날 리드 수요일해야 또한 자바있다. 시간은 4 대신 3이됩니다.

여기에 어떤 문제가 있습니까? 내가 오해하니? Java 8의 버그입니까?

+7

무엇 시간대/locale 의사가 현지화 된 요일이라고 말했듯이 월요일에 반해 일요일의 주를 시작하는 로케일 – hardillb

+0

명확히하기 위해 .withLocale (Locale.ENGLISH); 코드에서. –

답변

6

Joda-시간과 java.time 패턴 e을 해석하는 방법에 차이가있다와 요일을 확인할 수 있습니다.

Joda 타임

e 패턴 designates the numeric value of day-of-week : 그래서

Symbol Meaning  Presentation Examples 
------ ----------- ------------ ------- 
e  day of week number  2 

, e가 날짜 객체에서 요일을 얻기에 해당 사용 :

// using org.joda.time.DateTime and org.joda.time.format.DateTimeFormat 
DateTime d = new DateTime(2016, 12, 21, 20, 50, 25, 0, DateTimeZone.UTC); 
DateTimeFormatter fmt = DateTimeFormat.forPattern("e").withLocale(Locale.ENGLISH); 
System.out.println(d.toString(fmt)); // 3 
System.out.println(d.getDayOfWeek()); // 3 
System.out.println(d.dayOfWeek().getAsText(Locale.ENGLISH)); // Wednesday 

주 포맷터와 getDayOfWeek()이 모두 3을 반환합니다. getDayOfWeek() methodDateTimeConstants classWednesday's value is 3 (the third day of the weekISO's definition에 따라 정의 된 값)을 반환합니다.java.time API에서


패턴 ehas a different meaning :

Pattern Count Equivalent builder methods 
------- ----- -------------------------- 
e  1  append special localized WeekFields element for numeric day-of-week 

그것은 국부적 WeekFields 소자를 사용하고, 이것은 지역에 따라 달라질 수있다. 포맷터가 영어 로케일에 대한주의 지역화 일을 사용

ZonedDateTime z = ZonedDateTime.of(2016, 12, 21, 20, 50, 25, 0, ZoneOffset.UTC); 
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("e", Locale.ENGLISH); 
System.out.println(z.format(fmt)); // 4 
System.out.println(z.getDayOfWeek()); // WEDNESDAY 
System.out.println(z.getDayOfWeek().getValue()); // 3 

하는 것으로하고, getDayOfWeek().getValue() 반환 3를 호출하는 동안 값이 4 경우 : getDayOfWeek() 방법에 비해 동작은 다를 수 있습니다. 영어 로케일 e가 사용하는 것과이기 때문이다

java.time.temporal.WeekFields : ISO의 정의는 첫날 월요일을 사용하기 때문이다

// same as getDayOfWeek() 
System.out.println(z.get(WeekFields.ISO.dayOfWeek())); // 3 

: ISO의 정의를 사용하는 것과 같습니다

// using localized fields 
WeekFields wf = WeekFields.of(Locale.ENGLISH); 
System.out.println(z.get(wf.dayOfWeek())); // 4 

getDayOfWeek() 동안 일주일 중 WeekFields 영어 로케일 사용 일 :

// comparing the first day of week 
System.out.println(WeekFields.ISO.getFirstDayOfWeek()); // MONDAY 
System.out.println(wf.getFirstDayOfWeek()); // SUNDAY 

따라서 패턴이 설정된 로케일 (또는 설정되지 않은 경우 JVM 기본 로케일)에 따라 e 패턴이 getDayOfWeek()과 다르게 작동하거나 작동하지 않을 수 있습니다.

WeekFields.of(Locale.FRENCH).getFirstDayOfWeek(); // MONDAY 
WeekFields.of(new Locale("ar", "AE")).getFirstDayOfWeek(); // SATURDAY 

따르면 javadoc에, 숫자 값을 반환하는 유일한 패턴 : 일부 아랍어 로케일에서,주의 최초의 날이 토요일 동안 프랑스어 지역에서, 예를 들어, 그것은 단지 ISO처럼 작동 그 날의 날은 현지화 된 것 같습니다.

String input = "2016-12-21 20:50:25 Wednesday December +0000 3"; 
DateTimeFormatter parser = new DateTimeFormatterBuilder() 
    // date/time pattern 
    .appendPattern("yyyy-MM-dd HH:mm:ss EEEE MMMM ZZ ") 
    // numeric day of week 
    .appendValue(ChronoField.DAY_OF_WEEK) 
    // create formatter with English locale 
    .toFormatter(Locale.ENGLISH); 

ZonedDateTime date = ZonedDateTime.parse(input, parser); 

: 그래서, 입력 2016-12-21 20:50:25 Wednesday December +0000 3을 구문 분석, 당신은 요일합니다 (ISO 이외의 로케일에 민감한 분야)의 숫자 값을 나타 내기 위해 java.time.format.DateTimeFormatterBuilder을 사용하고 java.time.temporal.ChronoField와 날짜/시간 패턴에 가입 할 수 있습니다 또한 -, : 및 공백 문자를 인용 할 필요가 없으므로 패턴이보다 명확하고 읽기 쉽도록됩니다 (IMO).

영어 로케일을 설정하지 않으면 JVM 기본 로케일이 사용되므로 항상 영어로 설정되지 않을 수 있습니다. 또한 런타임시에도 예고없이 변경 될 수 있으므로 특히 입력이 어떤 언어인지 이미 알고있는 경우이를 지정하는 것이 좋습니다.


업데이트 : 그것은 (JDK 1.8.0_144) appendText(ChronoField.DAY_OF_WEEK, TextStyle.NARROW_STANDALONE)에 내 테스트에 해당하는 것에 따라 아마도 ccccc 패턴이 반환 (그리고 구문 분석) 작업을해야 3 :

DateTimeFormatter parser = DateTimeFormatter 
    .ofPattern("yyyy-MM-dd HH:mm:ss EEEE MMMM ZZ ccccc", Locale.ENGLISH); 
ZonedDateTime date = ZonedDateTime.parse(input, parser); 
+1

감사합니다. 이 설명은 실제로 도움이됩니다. 표현식이 strftime 형식의 입력 문자열에서 생성되기 때문에 이런 모든 필드를 인용합니다. 나는이 모든 가치를 추가하여 모든 것이 올바르게 작동하도록하는 방법을 찾아야한다. https://github.com/nielsbasjes/logparser/blob/master/httpdlog/httpdlog-parser/src/main/java/nl/basjes/parse/httpdlog/dissectors/StrfTimeStampDissector.java#L200 –

+2

@NielsBasjes를 참조하십시오. 환영합니다. 기꺼이 도와주세요! 나는 몇몇 테스트를했고 아마도'ccccc' 패턴이 여러분이 필요로하는 것이어야합니다. 답변을 업데이트했습니다. –

1

Locale.ENGLISH 수요일은 일요일부터 시작하여 주 4 일입니다. 당신은

WeekFields.of(Locale.ENGLISH).getFirstDayOfWeek(); //it's SUNDAY