有没有办法将以 ISO 8601 句点格式给出的字符串转换为人类可读的格式,并翻译为指定的语言? 示例:
P1W
--en
--> 1 Week
P2W
--en
--> 2 Weeks
P1W
--pl
--> 1 Tydzień
P2W
--pl
--> 2 Tygodnie
我正在寻找一个已经实施的解决方案,无需定义周期翻译。
Unicode 通用区域设置数据存储库(通常称为 CLDR)是一个权威的存储库,用于存储各种本地化信息(包括翻译)。
自 Java 9 以来,Java SE 已将 CLDR 用于许多(如果不是全部)本地化功能。但我不知道使用 CLDR 数据文件对 java.time.Period 实例进行任何本地化格式化。
CLDR 有自己的 Java 工具,但我对编译后的 .jar 进行了几个小时的实验,但不知道如何使用它。除了(非常短的)命令行帮助之外,我找不到任何文档。如果它有一个用于格式化句点的 API,我肯定会喜欢使用它。
但是CLDR 数据文件 仍然是获取此信息的最佳位置。 (它们也可以从 https://github.com/unicode-org/cldr 克隆)因此,考虑到这一点,我编写了一个(相当长的)类来使用 CLDR 数据文件格式化 period 实例。
涉及几个步骤:
common/supplemental/plurals.xml
文件包含所有语言环境的所有数据;它的格式记录在 unicode.org.common/main
目录中,作为按语言环境命名的 .xml 文件。与周期相关的元素在 <ldml>
→ <units>
→ <unitLength type="long">
→ <unit type="duration-week">
,以及 type="duration-year"、type="duration-month" 和 type="duration-day" 。单元元素内有 <unitPattern>
元素,每个元素都有一个 count
属性引用上述基数。common/main
中的相同语言环境特定文件包含<listPattern>
元素,这些元素描述如何格式化项目列表,包括用于组装周期部分列表的<listPattern type="unit">
。 此处描述了此数据的用途。请注意,Period.parse 接受 ISO 8601 格式,包括周,但 period 类本身不跟踪周,因此如果需要输出周,代码必须将 period 的天数除以 7。
这就是我的想法。解析不稳健,并且对正确的 CLDR 数据做出了很多假设。可能有一些方法可以加快 XML 文件的读取速度,例如缓存最近使用的文件,因此请将此视为概念证明:
public class PeriodFormatter {
public enum Count {
EXACTLY0("0"),
EXACTLY1("1"),
ZERO,
ONE,
TWO,
FEW,
MANY,
OTHER;
private final String attributeValue;
Count() {
this.attributeValue = name().toLowerCase(Locale.US);
}
Count(String attributeValue) {
this.attributeValue = attributeValue;
}
String attributeValue() {
return attributeValue;
}
static Count forAttributeValue(String attrValue) {
for (Count count : values()) {
if (count.attributeValue.equals(attrValue)) {
return count;
}
}
throw new IllegalArgumentException(
"No Count with attribute value \"" + attrValue + "\"");
}
}
private static class Range {
final long start;
final long end;
Range(long value) {
this(value, value);
}
Range(long start,
long end) {
this.start = start;
this.end = end;
}
boolean contains(long value) {
return value >= start && value <= end;
}
boolean contains(double value) {
return value >= start && value <= end;
}
}
private enum Operand {
ABSOLUTE_VALUE("n"),
INTEGER_PART("i"),
FRACTIONAL_PRECISION("v"),
FRACTIONAL_PRECISION_TRIMMED("w"),
FRACTIONAL_PART("f"),
FRACTIONAL_PART_TRIMMED("t"),
EXPONENT_PART("c", "e");
final String[] tokens;
private Operand(String... tokens) {
this.tokens = tokens;
}
static Operand forToken(String token) {
for (Operand op : values()) {
if (Arrays.asList(op.tokens).contains(token)) {
return op;
}
}
throw new IllegalArgumentException(
"No Operand for token \"" + token + "\"");
}
}
private static class Expression {
final Operand operand;
final Long modValue;
Expression(Operand operand,
Long modValue) {
this.operand = operand;
this.modValue = modValue;
}
double evaluate(BigDecimal value) {
switch (operand) {
case ABSOLUTE_VALUE:
return Math.abs(value.doubleValue());
case INTEGER_PART:
return value.longValue();
case FRACTIONAL_PRECISION:
return Math.max(value.scale(), 0);
case FRACTIONAL_PRECISION_TRIMMED:
return Math.max(value.stripTrailingZeros().scale(), 0);
case FRACTIONAL_PART:
BigDecimal frac = value.remainder(BigDecimal.ONE);
frac = frac.movePointRight(Math.max(0, frac.scale()));
return frac.doubleValue();
case FRACTIONAL_PART_TRIMMED:
BigDecimal trimmed =
value.stripTrailingZeros().remainder(BigDecimal.ONE);
trimmed = trimmed.movePointRight(
Math.max(0, trimmed.scale()));
return trimmed.longValue();
case EXPONENT_PART:
String expStr = String.format("%e", value);
return Long.parseLong(
expStr.substring(expStr.indexOf('e') + 1));
default:
break;
}
throw new RuntimeException("Unknown operand " + operand);
}
}
private static abstract class Relation {
boolean negated;
Expression expr;
abstract boolean matches(BigDecimal value);
final boolean matchIfIntegral(BigDecimal value,
LongPredicate test) {
double evaluatedValue = expr.evaluate(value);
long rounded = Math.round(evaluatedValue);
return Math.abs(evaluatedValue - rounded) < 0.000001
&& test.test(rounded);
}
}
private static class IsRelation
extends Relation {
long value;
@Override
boolean matches(BigDecimal value) {
return matchIfIntegral(value, n -> n == this.value);
}
}
private static class InRelation
extends Relation {
final List<Range> ranges = new ArrayList<>();
@Override
boolean matches(BigDecimal value) {
return ranges.stream().anyMatch(
range -> matchIfIntegral(value, n -> range.contains(n)));
}
}
private static class WithinRelation
extends Relation {
final List<Range> ranges = new ArrayList<>();
@Override
boolean matches(BigDecimal value) {
return ranges.stream().anyMatch(r -> r.contains(value.longValue()));
}
}
private static class Condition {
final List<Relation> relations = new ArrayList<>();
boolean matches(BigDecimal value) {
return relations.stream().allMatch(r -> r.matches(value));
}
}
private static class AndConditionSequence {
final List<Condition> conditions = new ArrayList<>();
boolean matches(BigDecimal value) {
return conditions.stream().allMatch(c -> c.matches(value));
}
}
private static class PluralRule {
final Count count;
final List<AndConditionSequence> conditions = new ArrayList<>();
PluralRule(String countSpec,
String ruleSpec) {
this.count = Count.forAttributeValue(countSpec);
Scanner scanner = new Scanner(ruleSpec);
AndConditionSequence andSequence = new AndConditionSequence();
Condition condition = new Condition();
andSequence.conditions.add(condition);
this.conditions.add(andSequence);
while (true) {
String token = scanner.findWithinHorizon("\\S", 0);
if (token.equals("@")) {
// Ignore samples.
break;
}
Operand operand = Operand.forToken(token);
Long modValue = null;
token = scanner.findWithinHorizon(
"mod|%|is|in|within|!?=|not", 0);
if (token.equals("mod") || token.equals("%")) {
modValue = Long.valueOf(
scanner.findWithinHorizon("\\d+", 0));
token = scanner.findWithinHorizon(
"is|in|within|!?=|not", 0);
}
Relation relation;
boolean negated = false;
if (token.equals("not")) {
token = scanner.findWithinHorizon("in|within", 0);
if (token.equals("within")) {
WithinRelation within = new WithinRelation();
relation = within;
within.negated = true;
parseRanges(within.ranges, scanner);
} else {
InRelation in = new InRelation();
relation = in;
in.negated = true;
parseRanges(in.ranges, scanner);
}
relation.negated = true;
} else if (token.equals("is")) {
IsRelation is = new IsRelation();
relation = is;
token = scanner.findWithinHorizon("not|\\d+", 0);
if (token.equals("not")) {
is.negated = true;
token = scanner.findWithinHorizon("\\d+", 0);
}
is.value = Long.valueOf(token);
} else if (token.endsWith("=")) {
InRelation in = new InRelation();
relation = in;
in.negated = token.startsWith("!");
parseRanges(in.ranges, scanner);
} else {
throw new RuntimeException(
"Unexpected token '" + token + "'");
}
relation.expr = new Expression(operand, modValue);
condition.relations.add(relation);
if (!scanner.hasNext("and|or")) {
break;
}
token = scanner.next();
if (token.equals("and")) {
condition = new Condition();
andSequence.conditions.add(condition);
} else {
andSequence = new AndConditionSequence();
this.conditions.add(andSequence);
}
}
}
static void parseRanges(Collection<Range> ranges,
Scanner scanner) {
boolean first = true;
while (true) {
if (!first) {
if (!scanner.hasNext(",.*")) {
break;
}
scanner.findWithinHorizon(",", 0);
}
String token =
scanner.findWithinHorizon("\\d+(?:\\.\\.\\d+)?", 0);
int period = token.indexOf('.');
if (period > 0) {
long start = Long.parseLong(token.substring(0, period));
long end = Long.parseLong(token.substring(period + 2));
ranges.add(new Range(start, end));
} else {
long value = Long.parseLong(token);
ranges.add(new Range(value));
}
first = false;
}
}
boolean matches(BigDecimal value) {
return conditions.stream().anyMatch(c -> c.matches(value));
}
}
private static final Map<Locale, List<PluralRule>> pluralRules =
new HashMap<>();
private static final Map<Locale, Path> dataFiles = new HashMap<>();
private static final Path cldrDir;
static {
String dir = System.getProperty("cldr");
if (dir == null) {
throw new RuntimeException(
"\"cldr\" system property must be set to root directory"
+ " of CLDR data. That data can be downloaded from"
+ "https://cldr.unicode.org/index/downloads or"
+ "https://github.com/unicode-org/cldr .");
}
cldrDir = Paths.get(dir);
}
private final XPath xpath;
public PeriodFormatter() {
this.xpath = XPathFactory.newInstance().newXPath();
}
private static InputSource createSource(Path path) {
return new InputSource(path.toUri().toASCIIString());
}
private Count countFor(BigDecimal amount,
Locale locale) {
synchronized (pluralRules) {
if (pluralRules.isEmpty()) {
Path pluralsFile = cldrDir.resolve(
Paths.get("common", "supplemental", "plurals.xml"));
NodeList rulesElements;
try {
rulesElements = (NodeList) xpath.evaluate(
"//plurals[@type='cardinal']/pluralRules",
createSource(pluralsFile),
XPathConstants.NODESET);
} catch (XPathException e) {
throw new RuntimeException(e);
}
int count = rulesElements.getLength();
for (int i = 0; i < count; i++) {
Element rulesElement = (Element) rulesElements.item(i);
String[] localeNames =
rulesElement.getAttribute("locales").split("\\s+");
NodeList ruleElements =
rulesElement.getElementsByTagName("pluralRule");
int ruleCount = ruleElements.getLength();
List<PluralRule> ruleList = new ArrayList<>(ruleCount);
for (int j = 0; j < ruleCount; j++) {
Element ruleElement = (Element) ruleElements.item(j);
ruleList.add(new PluralRule(
ruleElement.getAttribute("count"),
ruleElement.getTextContent()));
}
for (String localeName : localeNames) {
localeName = localeName.replace('_', '-');
pluralRules.put(
Locale.forLanguageTag(localeName),
ruleList);
}
}
}
}
Locale availableLocale = Locale.lookup(
Locale.LanguageRange.parse(locale.toLanguageTag()),
pluralRules.keySet());
if (availableLocale == null) {
availableLocale = Locale.ROOT;
}
List<PluralRule> rulesOfLocale = pluralRules.get(availableLocale);
for (PluralRule rule : rulesOfLocale) {
if (rule.matches(amount)) {
return rule.count;
}
}
throw new IllegalArgumentException("No plural rule matches " + amount);
}
private static List<Locale> listWithFallbacks(Locale locale) {
Collection<Locale> locales = new LinkedHashSet<>();
locales.add(locale);
Locale.Builder builder = new Locale.Builder();
builder.setLanguageTag(locale.toLanguageTag());
locales.add(builder.setVariant(null).build());
locales.add(builder.setRegion(null).build());
locales.add(builder.setScript(null).build());
locales.add(Locale.ROOT);
return new ArrayList<>(locales);
}
private Iterable<Path> dataFilesFor(Locale locale) {
synchronized (dataFiles) {
if (dataFiles.isEmpty()) {
Path dataFileDir = cldrDir.resolve(Paths.get("common", "main"));
try (DirectoryStream<Path> dir =
Files.newDirectoryStream(dataFileDir, "*.xml")) {
for (Path dataFile : dir) {
InputSource source = createSource(dataFile);
NodeList identityElements = (NodeList)
xpath.evaluate("/ldml/identity", source,
XPathConstants.NODESET);
Element identity = (Element) identityElements.item(0);
String lang =
xpath.evaluate("language/@type", identity);
String script =
xpath.evaluate("script/@type", identity);
String region =
xpath.evaluate("territory/@type", identity);
String variant =
xpath.evaluate("variant/@type", identity);
Locale dataFileLocale;
if (lang.equals("root")) {
dataFileLocale = Locale.ROOT;
} else {
Locale.Builder builder = new Locale.Builder();
builder.setLanguage(lang);
builder.setScript(script);
builder.setRegion(region);
builder.setVariant(variant);
dataFileLocale = builder.build();
}
dataFiles.put(dataFileLocale, dataFile);
}
} catch (IOException | XPathException e) {
throw new RuntimeException(e);
}
}
}
Collection<Locale> locales = listWithFallbacks(locale);
Collection<Path> dataFilesForLocale = new ArrayList<>();
for (Locale localeToCheck : locales) {
Path dataFile = dataFiles.get(localeToCheck);
if (dataFile != null) {
dataFilesForLocale.add(dataFile);
}
}
return dataFilesForLocale;
}
private Optional<Element> locateElement(Object source,
String path) {
try {
Element element = null;
while (true) {
NodeList elements;
if (source instanceof InputSource) {
elements = (NodeList) xpath.evaluate(path,
(InputSource) source, XPathConstants.NODESET);
} else {
elements = (NodeList) xpath.evaluate(path,
source, XPathConstants.NODESET);
}
if (elements.getLength() < 1) {
break;
}
element = (Element) elements.item(0);
NodeList list = (NodeList) xpath.evaluate("alias", element,
XPathConstants.NODESET);
if (list.getLength() == 0) {
// No more aliases to follow, so we've found our target.
break;
}
Element alias = (Element) list.item(0);
path = alias.getAttribute("path");
source = element;
}
return Optional.ofNullable(element);
} catch (XPathException e) {
throw new RuntimeException(e);
}
}
private Optional<String> readElement(Iterable<? extends Path> dataFiles,
String... paths)
throws XPathException {
Optional<Element> element = Optional.empty();
for (Path dataFile : dataFiles) {
Object source = createSource(dataFile);
element = Optional.empty();
for (String path : paths) {
element = locateElement(source, path);
if (!element.isPresent()) {
break;
}
source = element.get();
}
if (element.isPresent()) {
break;
}
}
return element.map(Element::getTextContent);
}
private String format(ChronoUnit units,
BigDecimal amount,
Locale locale) {
String type = null;
switch (units) {
case YEARS:
type = "duration-year";
break;
case MONTHS:
type = "duration-month";
break;
case WEEKS:
type = "duration-week";
break;
case DAYS:
type = "duration-day";
break;
default:
throw new IllegalArgumentException(
"Valid units are YEARS, MONTHS, WEEKS, and DAYS.");
}
Count count = countFor(amount, locale);
try {
Optional<String> formatPattern = readElement(dataFilesFor(locale),
"/ldml/units" +
"/unitLength[@type='long']" +
"/unit[@type='" + type + "']",
"unitPattern[@count='" + count.attributeValue() + "']");
if (formatPattern.isPresent()) {
String patternStr = formatPattern.get();
if (!patternStr.isEmpty()) {
MessageFormat format =
new MessageFormat(patternStr, locale);
return format.format(new Object[] { amount });
}
}
} catch (XPathException e) {
throw new RuntimeException(e);
}
throw new IllegalArgumentException(
"Could not find pattern for units " + units +
", amount " + amount + ", locale " + locale.toLanguageTag());
}
public String format(Period period,
Locale locale) {
return format(period, false, locale);
}
public String format(Period period,
boolean includeWeeks,
Locale locale) {
int years = period.getYears();
int months = period.getMonths();
int days = period.getDays();
List<String> parts = new ArrayList<>(4);
if (years != 0) {
BigDecimal value = BigDecimal.valueOf(years);
String yearStr = format(ChronoUnit.YEARS, value, locale);
parts.add(yearStr);
}
if (months != 0) {
BigDecimal value = BigDecimal.valueOf(months);
String monthStr = format(ChronoUnit.MONTHS, value, locale);
parts.add(monthStr);
}
if (includeWeeks) {
int weeks = days / 7;
if (weeks != 0) {
days %= 7;
BigDecimal value = BigDecimal.valueOf(weeks);
String weekStr = format(ChronoUnit.WEEKS, value, locale);
parts.add(weekStr);
}
}
if (days != 0) {
BigDecimal value = BigDecimal.valueOf(days);
String dayStr = format(ChronoUnit.DAYS, value, locale);
parts.add(dayStr);
}
return formatList(parts, locale);
}
private String formatList(List<?> parts,
Locale locale) {
if (parts.isEmpty()) {
return "";
}
int size = parts.size();
if (size == 1) {
return String.valueOf(parts.get(0));
}
Map<String, String> patternsByType = new HashMap<>();
for (Path dataFile : dataFilesFor(locale)) {
Object source = createSource(dataFile);
Optional<Element> listPatterns =
locateElement(source, "/ldml/listPatterns");
if (!listPatterns.isPresent()) {
continue;
}
Optional<Element> listPattern =
locateElement(listPatterns.get(), "listPattern[@type='unit']");
if (!listPattern.isPresent()) {
continue;
}
NodeList partList =
listPattern.get().getElementsByTagName("listPatternPart");
int count = partList.getLength();
for (int i = 0; i < count; i++) {
Element part = (Element) partList.item(i);
String type = part.getAttribute("type");
String pattern = part.getTextContent();
patternsByType.putIfAbsent(type, pattern);
}
}
if (size == 2 || size == 3) {
String pattern = patternsByType.get(String.valueOf(size));
if (pattern != null) {
MessageFormat format =new MessageFormat(pattern, locale);
return format.format(parts.toArray());
}
}
MessageFormat startFormat = new MessageFormat(
patternsByType.get("start"), locale);
MessageFormat middleFormat = new MessageFormat(
patternsByType.get("middle"), locale);
MessageFormat endFormat = new MessageFormat(
patternsByType.get("end"), locale);
String text = endFormat.format(
new Object[] { parts.get(size - 2), parts.get(size - 1) });
int index = size - 2;
while (--index > 0) {
text = middleFormat.format(new Object[] { parts.get(index), text });
}
text = startFormat.format(new Object[] { parts.get(index), text });
return text;
}
public static void main(String[] args) {
String localeStr = System.getProperty("locale");
Locale locale = localeStr != null ?
Locale.forLanguageTag(localeStr) : Locale.getDefault();
boolean showWeeks = Boolean.getBoolean("weeks");
PeriodFormatter formatter = new PeriodFormatter();
for (String arg : args) {
Period period = Period.parse(arg);
String s = formatter.format(period, showWeeks, locale);
System.out.println(arg + " => " + s);
}
}
}
抱歉,似乎两次,但我将这篇文章留在这里,这个问题对于日历显示名称字段来说是一个可怕的恐惧,早在 2010 年,我花了几天时间寻找小时,分钟,秒显示名称,直到我得到来自 Sun Microsystems 论坛的信息,其中一些没有名字。 API 文档信息几乎都是这么说的,只是不太清楚。它没有清楚地说明 java.time.temporal.TemporalField 和 java.time.temporal.ChronoField 及其方法 getDisplayName(Locale locale) 抱歉(删除了其他答案),我以为你的意思是“时间”本身,你的意思是“时间单位”是java的一部分TemporalUnit的持续时间是一个具有TemporalUnit的时间并且确实有“名称检索方法”但翻译, 不。 然而,java.util.Calendar也有“时间单位”并且可以用Locale实例化,并且应该具有返回“时间单位”外语名称的类字段和方法
这是一个测试打印机类,用于具有显示名称的字段。注释掉的代码打印 getDisplayName() 返回的“null”
// CalendarTest.java
public class CalendarTest{
int[] ipt = {java.util.Calendar.DAY_OF_WEEK,java.util.Calendar.MONTH,java.util.Calendar.ERA,java.util.Calendar.AM_PM};
public CalendarTest(){
//java.time.LocalDateTime tm = java.time.LocalDateTime.now(); +java.util.Calendar.HOUR DAY_OF_WEEK
for(int sd=0;sd<ipt.length;sd++){
pr(ipt[sd],"fr");
}
for(int sd1=0;sd1<ipt.length;sd1++){
pr(ipt[sd1],"zh");
}
for(int sd2=0;sd2<ipt.length;sd2++){
pr(ipt[sd2],"ar");
}
/*
//java.util.Date dt = cal.getTime();
System.out.println(""+cal.get(java.util.Calendar.HOUR));
String fieldname = cal.getDisplayName(java.util.Calendar.HOUR_OF_DAY, java.util.Calendar.SHORT, new java.util.Locale("en"));
System.out.println(fieldname);
fieldname = cal.getDisplayName(java.util.Calendar.HOUR_OF_DAY, java.util.Calendar.LONG, new java.util.Locale("en"));
System.out.println(fieldname);
*/
}//enconstr
public void pr(int fieldint,String loc){
java.util.Calendar cal = java.util.Calendar.getInstance(new java.util.Locale(loc));
java.util.Map<String,Integer> mp = cal.getDisplayNames(fieldint , java.util.Calendar.ALL_STYLES, new java.util.Locale(loc));
if(mp.isEmpty()==true){
System.out.println("Is Empty");
}else{
System.out.println("Contains");
}
java.util.Set<String> st = mp.keySet();
Object[] sta = st.toArray();
for(int fs=0;fs<sta.length;fs++){
System.out.println("A VALUE : "+sta[fs]);
}
}//enmeth
public static void main(String[] args){
new CalendarTest();
}//enmain
}//enclss
/* prints Special field values and in Locale but not field names
bash-5.1$ java CalendarTest
Contains
A VALUE : dimanche
A VALUE : mercredi
A VALUE : vendredi
A VALUE : samedi
A VALUE : jeudi
A VALUE : lundi
A VALUE : mardi
A VALUE : dim.
A VALUE : jeu.
A VALUE : lun.
A VALUE : mar.
A VALUE : mer.
A VALUE : sam.
A VALUE : ven.
Contains
A VALUE : septembre
A VALUE : décembre
A VALUE : novembre
A VALUE : février
A VALUE : janvier
A VALUE : juillet
A VALUE : octobre
A VALUE : avril
A VALUE : févr.
A VALUE : janv.
A VALUE : juil.
A VALUE : sept.
A VALUE : août
A VALUE : avr.
A VALUE : déc.
A VALUE : juin
A VALUE : mars
A VALUE : nov.
A VALUE : oct.
A VALUE : mai
Contains
A VALUE : ap. J.-C.
A VALUE : av. J.-C.
A VALUE : BC
A VALUE : A
A VALUE : B
Contains
A VALUE : AM
A VALUE : PM
A VALUE : a
A VALUE : p
Contains
A VALUE : 星期一
A VALUE : 星期三
A VALUE : 星期二
A VALUE : 星期五
A VALUE : 星期六
A VALUE : 星期四
A VALUE : 星期日
A VALUE : 一
A VALUE : 三
A VALUE : 二
A VALUE : 五
A VALUE : 六
A VALUE : 四
A VALUE : 日
Contains
A VALUE : 10月
A VALUE : 11月
A VALUE : 12月
A VALUE : 十一月
A VALUE : 十二月
A VALUE : 10
A VALUE : 11
A VALUE : 12
A VALUE : 1月
A VALUE : 2月
A VALUE : 3月
A VALUE : 4月
A VALUE : 5月
A VALUE : 6月
A VALUE : 7月
A VALUE : 8月
A VALUE : 9月
A VALUE : 一月
A VALUE : 七月
A VALUE : 三月
A VALUE : 九月
A VALUE : 二月
A VALUE : 五月
A VALUE : 八月
A VALUE : 六月
A VALUE : 十月
A VALUE : 四月
A VALUE : 1
A VALUE : 2
A VALUE : 3
A VALUE : 4
A VALUE : 5
A VALUE : 6
A VALUE : 7
A VALUE : 8
A VALUE : 9
Contains
A VALUE : 公元前
A VALUE : AD
A VALUE : BC
A VALUE : 公元
A VALUE : A
A VALUE : B
Contains
A VALUE : 上午
A VALUE : 下午
A VALUE : a
A VALUE : p
Contains
A VALUE : الأربعاء
A VALUE : الثلاثاء
A VALUE : الاثنين
A VALUE : الجمعة
A VALUE : الخميس
A VALUE : الأحد
A VALUE : السبت
A VALUE : ث
A VALUE : ج
A VALUE : ح
A VALUE : خ
A VALUE : ر
A VALUE : س
A VALUE : ن
Contains
A VALUE : أكتوبر
A VALUE : ديسمبر
A VALUE : سبتمبر
A VALUE : فبراير
A VALUE : نوفمبر
A VALUE : أبريل
A VALUE : أغسطس
A VALUE : يناير
A VALUE : يوليو
A VALUE : يونيو
A VALUE : مارس
A VALUE : مايو
A VALUE : أبر
A VALUE : أغس
A VALUE : أكت
A VALUE : ديس
A VALUE : سبت
A VALUE : فبر
A VALUE : مار
A VALUE : ماي
A VALUE : نوف
A VALUE : ينا
A VALUE : يول
A VALUE : يون
A VALUE : أ
A VALUE : ب
A VALUE : د
A VALUE : س
A VALUE : غ
A VALUE : ف
A VALUE : ك
A VALUE : ل
A VALUE : م
A VALUE : ن
A VALUE : و
A VALUE : ي
Contains
A VALUE : ق.م
A VALUE : A
A VALUE : B
A VALUE : م
Contains
A VALUE : a
A VALUE : p
A VALUE : ص
A VALUE : م
bash-5.1$
*/
下面的代码不起作用(如 OleV.V. 所说),因为某些字段没有获得短或长显示名称。
java.util.Calendar cal = java.util.Calendar.getInstance( java.util.TimeZone.getTimeZone("Asia/Shanghai") , java.util.Locale.CHINA);
String hourof = cal.getDisplayName(java.util.Calendar.HOUR , java.util.Calendar.LONG , java.util.Locale.CHINA);
在 html 中显示它,在文本渲染元素标签中使用属性 lang="zh",例如div , 输入 , td UTF-8 是绑定到语言环境的普通字符集,但对于中文来说可能是 UTF-16
查看 java.util.Calendar 的 API 文档中的“字段摘要”。
Week,似乎是一个问题,但 int SHORT 也许可以简单地显示“week”。