【问题标题】:DateTime Manipulation in C# vs JavaC# 与 Java 中的日期时间操作
【发布时间】:2018-01-24 14:08:06
【问题描述】:

我是 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);

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

【问题讨论】:

  • 使用 SimpleDateForma classt 处理java中的各种格式。
  • @AMallal:理想情况下,不要使用java.time
  • @Jon skeet:我的错

标签: java c# datetime date-parsing


【解决方案1】:

要解析时间,可以按照@Jon Skeet's answer中的说明进行:

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,则必须注意一些细节。

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

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

LocalDateTime 代表“本地”概念:它是日期(日、月和年)和时间(小时、分钟、秒和纳秒),但与特定时区没有任何关系。

同一个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());

但默认值可能因系统/环境而异,也可以更改,即使在运行时也是如此。为了不依赖它并对其进行更多控制,您可以使用显式区域:

// 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)。 避免使用三个字母的缩写(如CSTPST),因为它们是ambiguous and not standard

您可以致电ZoneId.getAvailableZoneIds() 获取可用时区列表(并选择最适合您系统的时区)。

还有夏令时的极端情况(LocalDateTime 可以存在两次,或者由于overlaps and gaps 而不能存在)。在这种情况下,Jon's solution 使用 ZonedDateTime 可以避免这个问题。

【讨论】:

【解决方案2】:

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

当然——事实上,在 .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 以提高可测试性。

(在野田时间也有类似的东西,但略有不同。如果您需要详细信息,请告诉我。)

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

这里的重点是:

  • 以明确指定的格式解析文本
  • 将文本解析为代表其所传达信息的类型:一天中的某个时间
  • 将该值与另一个应该仔细指定的值结合起来(就时钟和时区而言)

所有这些都会产生清晰、可靠、可测试的代码。 (而且现有的 .NET 代码不符合这些要点,IMO。)

【讨论】:

  • 等等,乔恩,这并没有给我 .NET 代码的等价物。我想要今天的日期,并将时间修改为我想要的字符串值。所以今天“16/08/2017 15:45”最好作为日期对象,这样我就可以对它执行操作。当转储到控制台时,这只会给出“17:45”。
  • @MoonKnight:Date 对象是即时。你想在哪个时区? Date 几乎总是使用错误的类型。我会再修改一下我的答案。
  • @MoonKnight:扩展了不少。
  • 不错。我只想要一张快照,但我同意你关于日期的所有观点。非常感谢您花时间写一个详细的回复,非常感谢。我接受你所说的一切。再次感谢。
猜你喜欢
  • 2013-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
  • 2019-09-06
  • 2013-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多