C# 中的日期时间操作与 Java

DateTime Manipulation in C# vs Java

我是 Java 的新手。我有一段时间从网页上获取,这是 "hh:mm" 格式(不是 24 小时)。这对我来说是一个字符串。然后我想将这个字符串与今天的日期结合起来,以便制作一个我可以使用的 Java Date

在 C# 中:

string s = "5:45 PM";
DateTime d;
DateTime.TryParse(s, out d);

在 Java 我尝试过:

String s = "5:45 PM";
Date d = new Date(); // Which instantiates with the current date/time.

String[] arr = s.split(" ");
boolean isPm = arr[1].compareToIgnoreCase("PM") == 0;

arr = arr[0].split(":");
int hours = Integer.parseInt(arr[0]);
d.setHours(isPm ? hours + 12 : hours);
d.setMinutes(Integer.parseInt(arr[1]));
d.setSeconds(0);

有没有更好的方法来实现我想要的?

Is there a better way to achieve what I want?

绝对 - 事实上,在 .NET 和 Java 中。在 .NET 中,我(以一种有偏见的方式)建议使用 Noda Time,这样您就可以将一天中的某个时间表示为 LocalTime,精确地解析您期望的模式。

在 Java 8 中你可以用 java.time.LocalTime 做同样的事情:

import java.time.*;
import java.time.format.*;

public class Test {
    public static void main(String[] args) {
        String text = "5:45 PM";
        DateTimeFormatter format = DateTimeFormatter.ofPattern("h:mm a");
        LocalTime time = LocalTime.parse(text, format);
        System.out.println(time);
    }
}

一旦您将文本解析为合适的类型,就可以将其与其他类型组合。例如,要获取系统时区的 ZonedDateTime,使用今天的日期和指定的一天中的时间,您可以使用:

ZonedDateTime zoned = ZonedDateTime.now().with(time);

默认情况下使用系统时区 和时钟 ,因此很难测试 - 我建议传入一个 Clock 以实现可测试性。

(Noda Time 中也有类似的东西,但略有不同。如果您需要详细信息,请告诉我。)

我强烈建议反对使用java.util.Datejust represents an instant in time并且有一个糟糕的API。

这里的重点是:

  • 使用指定格式解析文本
  • 将文本解析为表示其所传达信息的类型:一天中的某个时间
  • 将该值与另一个值相结合,该值应 仔细指定(根据时钟和时区)

所有这些都会导致清晰、可靠、可测试的代码。 (现有的 .NET 代码不符合这些要点中的任何一个,IMO。)

要解析时间,您可以按照中的说明进行操作:

String input = "5:45 PM";
DateTimeFormatter parser = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH);
LocalTime time = LocalTime.parse(input, parser);

请注意,我还使用了 java.util.Locale,因为如果您不指定它,它将使用系统的默认语言环境 - 有些语言环境可以为 AM/PM 字段使用不同的符号。使用显式语言环境可以避免这种极端情况(默认语言环境也可以更改,即使在运行时也是如此,因此最好使用显式语言环境)。

要与今天的日期结合,您需要 java.time.LocalDate(获取日期)并与 LocalTime 结合,以获得 LocalDateTime:

// combine with today's date
LocalDateTime combined = LocalDate.now().atTime(time);

然后您可以使用另一个格式化程序格式化 LocalDateTime

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
System.out.println(combined.format(fmt));

输出为:

16/08/2017 17:45


如果您想将 LocalDateTime 转换为 java.util.Date,您必须注意一些细节。

A java.util.Date 表示自 1970-01-01T00:00Z(又名 Unix 纪元)以来的毫秒数。这是一个瞬间(一个特定的时间点)。查看 this article 了解更多信息。

因此,同一个 Date 对象可以代表不同的日期或时间,具体取决于您所在的位置:想一想,现在,此时此刻,世界上的每个人都处于同一时刻(相同自 1970-01-01T00:00Z 以来的毫秒数),但世界各地的本地日期和时间不同。

一个LocalDateTime代表了"local"这个概念:它是一个日期(日、月、年)和时间(时、分、秒、纳秒),但与a没有任何关系特定时区。

同一个LocalDateTime对象可以代表不同时区的不同时刻。因此,要将其转换为 Date,您必须定义所需的时区。

一个选项是使用系统的默认时区:

// convert to system's default timezone
ZonedDateTime atDefaultTimezone = combined.atZone(ZoneId.systemDefault());
// convert to java.util.Date
Date date = Date.from(atDefaultTimezone.toInstant());

但默认值可能不同于 system/environment,并且即使在运行时也可以更改。为了不依赖它并对其进行更多控制,您可以使用显式区域:

// convert to a specific timezone
ZonedDateTime zdt = combined.atZone(ZoneId.of("Europe/London"));
// convert to java.util.Date
Date date = Date.from(zdt.toInstant());

请注意,我使用了 Europe/London。 API 使用 IANA timezones names(格式总是 Region/City,如 America/Sao_PauloEurope/Berlin)。 避免使用 3 个字母的缩写(如 CSTPST),因为它们是 ambiguous and not standard.

您可以通过调用 ZoneId.getAvailableZoneIds().

获取可用时区列表(并选择最适合您的系统的时区)

还有夏令时的极端情况(当 LocalDateTime 可以存在两次或由于 ). In this case, 而不能存在时,使用 ZonedDateTime 可以避免这个问题)。 =44=]