Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLDR-7401 Add examples for quarters and eras #3917

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.ibm.icu.text.UTF16;
import com.ibm.icu.text.UnicodeSet;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.Output;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.ULocale;
Expand Down Expand Up @@ -176,7 +177,6 @@ public class ExampleGenerator {
DATE_SAMPLE = calendar.getTime();
calendar.set(1999, 9, 27, 13, 25, 59); // 1999-10-27 13:25:59
DATE_SAMPLE2 = calendar.getTime();

calendar.set(1999, 8, 5, 7, 0, 0); // 1999-09-05 07:00:00
DATE_SAMPLE3 = calendar.getTime();
}
Expand Down Expand Up @@ -271,6 +271,54 @@ public void setCachingEnabled(boolean enabled) {

HelpMessages helpMessages;

/* For each calendar type, maps the closest two eras to 2025
* defined in that calendar to their corresponding start/end date.
* Dates are adjusted to be 2 days after official era start date and
* 2 days before era end date to avoid time zone issues.
* TODO: include methods for calendarData in supplementalDataInfo API
* to extract this data directly from supplementaldata.xml
*/
public static final Map<String, List<Date>> CALENDAR_ERAS =
new HashMap<String, List<Date>>() {
{ // month 0-indexed. start/end days adjusted by +/- 2, respectively
put(
"gregorian",
List.of(
new GregorianCalendar(0, 11, 29).getTime(),
new GregorianCalendar(1, 0, 03).getTime()));
put(
"japanese",
List.of(
new GregorianCalendar(1989, 0, 10).getTime(),
new GregorianCalendar(2019, 4, 3).getTime()));
put("islamic", List.of(new GregorianCalendar(622, 6, 17).getTime()));
put("chinese", List.of(new GregorianCalendar(-2636, 0, 03).getTime()));
put("hebrew", List.of(new GregorianCalendar(-3760, 9, 9).getTime()));
put("buddhist", List.of(new GregorianCalendar(-542, 0, 03).getTime()));
put(
"coptic",
List.of(
new GregorianCalendar(284, 07, 26).getTime(),
new GregorianCalendar(284, 07, 31).getTime()));
put("persian", List.of(new GregorianCalendar(622, 0, 03).getTime()));
put("dangi", List.of(new GregorianCalendar(-2332, 0, 03).getTime()));
put(
"ethiopic",
List.of(
new GregorianCalendar(8, 07, 26).getTime(),
new GregorianCalendar(8, 07, 31).getTime()));
put(
"ethiopic-amete-alem",
List.of(new GregorianCalendar(-5492, 07, 27).getTime()));
put("indian", List.of(new GregorianCalendar(79, 0, 03).getTime()));
put(
"roc",
List.of(
new GregorianCalendar(1911, 11, 29).getTime(),
new GregorianCalendar(1912, 0, 03).getTime()));
}
};

public CLDRFile getCldrFile() {
return cldrFile;
}
Expand Down Expand Up @@ -465,6 +513,10 @@ private String constructExampleHtml(String xpath, String value, boolean nonTrivi
result = handleDisplayNames(xpath, parts, value);
} else if (parts.contains("currency")) {
result = handleCurrency(xpath, parts, value);
} else if (parts.contains("eras")) {
result = handleEras(parts, value);
} else if (parts.contains("quarters")) {
result = handleQuarters(parts, value);
} else if (parts.contains("dayPeriods")) {
result = handleDayPeriod(parts, value);
} else if (parts.contains("monthContext")) {
Expand Down Expand Up @@ -2876,6 +2928,86 @@ private String handleDateRangePattern(String value) {
return result;
}

/**
* Add examples for eras. First checks if there is info for this calendar type and this era type
* in the CALENDAR_ERAS map, then generates a sample date based on this info and formats it
*/
private String handleEras(XPathParts parts, String value) {
String calendarId = parts.getAttributeValue(3, "type");
String type = parts.getAttributeValue(-1, "type");
String id =
(calendarId.startsWith("islamic"))
? "islamic"
: calendarId; // islamic variations map to same sample
if (!CALENDAR_ERAS.containsKey(id)) {
return null;
}
int typeIndex = Integer.parseInt(type);
if (calendarId.equals("japanese")) {
if (typeIndex < 235) { // examples only for 2 most recent eras
return null;
} else {
typeIndex %= 235; // map to length 2 list
}
}
List<Date> eraDates = CALENDAR_ERAS.get(id);
Date sample = eraDates.get(typeIndex);
String skeleton = "Gy";
String checkPath =
"//ldml/dates/calendars/calendar[@type=\""
+ calendarId
+ "\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\""
+ skeleton
+ "\"]";
String dateFormat = cldrFile.getWinningValue(checkPath);
macchiati marked this conversation as resolved.
Show resolved Hide resolved
SimpleDateFormat sdf = icuServiceBuilder.getDateFormat(calendarId, dateFormat);
DateFormatSymbols dfs = sdf.getDateFormatSymbols();
String[] eraNames = dfs.getEras();
eraNames[typeIndex] = value; // overwrite era to current value
dfs.setEras(eraNames);
sdf.setDateFormatSymbols(dfs);
return sdf.format(sample);
}

/**
* Add examples for quarters for the gregorian calendar, matching each quarter type (1, 2, 3, 4)
* to a corresponding sample month and formatting an example with that date
*/
private String handleQuarters(XPathParts parts, String value) {
String calendarId = parts.getAttributeValue(3, "type");
if (!calendarId.equals("gregorian")) {
return null;
}
String width = parts.findAttributeValue("quarterWidth", "type");
if (width.equals("narrow")) {
return null;
}
String context = parts.findAttributeValue("quarterContext", "type");
String type = parts.getAttributeValue(-1, "type"); // 1-indexed
int quarterIndex = Integer.parseInt(type) - 1;
String skeleton = (width.equals("wide")) ? "yQQQQ" : "yQQQ";
String checkPath =
"//ldml/dates/calendars/calendar[@type=\""
+ calendarId
+ "\"]/dateTimeFormats/availableFormats/dateFormatItem[@id=\""
+ skeleton
+ "\"]";
String dateFormat = cldrFile.getWinningValue(checkPath);
macchiati marked this conversation as resolved.
Show resolved Hide resolved
SimpleDateFormat sdf = icuServiceBuilder.getDateFormat(calendarId, dateFormat);
DateFormatSymbols dfs = sdf.getDateFormatSymbols();
int widthVal = width.equals("abbreviated") ? 0 : 1;
String[] quarterNames = dfs.getQuarters(0, widthVal); // 0 for formatting
quarterNames[quarterIndex] = value;
dfs.setQuarters(quarterNames, 0, widthVal);
sdf.setDateFormatSymbols(dfs);
sdf.setTimeZone(ZONE_SAMPLE);
final int[] monthSamples = new int[] {1, 4, 7, 10}; // {feb, may, oct, nov}
int month = monthSamples[quarterIndex];
calendar.set(1999, month, 5, 13, 25, 59);
Date sample = calendar.getTime();
return sdf.format(sample);
}

/**
* @param elementToOverride the element that is to be overridden
* @param element the overriding element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.impl.Relation;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -154,7 +156,7 @@ public void testCurrency() {
"//ldml/characters/parseLenients.*",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/months/monthContext[@type=\"([^\"]*+)\"]/monthWidth[@type=\"([^\"]*+)\"]/month[@type=\"([^\"]*+)\"]",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/days/dayContext[@type=\"([^\"]*+)\"]/dayWidth[@type=\"([^\"]*+)\"]/day[@type=\"([^\"]*+)\"]",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/quarters/quarterContext[@type=\"([^\"]*+)\"]/quarterWidth[@type=\"([^\"]*+)\"]/quarter[@type=\"([^\"]*+)\"]",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/quarters/quarterContext[@type=\"([^\"]*+)\"]/quarterWidth[@type=\"([^\"]*+)\"]/quarter[@type=\"([^\"]*+)\"]", // examples only for gregorian
"//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/displayName",
"//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/relative[@type=\"([^\"]*+)\"]",
"//ldml/dates/fields/field[@type=\"([^\"]*+)\"]/relativeTime[@type=\"([^\"]*+)\"]/relativeTimePattern[@count=\"([^\"]*+)\"]",
Expand Down Expand Up @@ -201,7 +203,7 @@ public void testCurrency() {
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/appendItems/appendItem[@request=\"([^\"]*+)\"]",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/intervalFormats/intervalFormatFallback",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/dateTimeFormats/intervalFormats/intervalFormatItem[@id=\"([^\"]*+)\"]/greatestDifference[@id=\"([^\"]*+)\"]",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNames/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNames/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]", // examples only for two closest eras to 2025
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraAbbr/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/eras/eraNarrow/era[@type=\"([^\"]*+)\"][@alt=\"([^\"]*+)\"]",
"//ldml/dates/calendars/calendar[@type=\"([^\"]*+)\"]/months/monthContext[@type=\"([^\"]*+)\"]/monthWidth[@type=\"([^\"]*+)\"]/month[@type=\"([^\"]*+)\"][@yeartype=\"([^\"]*+)\"]",
Expand Down Expand Up @@ -1719,6 +1721,84 @@ public void TestUnicodeSetExamples() {
}
}

public void TestEraMap() {
ExampleGenerator exampleGenerator = getExampleGenerator("en");
Relation<String, String> keyToSubtypes = SupplementalDataInfo.getInstance().getBcp47Keys();
Set<String> calendars = keyToSubtypes.get("ca"); // gets calendar codes
Map<String, String> codeToType =
new HashMap<String, String>() {
{ // calendars where code != type
put("gregory", "gregorian");
put("iso8601", "gregorian");
put("ethioaa", "ethiopic-amete-alem");
put("islamic-civil", "islamic");
put("islamic-rgsa", "islamic");
put("islamic-tbla", "islamic");
put("islamic-umalqura", "islamic");
put("islamicc", "islamic");
}
};
for (String id : calendars) {
if (codeToType.containsKey(id)) {
id = codeToType.get(id);
}
Map<String, List<Date>> calendarMap = exampleGenerator.CALENDAR_ERAS;
assertTrue(
"CALENDAR_ERAS map contains calendar type \"" + id + "\"",
calendarMap.containsKey(id));
}
}

public void TestEraFormats() {
ExampleGenerator exampleGeneratorJa = getExampleGenerator("ja");
ExampleGenerator exampleGeneratorEs = getExampleGenerator("es");
ExampleGenerator exampleGeneratorZh = getExampleGenerator("zh");
checkValue(
"japanese type=235 abbreviated",
"〖平成1年〗",
exampleGeneratorJa,
"//ldml/dates/calendars/calendar[@type=\"japanese\"]/eras/eraAbbr/era[@type=\"235\"]");
checkValue(
"gregorian type=0 wide",
"〖1 antes de Cristo〗",
exampleGeneratorEs,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/eras/eraNames/era[@type=\"0\"]");
checkValue(
"gregorian type=0-variant wide",
"〖1 antes de la era común〗",
exampleGeneratorEs,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/eras/eraNames/era[@type=\"0\"][@alt=\"variant\"]");
checkValue(
"roc type=1 abbreviated",
"〖民国1年〗",
exampleGeneratorZh,
"//ldml/dates/calendars/calendar[@type=\"roc\"]/eras/eraAbbr/era[@type=\"1\"]");
}

public void TestQuarterFormats() {
ExampleGenerator exampleGenerator = getExampleGenerator("ti");
checkValue(
"ti Q2 format wide",
"〖2ይ ርብዒ 1999〗",
exampleGenerator,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/quarters/quarterContext[@type=\"format\"]/quarterWidth[@type=\"wide\"]/quarter[@type=\"2\"]");
checkValue(
"ti Q2 format abbreviated",
"〖ር2 1999〗",
exampleGenerator,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/quarters/quarterContext[@type=\"format\"]/quarterWidth[@type=\"abbreviated\"]/quarter[@type=\"2\"]");
checkValue(
"ti Q4 stand-alone wide",
"〖4ይ ርብዒ 1999〗",
exampleGenerator,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/quarters/quarterContext[@type=\"stand-alone\"]/quarterWidth[@type=\"wide\"]/quarter[@type=\"4\"]");
checkValue(
"ti Q4 stand-alone abbreviated",
"〖ር4 1999〗",
exampleGenerator,
"//ldml/dates/calendars/calendar[@type=\"gregorian\"]/quarters/quarterContext[@type=\"stand-alone\"]/quarterWidth[@type=\"abbreviated\"]/quarter[@type=\"4\"]");
}

static final class MissingKey implements Comparable<MissingKey> {
final SectionId sectionId;
final PageId pageId;
Expand Down
Loading