提问人:Bryan 提问时间:1/21/2017 最后编辑:Bryan 更新时间:5/30/2017 访问量:2095
具有更多控制权的本地化日期/时间
Localized Date/Time with More Control
问:
我正在使用 ThreeTen-Backport(特别是 ThreeTenABP)在我的项目中显示时间戳。我希望显示的时间戳以本地化格式显示(基于系统);使用 DateTimeFormatter.ofLocalizedDateTime()
方法都很容易:Locale
DateTimeFormatter formatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(Locale.getDefault())
.withZone(ZoneId.systemDefault());
String timestamp = formatter.format(Instant.now());
问题是我对只有四种类型(、、、)的格式化程序的输出没有太多控制。我很好奇是否有一种方法可以在不丢失本地化格式的情况下对输出进行更精细的控制。FormatStyle
SHORT
MEDIUM
LONG
FULL
使用前面的代码,区域设置的结果为:timestamp
"en_US"
"January 23, 2017 1:28:37 PM EST"
虽然区域设置的结果将是:"ja_JP"
"2017年1月23日 13:28:37 GMT-5:00"
如您所见,每个区域设置都使用特定的模式,并使用默认的 12 小时或 24 小时格式。我想保持本地化的模式,但要更改一些内容,例如是否显示时区,或者是否使用 12 或 24 小时格式。
例如;如果我可以将两个区域设置都设置为使用 12 小时格式,并删除时区;结果如下所示:
"January 23, 2017 1:28:37 PM"
"2017年1月23日 1:28:37午後"
答:
可以使用 DateTimeFormatterBuilder.getLocalizedDateTimePattern
获取 Locale
的格式字符串。获得该字符串后,可以使用 DateTimeFormatter.ofPattern
方法对其进行操作。
String fr = DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.LONG, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRANCE);
//d MMMM yyyy HH' h 'mm z
String ge = DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.LONG, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.GERMAN);
//d. MMMM yyyy HH:mm' Uhr 'z
String ca = DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.LONG, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.CANADA);
//MMMM d, yyyy h:mm:ss 'o''clock' a z
String en = DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.LONG, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.ENGLISH);
//MMMM d, yyyy h:mm:ss a z
在 DateTimeFormatter
中,您可以使用符号字符和 ofPattern 方法指定日期的各个单位。每个单位使用的符号字符数也会影响显示的内容:
M
将为您提供数字的月份。MM
即使月份小于 10,也会让您获得两位数的月份。MMM
应该会给你月份名称。
请参阅 DateTimeFormatter
文档中的“格式化和分析模式”部分。
下面的模式为您提供了四位数的年份、两位数的月份和两位数的日期。
LocalDate localDate = LocalDate.now(); //For reference
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String formattedString = localDate.format(formatter);
评论
Locale
的 (AFAIK) 的问题在于它们使用预定义的模式。虽然,可以获取它们并操纵/更改模式以满足您的需求。FormatStyle
我没有使用特定于 Android 的环境,所以我不确定这段代码对你的效果如何。我正在使用 Java JDK 1.7.0_79 和 ThreeTen 向后移植 1.3.4。我也使用美国/New_York时区进行测试 - 我想它对应于 EST。
我注意到与您的环境存在一些差异:
- 用于日语区域设置给了我
FormatStyle.LONG
2017/01/23 13:28:37 EST
- 我不得不用它来得到
FormatStyle.FULL
2017年1月23日 13時28分37秒 EST
但我认为这不会使我的测试无效。
首先,我使用 class 将本地化模式作为 .然后我根据想要的配置对此进行了一些替换:java.text.DateFormat
String
String
- 查找 、 或 并更改为使用 12 或 24 小时格式(我替换了保持相同数量的字母)
HH
hh
H
h
- 删除或添加(或):删除或添加时区
z
Z
- 避免文字:某些模式(例如pt_BR区域设置)在小时后有一个文字(例如成为),因此我在替换时必须注意这一点
h
HH'h'
13h
创建格式化程序的代码为:
// creates a formatter with the specified style, locale and zone
// there are options to use 12 or 24 hour format and include or not a timezone
public DateTimeFormatter getFormatter(FormatStyle style, Locale locale, ZoneId zone,
boolean use24HourFormat, boolean useTimezone) {
// get the format correspondent to the style and locale
DateFormat dateFormat = DateFormat.getDateTimeInstance(style.ordinal(), style.ordinal(), locale);
// *** JDK 1.7.0_79 returns SimpleDateFormat ***
// If Android returns another type, check if it's possible to get the pattern from this type
if (dateFormat instanceof SimpleDateFormat) {
// get the pattern String for the locale
String pattern = ((SimpleDateFormat) dateFormat).toPattern();
if (use24HourFormat) {
if (pattern.contains("hh")) { // check the "hh" hour format
// hh not surrounded by ' (to avoid literals)
pattern = pattern.replaceAll("((?<!\')hh)|(hh(?!\'))", "HH");
} else { // check the "h" hour format
// h not surrounded by ' (to avoid literals)
pattern = pattern.replaceAll("((?<!\')h)|(h(?!\'))", "H");
}
} else {
if (pattern.contains("HH")) { // check the "HH" hour format
// HH not surrounded by ' (to avoid literals)
pattern = pattern.replaceAll("((?<!\')HH)|(HH(?!\'))", "hh");
} else { // check the "H" hour format
// H not surrounded by ' (to avoid literals)
pattern = pattern.replaceAll("((?<!\')H)|(H(?!\'))", "h");
}
}
if (useTimezone) {
// checking if already contains a timezone (the naive way)
if (!pattern.contains("z") && !pattern.contains("Z")) {
// I'm adding z in the end, but choose whatever pattern you want for the timezone (it can be Z, zzz, and so on)
pattern += " z";
}
} else {
// 1 or more (z or Z) not surrounded by ' (to avoid literals)
pattern = pattern.replaceAll("((?<!\')[zZ]+)|([zZ]+(?!\'))", "");
}
// create the formatter for the locale and zone, with the customized pattern
return DateTimeFormatter.ofPattern(pattern, locale).withZone(zone);
}
// can't get pattern string, return the default formatter for the specified style/locale/zone
return DateTimeFormatter.ofLocalizedDateTime(style).withLocale(locale).withZone(zone);
}
一些用法示例(我的默认值是 pt_BR - 巴西葡萄牙语):Locale
ZoneId zone = ZoneId.of("America/New_York");
Instant instant = ZonedDateTime.of(2017, 1, 23, 13, 28, 37, 0, zone).toInstant();
FormatStyle style = FormatStyle.FULL;
// US locale, 24-hour format, with timezone
DateTimeFormatter formatter = getFormatter(style, Locale.US, zone, true, true);
System.out.println(formatter.format(instant)); // Monday, January 23, 2017 13:28:37 PM EST
// US locale, 24-hour format, without timezone
formatter = getFormatter(style, Locale.US, zone, true, false);
System.out.println(formatter.format(instant)); // Monday, January 23, 2017 13:28:37 PM
// US locale, 12-hour format, with timezone
formatter = getFormatter(style, Locale.US, zone, false, true);
System.out.println(formatter.format(instant)); // Monday, January 23, 2017 1:28:37 PM EST
// US locale, 12-hour format, without timezone
formatter = getFormatter(style, Locale.US, zone, false, false);
System.out.println(formatter.format(instant)); // Monday, January 23, 2017 1:28:37 PM
// japanese locale, 24-hour format, with timezone
formatter = getFormatter(style, Locale.JAPAN, zone, true, true);
System.out.println(formatter.format(instant)); // 2017年1月23日 13時28分37秒 EST
// japanese locale, 24-hour format, without timezone
formatter = getFormatter(style, Locale.JAPAN, zone, true, false);
System.out.println(formatter.format(instant)); // 2017年1月23日 13時28分37秒
// japanese locale, 12-hour format, with timezone
formatter = getFormatter(style, Locale.JAPAN, zone, false, true);
System.out.println(formatter.format(instant)); // 2017年1月23日 1時28分37秒 EST
// japanese locale, 12-hour format, without timezone
formatter = getFormatter(style, Locale.JAPAN, zone, false, false);
System.out.println(formatter.format(instant)); // 2017年1月23日 1時28分37秒
// pt_BR locale, 24-hour format, with timezone
formatter = getFormatter(style, Locale.getDefault(), zone, true, true);
System.out.println(formatter.format(instant)); // Segunda-feira, 23 de Janeiro de 2017 13h28min37s EST
// pt_BR locale, 24-hour format, without timezone
formatter = getFormatter(style, Locale.getDefault(), zone, true, false);
System.out.println(formatter.format(instant)); // Segunda-feira, 23 de Janeiro de 2017 13h28min37s
// pt_BR locale, 12-hour format, with timezone
formatter = getFormatter(style, Locale.getDefault(), zone, false, true);
System.out.println(formatter.format(instant)); // Segunda-feira, 23 de Janeiro de 2017 01h28min37s EST
// pt_BR locale, 12-hour format, without timezone
formatter = getFormatter(style, Locale.getDefault(), zone, false, false);
System.out.println(formatter.format(instant)); // Segunda-feira, 23 de Janeiro de 2017 01h28min37s
笔记:
- 该方法返回一个 ,但我不确定它在 Android 中的工作方式是否相同。您可以检查它是否返回不同的类型,以及是否可以从此类中获取模式(如果不是,那么我不知道另一种方法)。
DateFormat.getDateTimeInstance
SimpleDateFormat
String
- 我创建了更改小时格式(12 或 24)以及包含或删除时区的选项。但是您可以根据需要创建任意数量的选项 - 一旦您有了 模式 ,您可以用它做任何事情
String
- 我的正则表达式(IMO)有点难看。但我不是正则表达式专家,我不确定如何改进它们。我已经用一些语言环境测试了它们,它们似乎很好,但可能存在一些特殊情况,它们会失败(尽管我还没有测试到足够的测试来找到它)
- 如果要添加偏移量(如),可以使用模式(而不是 )。如果您想要与 GMT 的偏移量(如 ),您可以使用该模式。
-05:00
xxx
z
GMT-05:00
ZZZZ
- 我不确定是否所有模式都具有 as 时区(它们可以使用 或 ),因此您可以更改代码以查找其他模式。在我的测试中,我没有发现任何不同之处,但无论如何,我建议对此进行仔细检查以确保。
z
Z
x
z
- 我正在检查模式是否已经有一个时区 - 一种非常幼稚/愚蠢的方式,因为它不处理文字(引号内)。也许这也可以更改为使用正则表达式(尽管我没有找到具有文字模式的区域设置)。
pattern.contains("z")
z
z
评论
a
FormatStytle
评论