更多控制的本地化日期/时间

时间:2017-01-20 21:23:25

标签: java android localization threetenbp

我正在使用ThreeTen-Backport(特别是ThreeTenABP)来显示项目中的时间戳。我希望显示的时间戳以本地化格式显示(基于系统的Locale);这很容易使用DateTimeFormatter.ofLocalizedDateTime()方法之一:

DateTimeFormatter formatter = DateTimeFormatter
        .ofLocalizedDateTime(FormatStyle.LONG)
        .withLocale(Locale.getDefault())
        .withZone(ZoneId.systemDefault());

String timestamp = formatter.format(Instant.now());

问题在于我对格式化程序的输出没有多少控制权,只有四种FormatStyle类型SHORTMEDIUMLONG,{{1 }})。我很好奇是否有办法对输出进行更精细的控制,而不会丢失本地化格式。

使用上一代码,FULL语言区域的结果timestamp将为:

"en_US"

虽然"January 23, 2017 1:28:37 PM EST" 语言环境的结果是:

"ja_JP"

如您所见,每个语言环境都使用特定模式,并使用12或24小时格式的默认值。我想维护本地化模式,但更改时区是否显示,或者是否使用12或24小时格式。

例如;如果我可以将两个语言环境设置为使用12小时格式,并删除时区;结果如下:

"2017年1月23日 13:28:37 GMT-5:00"

2 个答案:

答案 0 :(得分:2)

FormatStyle(AFAIK)的问题在于他们使用预定义的模式。虽然,可以让他们操纵/改变模式以满足您的需求。

我没有使用特定于Android的环境,因此我不确定此代码对您的效果如何。我正在使用 Java JDK 1.7.0_79 ThreeTen Backport 1.3.4 。我还使用 America / New_York 时区进行测试 - 我猜这与 EST 相对应。

我注意到您的环境存在一些差异:

  • FormatStyle.LONG用于日语区域设置会为我2017/01/23 13:28:37 EST
  • 我必须使用FormatStyle.FULL来获取2017年1月23日 13時28分37秒 EST

但我认为这并没有使我的测试无效。

首先,我使用java.text.DateFormat类将本地化模式作为String。然后我根据所需的配置对String进行了一些替换:

  • 查找HHhhHh并更改为使用12或24小时格式(我替换时保留相同数量的字母)
  • 删除或添加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);
}

一些使用示例(我的默认Locale pt_BR - 巴西葡萄牙语):

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 

备注:

  • 方法DateFormat.getDateTimeInstance会返回SimpleDateFormat,但我不确定它在Android中的工作方式是否相同。您可以检查它是否返回不同类型,以及是否可以从此课程中获取模式String(如果不是,那么我不知道另一种做法它)。
  • 我创建了更改小时格式(12或24)并包含或删除时区的选项。但是你可以根据需要创建任意数量的选项 - 一旦你拥有模式String,你可以用它做任何事情
  • 我的正则表达式(IMO)有点难看。但我不是正则表达式专家,我不确定如何改进它们。我已经用一些语言测试了它们,它们看起来很好,但可能会有一些特殊情况,它们会失败(尽管我还没有经过足够的测试才能找到它)
  • 如果您想添加偏移量(例如-05:00),您可以使用xxx模式(而不是z)。如果您希望偏移量为 GMT (例如GMT-05:00),则可以使用ZZZZ模式。
  • 我不确定所有模式是否都有z作为时区(他们可以使用Zx),因此您可能会更改代码以查找其他模式。在我的测试中,我没有找到与z不同的任何内容,但无论如何,我建议对此进行双重检查以确保。
  • 我通过pattern.contains("z")检查模式是否已经有时区 - 这是一种非常天真/愚蠢的方式,因为它不会处理z文字(内部引号)。也许这可以更改为使用正则表达式(尽管我没有找到具有z字面值的模式的区域设置。

答案 1 :(得分:0)

您可以使用Locale获取DateTimeFormatterBuilder.getLocalizedDateTimePattern的格式字符串。获得该字符串后,您可以使用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中,您可以使用符号字符和方法指定日期的各个单位。每单位使用的符号字符数也会影响显示的内容:

  • M会以数字显示月份。
  • MM将为您提供两位数的月份,即使月份小于10。
  • MMM应该为您提供月份名称。

请参阅“格式化和分析的模式”部分 DateTimeFormatter文档。

下面的模式为您提供四位数年份,两位数月份和两位数日期。

LocalDate localDate = LocalDate.now(); //For reference
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String formattedString = localDate.format(formatter);