Android上的时间解析问题

4

当我尝试解析时间字符串02:22 p.m.时,出现了解析异常。

我有以下转换函数:

public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){
    SimpleDateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ROOT);
    SimpleDateFormat targetFormat = new SimpleDateFormat(newdateformat,Locale.ROOT);
    Date date = null;
    try {
        date = originalFormat.parse(datestring);
        String formattedDate = targetFormat.format(date);
        Date parsedDate = targetFormat.parse(formattedDate);
        long nowMilliseconds = parsedDate.getTime();

        return nowMilliseconds;
    } catch (ParseException e) {
        e.printStackTrace();
        return 0;
    }

}

该方法在另一个活动中使用时间格式"02:22 p.m."进行调用。olddateformatnewdateformat是相同的:hh:mm a

此操作会导致日志中出现以下错误:

java.text.ParseException: 无法解析的日期:"02:22 p.m."(位于偏移量6处)

如何解决此问题?时间格式与上述完全相同。


“olddateformat”和“newdateformat”变量的值是多少? - user7605325
"hh:mm a" 相同 - SARATH V
我认为 SimpleDateFormat 无法自定义解析 p.m.(它只能识别 PM)。此外,要获取 nowMilliseconds 值,您需要所有日期字段(日/月/年)和时区。SimpleDateFormat 将其设置为1970年1月1日(并将秒数设置为零),并使用系统的默认时区。这符合您的要求吗? - user7605325
如果时间格式为“02:22 PM”,我可以获取当前毫秒数。是否有解析下午类型的可能格式? - SARATH V
如果您删除点号,它就是这样的(恐怕这是唯一的方法)。但是,您是否理解nowMilliseconds是一个时间戳值?(自1970年01月01日00:00Z以来的毫秒数)。该值不仅取决于小时,还取决于日期、月份、年份和时区。SimpleDateFormat为其设置默认值(请参见我的先前评论),但这真的是您需要的吗? - user7605325
如果我在我的机器上运行这段代码,它将使用我的系统默认时区(美洲/圣保罗),结果为62520000。但是,如果我将时区更改为另一个(比如亚洲/加尔各答),结果将为31920000。您需要这个值做什么? - user7605325
3个回答

3

恰好在盖尔语环境中,上午和下午被称为a.m.和p.m.。至少在我的Java 8中是这样的。我不确定在(所有)安卓手机上是否也是如此,但你可以进行一些实验。

String datestring = "02:22 p.m.";
Locale parseLocale = Locale.forLanguageTag("ga");
DateTimeFormatter originalFormat = DateTimeFormatter.ofPattern("hh:mm a", parseLocale);
System.out.println(LocalTime.parse(datestring, originalFormat));

这将打印

14:22

正如Hugo所热情而正确地推荐的他的答案,我正在使用现代Java日期和时间API,因此您需要ThreeTenABP来运行上述代码。请参见如何在Android项目中使用ThreeTenABP。或者,您也可以尝试使用已过时的SimpleDateFormat,并且使用相同的区域设置。

在我的Java 8上,美国西班牙语区域设置显示相同的行为,因此您也可以尝试:Locale.forLanguageTag("es-US")


2
我相信SimpleDateFormat无法自定义解析p.m.部分(它只能识别AMPM)。

因此,一个替代方案是去掉点:

String time = "02:22 p.m.";
SimpleDateFormat format = new SimpleDateFormat("hh:mm a", Locale.ROOT);
date = format.parse(time.replaceAll("\\.", ""));

一个细节:要获取nowMilliseconds值,您需要所有日期字段(日/月/年)和时区。由于这些字段不在输入的String中,SimpleDateFormat将它们设置为1970年1月1日(还将秒和毫秒设置为零),并使用系统的默认时区。 我不确定获取1970年1月的这种行为是否在所有Java版本中都是一致的,这是另一个问题,因为您可以根据代码运行的环境/设备而获得不同的值。实际上,您可能会因为它使用系统的默认时区而在任何情况下获得不同的结果,而这可以在不同的环境中有所不同。 如果我在我的机器上运行此代码,则使用我的系统的默认时区(America/Sao_Paulo),结果为62520000。但如果我将时区更改为另一个时区(比如Asia/Kolkata),则结果为31920000。您必须注意这种变化,并检查那是否是您真正需要的。
另一个细节是,如果 olddateformatnewdateformat 相同,则不需要创建两个不同的格式化程序。

Java的新日期/时间API

旧的类(DateCalendarSimpleDateFormat)存在很多问题设计问题,它们正在被新的API所取代。

在Android中,您可以使用ThreeTen Backport,这是Java 8新日期/时间类的一个很好的后移版本。您还需要ThreeTenABP(更多关于如何使用它的信息在这里)。

所有相关的类都在org.threeten.bp包中。

通过这个新的 API,你可以使用 org.threeten.bp.format.DateTimeFormatterBuilder 自定义与上午/下午对应的文本(无需手动删除点)。而且每种情况都有特定的类别。在这种情况下,输入只包含时间字段(小时和分钟),所以我将使用 org.threeten.bp.LocalTime 类(它表示仅时间 - 小时/分钟/秒/纳秒 - 没有日期):
String time = "02:22 p.m.";
// map AM and PM values to strings "a.m." and "p.m."
Map<Long, String> map = new HashMap<Long, String>();
map.put(0L, "a.m.");
map.put(1L, "p.m.");
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // hour and minute
    .appendPattern("hh:mm ")
    // use custom values for AM/PM
    .appendText(ChronoField.AMPM_OF_DAY, map)
    // create formatter
    .toFormatter(Locale.ROOT);

// parse the time
LocalTime parsedTime = LocalTime.parse(time, fmt);

parsedTime变量将包含与02:22 PM(仅此值,它没有日期字段(日/月/年)或时区)相对应的值。

要获取毫秒值(number of milliseconds since 1970-01-01T00:00Z),还需要日期(日/月/年)和时区。正如我之前所说,这些字段可能会影响最终值。

在旧API中,SimpleDateFormat试图变得“聪明”,并为这些字段设置默认值(系统默认时区下的1970年1月1日),但新API对此更加严格,您必须明确告诉它您想要的日期和时区。

在此示例中,我正在使用Asia/Kolkata时区,但您可以根据需要进行更改(有关详细信息,请参见以下内容):

import org.threeten.bp.LocalDate;
import org.threeten.bp.ZoneId;
import org.threeten.bp.ZonedDateTime;

// timezone for Asia/Kolkata
ZoneId zone = ZoneId.of("Asia/Kolkata");
// current date in Kolkata timezone
LocalDate now = LocalDate.now(zone);
// get the parsed time at the specified date, at the specified zone
ZonedDateTime zdt = parsedTime.atDate(now).atZone(zone);
// get the millis value
long millis = zdt.toInstant().toEpochMilli();

如果您想要一个特定的日期而不是当前日期,您可以使用LocalDate.of(2017, 5, 20) - 这将得到例如2017年5月20日。通过这种方式,您可以将上面的代码设置为所需的日期和时区。
请注意,API使用IANA时区名称(始终采用Region/City格式,例如America/Sao_PauloAsia/Kolkata)。避免使用3字母缩写(如ISTPST),因为它们是模糊且不标准的
您可以调用ZoneId.getAvailableZoneIds()来获取可用时区列表(并选择最适合您系统的时区)。
如果你想精确地模拟 SimpleDateFormat 的行为,你可以使用 LocalDate.of(1970, 1, 1) 并使用默认时区 ZoneId.systemDefault() - 但是这不是推荐的做法,因为系统的默认值可能会在运行时被更改而没有通知。最好明确指定使用的时区。

或者你可以创建一个格式化程序,始终为日期设置默认值(使用 org.threeten.bp.temporal.ChronoField 类)并始终使用相同的时区。这样,你就可以直接将其解析为 org.threeten.bp.Instant 并获取毫秒值。

String time = "02:22 p.m.";
ZoneId zone = ZoneId.of("Asia/Kolkata");
DateTimeFormatter fmt2 = new DateTimeFormatterBuilder()
    // hour and minute
    .appendPattern("hh:mm ")
    // use custom values for AM/PM (use the same map from previous example)
    .appendText(ChronoField.AMPM_OF_DAY, map)
    // default value for day: 1
    .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
    // default value for month: January
    .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
    // default value for year: 1970
    .parseDefaulting(ChronoField.YEAR, 1970)
    // create formatter at my specific timezone
    .toFormatter(Locale.ROOT).withZone(zone);
// get the millis value
long millis = Instant.from(fmt2.parse(time)).toEpochMilli();

0

我所做的更改对我来说正常运行。

 public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){
        DateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ENGLISH);
        DateFormat targetFormat = new
                SimpleDateFormat(newdateformat,Locale.ENGLISH);
        Date date = null;
        try {
            date = originalFormat.parse(datestring.replaceAll("\\.", ""));
            String formattedDate = targetFormat.format(date);
            Date parsedDate = targetFormat.parse(formattedDate);
            long nowMilliseconds = parsedDate.getTime();

            return nowMilliseconds;
        } catch (ParseException e) {
            e.printStackTrace();
            return 0;
        }

    }

Locale.ENGLISH 你可以使用你的地区语言,英语解决了我的问题。参考资料。

感谢回复和参考资料。


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