使用本地化的Java DateFormat/SimpleDateFormat(仅显示月份中的天数和月份)

3

我有一个多文化的应用程序,该项目使用Java 7。我需要显示日期,但只需要月份和日。例如,在Locale.UK中,今天的日期看起来像:24/01,但在Locale.US中则是:1/24。如何在Java 7中实现这一点?我尝试使用DateFormat.getDateInstance(dateFormat, locale),但在这种情况下,我只能使用预定义的日期格式,例如DateFormat.SHORTDateFormat.DEFAULT等,没有预定义的仅包含月份和日的格式。接下来,我试图使用带有区域设置的SimpleDateFormat,但它并没有按照我想要的方式运行,它只是根据区域设置翻译了一些文本。以下是我的示例代码:

DateFormat dfuk = DateFormat.getDateInstance(DateFormat.SHORT, Locale.UK);
DateFormat dfus = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US);
System.out.println(dfuk.format(new Date()));  // 24/01/17
System.out.println(dfus.format(new Date()));  // 1/24/17
SimpleDateFormat sdfuk = new SimpleDateFormat("dd/MM", Locale.UK);
SimpleDateFormat sdfus = new SimpleDateFormat("dd/MM", Locale.US);
System.out.println(sdfuk.format(new Date()));  // 24/01
System.out.println(sdfus.format(new Date()));  // 24/01

我期望最后一行输出01/24(或1/24)。如何实现这一点?

你关于sdfus的倒数第三行写到 SimpleDateFormat sdfus = new SimpleDateFormat("dd/MM", Locale.US); -- 你是不是想说应该用 MM/dd 而不是 dd/MM - leeyuiwah
可以实现,但是如果你说我必须编写额外的条件来验证实际区域设置,并选择适当的格式(dd/MM或MM/dd),我希望找到Java库内置的解决方案来解决我的问题。 - bladekp
getDateInstance() 方法需要一个 style 参数。最短的样式似乎是 DateFormat.SHORT。你可以争论,也许他们应该提供一个更短的(比如DateFormat.SHORTER?)参考网址:http://docs.oracle.com/javase/6/docs/api/java/text/DateFormat.html#getDateInstance(int) - leeyuiwah
1
我的库Time4J提供了类net.time4j.xml.AnnualDate,支持大约80个语言环境下的四种格式样式。版本行v3.x可在Java-6或7上运行,更新版本v4.x可在Java-8上运行。如果您仍然缺少任何语言环境,请在Time4J跟踪器上打开问题。当前的i18n数据基于unicode-cldr-version v30。 - Meno Hochschild
4个回答

2

你的代码出现了相同的格式错误,因为两个DateFormat都使用了相同的模式,这是不正确的。

SimpleDateFormat sdfuk = new SimpleDateFormat("dd/MM", Locale.UK);
SimpleDateFormat sdfus = new SimpleDateFormat("dd/MM", Locale.US);

如果您有一个将要转换为日期的字符串,那么需要使用本地时间...对于您正在尝试进行的解析(字符串 -> 日期),只需要模式即可... 示例
DateFormat sdfuk = new SimpleDateFormat("dd/MM");
DateFormat sdfus = new SimpleDateFormat("MM/d");
System.out.println(sdfuk.format(new Date())); // 24/01
System.out.println(sdfus.format(new Date())); // 01/24

1
这个可以,但我们有大约80个本地化语言,如果你说我必须编写额外的条件来验证实际的本地化语言,并选择适当的格式(dd/MM或MM/dd),我希望找到Java库内置的解决方案来解决我的问题。 - bladekp

1
将你的倒数第三行改为:
SimpleDateFormat sdfus = new SimpleDateFormat("MM/dd", Locale.US);

克服标准Java提供的不足:

getDateInstance()方法需要一个style参数。最短的样式似乎是DateFormat.SHORT。你可以提出,也许他们应该提供一个更短的(例如DateFormat.SHORTER)。

http://docs.oracle.com/javase/6/docs/api/java/text/DateFormat.html#getDateInstance(int)

在此之前,您可以构建一个短样式的枚举。以下是一个示例:
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

enum PatternShorter { // or PatternMonthDayOnly
    MM_SLASH_DD     ("MM/dd")
    , DD_SLASH_MM   ("dd/MM")
    ;
    private String pattern;

    public String getPattern() {
        return pattern;
    }

    private PatternShorter(String pattern) {
        this.pattern = pattern;
    }

    public static PatternShorter getDefault() { return DD_SLASH_MM; }
}

public class DateFormatEx {
    private static Map<Locale, PatternShorter> patternShorter = new HashMap<>();

    static {
        patternShorter.put(Locale.UK, PatternShorter.DD_SLASH_MM);
        patternShorter.put(Locale.UK, PatternShorter.MM_SLASH_DD);
        // any locale not listed here will get the default pattern
    }

    private static String getPattern (Locale locale) {      
        if (patternShorter.get(locale)!=null) {
            return patternShorter.get(locale).getPattern();
        } else {
            return PatternShorter.getDefault().getPattern();
        }
    }

    public static void main(String[] args) {
        List<Locale> listOfLocale = Arrays.asList(Locale.UK, Locale.US, Locale.FRENCH);
        for (Locale locale : listOfLocale) {
            SimpleDateFormat fmt 
            = new SimpleDateFormat(getPattern(locale), locale);
            System.out.format("for locale %s the shorter date/month display is: %s%n"
                    , locale.toString()
                    , fmt.format(new Date()));  
        }
    }

}

输出将是:

for locale en_GB the shorter date/month display is: 01/24
for locale en_US the shorter date/month display is: 24/01
for locale fr the shorter date/month display is: 24/01

1
这样做是可以的,但我们有大约80个本地化语言,如果你说我必须编写额外的条件来验证实际的本地化语言,并选择适当的格式(dd/MM或MM/dd),我希望能够找到Java库内置的解决方案来解决我的问题。 - bladekp
@bladekp -- 我已更新回答并添加了如何克服标准Java所提供缺点的解释。 - leeyuiwah

0

我最终修改了由DateFormat.getDateInstance(DateFormat.SHORT)提供的本地化格式字符串,从中删除了年份部分:

DateFormat shortDateFormat = DateFormat.getDateInstance(DateFormat.SHORT);
String shortPattern = ((SimpleDateFormat) shortDateFormat).toLocalizedPattern();
String shorterPattern = shortPattern.replaceAll("[/\\- ]*[yY]+[^a-zA-Z]*", "");
DateFormat shorterDateFormat = new SimpleDateFormat(shorterPattern);

我浏览了系统提供的全部734个区域设置,并且就我所知,生成的日期格式对所有区域设置都很好。

以下是一些将原始短格式与修改后的更短格式进行比较的示例:

         short      shorter
en_US    M/d/yy     M/d
en_GB    dd/MM/y    dd/MM
de_DE    dd.MM.yy   dd.MM.
fr_FR    dd/MM/y    dd/MM
nl_NL    dd-MM-y    dd-MM
ja_JP    y/MM/dd    MM/dd

0

简而言之

MonthDay.from( 
    LocalDate.now( ZoneId.of( "America/Montreal" ) ) 
).format( DateTimeFormatter.ofPattern( "MM/dd" ) )

01/24

月日

有一个专门的类:MonthDay

MonthDay 表示月份和日期的组合。

MonthDay md = MonthDay.of( Month.JANUARY , 24 );

避免使用过时的日期时间类。

避免使用麻烦的旧日期时间类,如 SimpleDateFormat,现在已过时,并被 java.time 类取代。

MonthDay 类是内置于 Java 8 及更高版本的 java.time 框架的一部分。大部分 java.time 功能可以回溯到 Java 6 和 Java 7(详见下文)。

ISO 8601

md.toString(): --01-24

toString 方法生成符合标准 ISO 8601 格式的字符串。MonthDay 类也可以解析这种字符串。

当前日期

您想要获取当前日期的月份和日期。对于当前日期,我们将使用 LocalDate 类。LocalDate 类表示仅包含日期而不包含时间和时区的值。

时区

时区对于确定日期至关重要。对于任何给定的时刻,日期会因时区而在全球范围内变化。例如,在巴黎法国午夜过后几分钟是新的一天,但在蒙特利尔魁北克仍然是“昨天”。
请使用continent/region格式指定一个正确的时区名称,例如America/MontrealAfrica/CasablancaPacific/Auckland。不要使用3-4个字母的缩写,例如ESTIST,因为它们不是真正的标准化的时区,也不是唯一的!
请明确,Locale与时区没有任何关系Locale仅适用于生成字符串时的文本格式,但与时区是分开且独立的。
ZoneId z = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( z );

现在提取一个MonthDay对象。

MonthDay md = MonthDay.from( today );

today.toString(): 2017-01-25

md.toString(): --01-25

其他格式

您可以生成其他格式的字符串。

java.time类可以通过Locale自动本地化一些日期时间值。不幸的是,这不适用于MonthDay。对于MonthDay,当您需要除标准ISO 8601格式之外的内容时,必须在DateTimeFormatter对象中明确指定所需的格式。顺便说一句,我鼓励您尽可能使用标准格式,特别是用于交换数据。您甚至可以考虑培训用户接受此格式。标准格式是明确无歧义的,而您的英美格式(dd/MM、MM/dd)可能完全模棱两可,例如01/02既可以是1月2日也可以是2月1日。

使用Locale来确定(a)名称翻译的人类语言,例如星期几、月份等,以及(b)决定缩写、大写、标点符号等方面的文化规范。通常,我建议始终为您的DateTimeFormatter指定一个Locale,而不是隐式地依赖于JVM的当前默认Locale。但是我没有看到Locale如何影响此特定格式的输出方式。因此,在此示例中省略了Locale

DateTimeFormatter fUS = DateTimeFormatter.ofPattern( "MM/dd" );
DateTimeFormatter fUK = DateTimeFormatter.ofPattern( "dd/MM" );

String output = md.format( fUS );

实时代码

查看此示例 在IdeOne.com上实时运行的代码


关于java.time

java.time框架内置于Java 8及以上版本中。这些类取代了老旧的遗留日期时间类,如java.util.DateCalendarSimpleDateFormat

Joda-Time项目现在处于维护模式,建议迁移到java.time类。

想要了解更多,请查看Oracle教程。并在Stack Overflow上搜索许多示例和解释。规范是JSR 310

如何获取java.time类?

ThreeTen-Extra 项目通过添加附加类来扩展 java.time。该项目是可能未来对 java.time 的补充的试验场。您可以在这里找到一些实用的类,例如 IntervalYearWeekYearQuarter更多


1
我认为这个回答没有理解问题的要点。OP想要一种检索区域设置的“最短”日期格式的方法 - 也就是说,对于Locale.US来说是MM/d,对于Locale.UK来说是d/MM,对于其他任何Locale来说都是适当的格式,例如德国的dd.MM。我认为这是不可能的。但你所回答的是完全不同的问题。 - Dawood ibn Kareem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接