1

我有以下代码

public Long getEpochTime(String dateToGetItsEpoch) throws ParseException
{
    TimeZone timeZone = TimeZone.getTimeZone("UTC");
    final String REQUEST_DATE_FORMAT = "dd/MM/yyyy h:m";

    DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
    Date localDate = format.parse(dateToGetItsEpoch);

    Calendar cal = Calendar.getInstance(timeZone);
    cal.setTime(localDate);

    format.setTimeZone(timeZone);
    final String utcTime = format.format(cal.getTime());

    Date d = cal.getTime();

    return d.getTime();
}

如果我将设备的语言环境更改为任何内容,我总是将 UTC 时间作为返回值。哪个是正确的,但是我想知道这是怎么回事?设备如何知道哪个时区是我给它的日期,以便它进行相应的计算?

4

2 回答 2

3

ADate根本没有时区。ASimpleDateFormat作为解析和格式化的默认值;aCalendar也可以;没有Date

给定以下操作序列:

TimeZone timeZone = TimeZone.getTimeZone("UTC");
DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
Date localDate = format.parse(dateToGetItsEpoch);

Calendar cal = Calendar.getInstance(timeZone);
cal.setTime(localDate);

format.setTimeZone(timeZone);
final String utcTime = format.format(cal.getTime());

...您最初使用设备的默认时区解析字符串,然后将其格式化为 UTC。请注意,这Calendar部分在这里无关紧要 - 你会得到相同的结果:

TimeZone timeZone = TimeZone.getTimeZone("UTC");
DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
Date date = format.parse(dateToGetItsEpoch);
format.setTimeZone(timeZone);
final String utcTime = format.format(date);

请注意,我个人建议尽可能使用Joda Time在 Java 中进行日期/时间工作。这是一个比Calendar/更干净的 API Date

于 2013-08-28T08:29:15.117 回答
1

java.time

Jon Skeet的答案是正确的。这是一些更新的代码以使用现代java.time类,这些类取代了麻烦的遗留日期时间类。

格式化模式

定义格式模式以匹配您的输入。

顺便说一句,您的格式选择不佳。相反,我建议使用为将日期时间值交换为文本而设计的标准ISO 8601格式。

12 小时制与 24 小时制

您的输入数据或格式模式存在缺陷。您使用了小写字母h,这意味着 12 小时制中一个小时的一位或两位数字(而不是 24 小时制,即大写HHH)。AM因此,除非您添加一些or指示符,否则您的输入毫无意义PM。我会假设你错误地从你的问题代码中省略了这个。

Locale locale = Locale.US ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu h:m a" ).withLocale( locale ) ;

LocalDateTime

将此类字符串解析为LocalDateTime对象,因为它们缺少预期时区的指示符或与 UTC 的偏移量。

String input = "23/01/2020 4:5 PM" ;

LocalDateTime ldt = LocalDateTime.parse( input , f ) ;

ldt.toString(): 2020-01-23T16:05

片刻

我们上面得到的LocalDateTime对象并不代表一个时刻,不是时间轴上的一个点。我们的时间是 23 日下午 4 点左右。但我们不知道这是否意味着东京、图卢兹或托莱多的下午 4 点——所有这些时刻相隔几个小时。

要确定某个时刻,我们必须确定预期的时区。然后将该区域应用为 aZoneId以获得ZonedDateTime. 然后我们就到了。

Locale不是时区

我的设备的语言环境到任何地方

A时区Locale无关。ALocale用于本地化生成的表示日期时间对象的文本。

要本地化,请指定:

  • FormatStyle确定字符串的长度或缩写。
  • Locale确定:
    • 用于翻译日期名称、月份名称等的人类语言。
    • 决定缩写、大写、标点、分隔符等问题的文化规范。

例子:

Locale l = Locale.CANADA_FRENCH ;   // Or Locale.US, Locale.JAPAN, etc.
DateTimeFormatter f = 
    DateTimeFormatter
    .ofLocalizedDateTime( FormatStyle.FULL )
    .withLocale( l )
;
String output = myZonedDateTime.format( f );

您可能有一位来自魁北克的工程师,他使用Locale.CANADA_FRENCH人类语言和文化规范,但在日本访问时使用Asia/Tokyo时区来安排约会。

ZonedDateTime

回到你的LocalDateTime对象。如果您确定它是为了代表突尼斯挂钟时间中的某个时刻,则应用时区Africa/Tunis.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;

您问:

设备如何知道哪个时区是我给它的日期,以便它进行相应的计算?

您正在使用糟糕的日期时间类,这些类未能解释缺少时区指示符或与 UTC 偏移量的日期时间的概念。所以从技术上讲,你的代码是一团糟,一个黑客,在Joda-Time及其继任者java.time之前的那些日子里是不可避免的。

我建议不费吹灰之力试图理解Date&的行为Calendar。继续使用java.time,业界领先的日期时间处理框架。

于 2020-02-05T23:37:55.833 回答