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.Date
,just 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_Paulo
或 Europe/Berlin
)。
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 ambiguous and not standard.
您可以通过调用 ZoneId.getAvailableZoneIds()
.
获取可用时区列表(并选择最适合您的系统的时区)
还有夏令时的极端情况(当 LocalDateTime
可以存在两次或由于 ). In this case, 而不能存在时,使用 ZonedDateTime
可以避免这个问题)。 =44=]
我是 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.Date
,just 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_Paulo
或 Europe/Berlin
)。
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 ambiguous and not standard.
您可以通过调用 ZoneId.getAvailableZoneIds()
.
还有夏令时的极端情况(当 LocalDateTime
可以存在两次或由于 ZonedDateTime
可以避免这个问题)。 =44=]