package net.time4j.format.expert; import net.time4j.DayPeriod; import net.time4j.PlainTime; import net.time4j.PlainTimestamp; import net.time4j.format.Attributes; import net.time4j.format.CalendarText; import net.time4j.format.Leniency; import net.time4j.format.OutputContext; import net.time4j.format.TextWidth; import net.time4j.i18n.IsoTextProviderSPI; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.ParseException; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; import java.util.Set; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; @RunWith(JUnit4.class) public class DayPeriodTest { @Test public void displayMidnight() { assertThat( PlainTime.midnightAtEndOfDay().get( DayPeriod.of(Locale.ENGLISH).fixed()), is("midnight")); assertThat( PlainTime.midnightAtStartOfDay().get( DayPeriod.of(Locale.GERMAN).approximate()), is("Mitternacht")); } @Test public void displayMorning() { assertThat( PlainTime.of(11, 59).get( DayPeriod.of(Locale.ENGLISH).fixed()), is("am")); assertThat( PlainTime.of(11, 59).get( DayPeriod.of(Locale.ENGLISH).approximate()), is("in the morning")); } @Test public void displayNoon() { assertThat( PlainTime.of(12).get( DayPeriod.of(Locale.ENGLISH).fixed()), is("noon")); assertThat( PlainTime.of(12).get( DayPeriod.of(Locale.ENGLISH).approximate()), is("noon")); } @Test public void displayAfternoon() { assertThat( PlainTime.of(17, 59).get( DayPeriod.of(Locale.ENGLISH).fixed()), is("pm")); assertThat( PlainTime.of(17, 59).get( DayPeriod.of(Locale.GERMAN).fixed(TextWidth.ABBREVIATED, OutputContext.FORMAT)), is("nachm.")); assertThat( PlainTime.of(17, 59).get( DayPeriod.of(Locale.ENGLISH).approximate()), is("in the afternoon")); assertThat( PlainTime.of(17, 59).get( DayPeriod.of(Locale.GERMAN).approximate()), is("nachmittags")); } @Test public void displayEvening() { assertThat( PlainTime.of(20, 45).get( DayPeriod.of(Locale.ENGLISH).approximate(TextWidth.WIDE, OutputContext.STANDALONE) ), is("evening")); } @Test public void displayNight() { assertThat( PlainTime.of(5, 59).get( DayPeriod.of(Locale.ENGLISH).approximate(TextWidth.WIDE, OutputContext.FORMAT) ), is("at night")); } @Test public void startEndForMidnight() { assertThat( DayPeriod.of(Locale.US).getStart(PlainTime.of(0)), is(PlainTime.of(21))); assertThat( DayPeriod.of(Locale.US).getEnd(PlainTime.of(0)), is(PlainTime.of(6))); } @Test public void startEndForNight1() { assertThat( DayPeriod.of(Locale.US).getStart(PlainTime.of(4)), is(PlainTime.of(21))); assertThat( DayPeriod.of(Locale.US).getEnd(PlainTime.of(4)), is(PlainTime.of(6))); } @Test public void startEndForMorning() { assertThat( DayPeriod.of(Locale.US).getStart(PlainTime.of(9)), is(PlainTime.of(6))); assertThat( DayPeriod.of(Locale.US).getEnd(PlainTime.of(9)), is(PlainTime.of(12))); } @Test public void startEndForNoon() { assertThat( DayPeriod.of(Locale.US).getStart(PlainTime.of(12)), is(PlainTime.of(12))); assertThat( DayPeriod.of(Locale.US).getEnd(PlainTime.of(12)), is(PlainTime.of(18))); } @Test public void startEndForAfternoon() { assertThat( DayPeriod.of(Locale.US).getStart(PlainTime.of(15)), is(PlainTime.of(12))); assertThat( DayPeriod.of(Locale.US).getEnd(PlainTime.of(15)), is(PlainTime.of(18))); } @Test public void startEndForEvening() { assertThat( DayPeriod.of(Locale.US).getStart(PlainTime.of(20)), is(PlainTime.of(18))); assertThat( DayPeriod.of(Locale.US).getEnd(PlainTime.of(20)), is(PlainTime.of(21))); } @Test public void startEndForNight2() { assertThat( DayPeriod.of(Locale.US).getStart(PlainTime.of(23)), is(PlainTime.of(21))); assertThat( DayPeriod.of(Locale.US).getEnd(PlainTime.of(23)), is(PlainTime.of(6))); } @Test public void fallback() { assertThat( PlainTime.of(5).get(DayPeriod.of(new Locale("xyz")).approximate()), is("AM")); assertThat( PlainTime.of(12).get(DayPeriod.of(new Locale("xyz")).approximate()), is("PM")); } @Test public void colombiaMorning() { DayPeriod dp = DayPeriod.of(new Locale("es", "CO")); assertThat( dp.getStart(PlainTime.of(3)), is(PlainTime.midnightAtStartOfDay())); assertThat( dp.getStart(PlainTime.of(7)), is(PlainTime.midnightAtStartOfDay())); } @Test public void formatFixedEnglish() throws ParseException { ChronoFormatter<PlainTime> f = ChronoFormatter.ofTimePattern("h:mm b", PatternType.CLDR, Locale.ENGLISH); assertThat( f.format(PlainTime.of(3, 45)), is("3:45 am")); assertThat( f.parse("3:45 am"), is(PlainTime.of(3, 45))); assertThat( f.format(PlainTime.of(23, 45)), is("11:45 pm")); assertThat( f.parse("11:45 pm"), is(PlainTime.of(23, 45))); assertThat( f.format(PlainTime.of(0)), is("12:00 midnight")); assertThat( f.parse("12:00 midnight"), is(PlainTime.of(0))); assertThat( f.format(PlainTime.of(12)), is("12:00 noon")); assertThat( f.parse("12:00 noon"), is(PlainTime.of(12))); assertThat( f.format(PlainTime.of(17, 15)), is("5:15 pm")); assertThat( f.parse("5:15 pm"), is(PlainTime.of(17, 15))); } @Test public void formatFlexibleEnglishWide() throws ParseException { ChronoFormatter<PlainTime> f = ChronoFormatter.ofTimePattern("h:mm BBBB", PatternType.CLDR, Locale.ENGLISH); assertThat( f.format(PlainTime.of(3, 45)), is("3:45 at night")); assertThat( f.parse("3:45 at night"), is(PlainTime.of(3, 45))); assertThat( f.format(PlainTime.of(23, 45)), is("11:45 at night")); assertThat( f.parse("11:45 at night"), is(PlainTime.of(23, 45))); assertThat( f.format(PlainTime.of(0)), is("12:00 midnight")); assertThat( f.parse("12:00 midnight"), is(PlainTime.of(0))); assertThat( f.parse("12:00 at night"), is(PlainTime.of(0))); assertThat( f.format(PlainTime.of(12)), is("12:00 noon")); assertThat( f.parse("12:00 noon"), is(PlainTime.of(12))); assertThat( f.parse("12:00 in the afternoon"), is(PlainTime.of(12))); assertThat( f.format(PlainTime.of(17, 15)), is("5:15 in the afternoon")); assertThat( f.parse("5:15 in the afternoon"), is(PlainTime.of(17, 15))); } @Test public void consistencyAtMidnight1() throws ParseException { ChronoFormatter<PlainTime> f = ChronoFormatter.ofTimePattern("h:mm BBBB", PatternType.CLDR, Locale.ENGLISH).with(Leniency.STRICT); assertThat( f.parse("12:00 midnight"), is(PlainTime.of(0))); } @Test(expected=ParseException.class) public void consistencyAtMidnight2() throws ParseException { ChronoFormatter<PlainTime> f = ChronoFormatter.ofTimePattern("h:mm BBBB", PatternType.CLDR, Locale.ENGLISH).with(Leniency.STRICT); assertThat( f.parse("12:00 at night"), is(PlainTime.of(0))); } @Test public void formatFlexibleEnglishNarrow() throws ParseException { ChronoFormatter<PlainTime> f = ChronoFormatter.ofTimePattern("h:mm BBBBB", PatternType.CLDR, Locale.ENGLISH); assertThat( f.format(PlainTime.of(3, 45)), is("3:45 at night")); assertThat( f.parse("3:45 at night"), is(PlainTime.of(3, 45))); assertThat( f.format(PlainTime.of(23, 45)), is("11:45 at night")); assertThat( f.parse("11:45 at night"), is(PlainTime.of(23, 45))); assertThat( f.format(PlainTime.of(0)), is("12:00 mi")); assertThat( f.parse("12:00 at night"), is(PlainTime.of(0))); assertThat( f.format(PlainTime.of(12)), is("12:00 n")); assertThat( f.parse("12:00 n"), is(PlainTime.of(12))); assertThat( f.parse("12:00 in the afternoon"), is(PlainTime.of(12))); assertThat( f.format(PlainTime.of(17, 15)), is("5:15 in the afternoon")); assertThat( f.parse("5:15 in the afternoon"), is(PlainTime.of(17, 15))); } @Test public void formatFlexibleGerman0345() throws ParseException { ChronoFormatter<PlainTimestamp> f = ChronoFormatter.ofTimestampPattern("d. MMMM uuuu h:mm BBBB", PatternType.CLDR, Locale.GERMAN); assertThat( f.format(PlainTimestamp.of(2015, 12, 10, 3, 45)), is("10. Dezember 2015 3:45 nachts")); assertThat( f.parse("10. Dezember 2015 3:45 nachts"), is(PlainTimestamp.of(2015, 12, 10, 3, 45))); assertThat( f.format(PlainTimestamp.of(2015, 12, 10, 15, 45)), is("10. Dezember 2015 3:45 nachmittags")); assertThat( f.parse("10. Dezember 2015 3:45 nachmittags"), is(PlainTimestamp.of(2015, 12, 10, 15, 45))); } @Test public void formatFlexibleGerman0900() throws ParseException { ChronoFormatter<PlainTimestamp> f = ChronoFormatter.ofTimestampPattern("d. MMMM uuuu h:mm BBBB", PatternType.CLDR, Locale.GERMAN); assertThat( f.format(PlainTimestamp.of(2015, 12, 10, 9, 0)), is("10. Dezember 2015 9:00 morgens")); assertThat( f.parse("10. Dezember 2015 9:00 morgens"), is(PlainTimestamp.of(2015, 12, 10, 9, 0))); assertThat( f.format(PlainTimestamp.of(2015, 12, 10, 21, 0)), is("10. Dezember 2015 9:00 abends")); assertThat( f.parse("10. Dezember 2015 9:00 abends"), is(PlainTimestamp.of(2015, 12, 10, 21, 0))); } @Test public void formatFlexibleIndonesian() throws ParseException { // test for ambivalent code "afternoon1" ChronoFormatter<PlainTime> f = ChronoFormatter.ofTimePattern("h:mm BBBB", PatternType.CLDR, new Locale("id")); // or "in" assertThat( f.format(PlainTime.of(13, 45)), is("1:45 siang")); assertThat( f.parse("1:45 siang"), is(PlainTime.of(13, 45))); assertThat( f.format(PlainTime.of(11, 15)), is("11:15 siang")); assertThat( f.parse("11:15 siang"), is(PlainTime.of(11, 15))); } @Test public void formatCustom() throws ParseException { Map<PlainTime, String> timeToLabels = new HashMap<>(); timeToLabels.put(PlainTime.of(23), "night"); timeToLabels.put(PlainTime.of(7), "morning"); timeToLabels.put(PlainTime.of(12), "afternoon"); timeToLabels.put(PlainTime.of(18, 30), "evening"); ChronoFormatter<PlainTime> f = ChronoFormatter.setUp(PlainTime.axis(), Locale.ENGLISH) .addPattern("h:mm ", PatternType.CLDR) .addDayPeriod(timeToLabels) .build() .with(Leniency.STRICT); assertThat( f.format(PlainTime.of(11, 59)), is("11:59 morning")); assertThat( f.parse("11:59 morning"), is(PlainTime.of(11, 59))); assertThat( f.format(PlainTime.of(12)), is("12:00 afternoon")); assertThat( f.parse("12:00 afternoon"), is(PlainTime.of(12))); assertThat( f.format(PlainTime.of(18, 29)), is("6:29 afternoon")); assertThat( f.parse("6:29 afternoon"), is(PlainTime.of(18, 29))); assertThat( f.format(PlainTime.of(18, 30)), is("6:30 evening")); assertThat( f.parse("6:30 evening"), is(PlainTime.of(18, 30))); } @Test(expected=ParseException.class) public void parseCustomWithInconsistency() throws ParseException { Map<PlainTime, String> timeToLabels = new HashMap<>(); timeToLabels.put(PlainTime.of(23), "night"); timeToLabels.put(PlainTime.of(7), "morning"); timeToLabels.put(PlainTime.of(12), "afternoon"); timeToLabels.put(PlainTime.of(18, 30), "evening"); ChronoFormatter<PlainTime> f = ChronoFormatter.setUp(PlainTime.axis(), Locale.ENGLISH) .addPattern("h:mm ", PatternType.CLDR) .addDayPeriod(timeToLabels) .build() .with(Leniency.STRICT); f.parse("12:00 morning"); } @Test public void parseFlexibleZulu() throws ParseException { ChronoFormatter<PlainTime> f = ChronoFormatter .ofTimePattern("h:mm BBBB", PatternType.CLDR, new Locale("zu")) .with(Leniency.STRICT); assertThat( f.with(Attributes.OUTPUT_CONTEXT, OutputContext.STANDALONE).parse("5:45 entathakusa"), is(PlainTime.of(5, 45))); assertThat( f.parse("5:45 entathakusa"), is(PlainTime.of(5, 45))); assertThat( f.parse("9:45 ekuseni"), is(PlainTime.of(9, 45))); assertThat( f.parse("10:45 emini"), is(PlainTime.of(10, 45))); assertThat( f.parse("12:45 emini"), is(PlainTime.of(12, 45))); assertThat( f.parse("6:45 ntambama"), is(PlainTime.of(18, 45))); assertThat( f.parse("7:45 ebusuku"), is(PlainTime.of(19, 45))); } @Test public void parseFlexibleFarsi() throws ParseException { // test for ambivalent dayperiods ChronoFormatter<PlainTime> f = ChronoFormatter .ofTimePattern("h:mm BBBB", PatternType.CLDR, new Locale("fa")) .with(Leniency.STRICT) .with(Attributes.ZERO_DIGIT, '0'); assertThat( f.parse("3:45 عصر"), // afternoon1 is(PlainTime.of(15, 45))); assertThat( f.parse("6:45 عصر"), // evening1 is(PlainTime.of(18, 45))); } @Test public void checkSanity() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Method mKey = DayPeriod.class.getDeclaredMethod( "createKey", Map.class, TextWidth.class, OutputContext.class, String.class); mKey.setAccessible(true); Field field = DayPeriod.class.getDeclaredField("codeMap"); field.setAccessible(true); IsoTextProviderSPI spi = new IsoTextProviderSPI(); for (Locale locale : spi.getAvailableLocales()) { Map<String, String> textForms = CalendarText.getIsoInstance(locale).getTextForms(); DayPeriod dp = DayPeriod.of(locale); Map<?, ?> m = Map.class.cast(field.get(dp)); Set<String> codes = new LinkedHashSet<>(); for (Object code : m.values()) { codes.add(code.toString()); } for (TextWidth tw : TextWidth.values()) { if (tw == TextWidth.SHORT) { continue; } for (OutputContext oc : OutputContext.values()) { Set<String> translations = new HashSet<>(); for (String code : codes) { String key = mKey.invoke(null, textForms, tw, oc, code).toString(); String text = textForms.get(key); if (text == null) { fail("Fallback problem detected: " + locale + "/" + tw + "/" + oc + "/" + code); } else if (!translations.add(text) && (tw != TextWidth.NARROW) && isCheckWanted(locale)) { fail("Ambivalent text forms detected: " + locale + "/" + tw + "/" + oc + "/" + code); } } } } } } private static boolean isCheckWanted(Locale locale) { String lang = locale.getLanguage(); // require manual check return !(lang.equals("fa") || lang.equals("gl") || lang.equals("hu") || lang.equals("zu")); } }