我相信
SimpleDateFormat
无法自定义解析
p.m.
部分(它只能识别
AM
或
PM
)。
因此,一个替代方案是去掉点:
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
。您必须注意这种变化,并检查那是否是您真正需要的。
另一个细节是,如果
olddateformat
和
newdateformat
相同,则不需要创建两个不同的格式化程序。
Java的新日期/时间API
旧的类(Date
、Calendar
和SimpleDateFormat
)存在很多问题和设计问题,它们正在被新的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<Long, String> map = new HashMap<Long, String>();
map.put(0L, "a.m.");
map.put(1L, "p.m.");
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.appendPattern("hh:mm ")
.appendText(ChronoField.AMPM_OF_DAY, map)
.toFormatter(Locale.ROOT);
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;
ZoneId zone = ZoneId.of("Asia/Kolkata");
LocalDate now = LocalDate.now(zone);
ZonedDateTime zdt = parsedTime.atDate(now).atZone(zone);
long millis = zdt.toInstant().toEpochMilli();
如果您想要一个特定的日期而不是当前日期,您可以使用
LocalDate.of(2017, 5, 20)
- 这将得到例如
2017年5月20日。通过这种方式,您可以将上面的代码设置为所需的日期和时区。
请注意,API使用
IANA时区名称(始终采用
Region/City
格式,例如
America/Sao_Paulo
或
Asia/Kolkata
)。避免使用3字母缩写(如
IST
或
PST
),因为它们是
模糊且不标准的。
您可以调用
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()
.appendPattern("hh:mm ")
.appendText(ChronoField.AMPM_OF_DAY, map)
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
.parseDefaulting(ChronoField.YEAR, 1970)
.toFormatter(Locale.ROOT).withZone(zone);
long millis = Instant.from(fmt2.parse(time)).toEpochMilli();
SimpleDateFormat
无法自定义解析p.m.
(它只能识别PM
)。此外,要获取nowMilliseconds
值,您需要所有日期字段(日/月/年)和时区。SimpleDateFormat
将其设置为1970年1月1日(并将秒数设置为零),并使用系统的默认时区。这符合您的要求吗? - user7605325nowMilliseconds
是一个时间戳值?(自1970年01月01日00:00Z以来的毫秒数)。该值不仅取决于小时,还取决于日期、月份、年份和时区。SimpleDateFormat
为其设置默认值(请参见我的先前评论),但这真的是您需要的吗? - user7605325亚洲/加尔各答
),结果将为31920000。您需要这个值做什么? - user7605325