考虑日期时的时差
Hours difference while considering the date
我需要从数据库中提取日期字段并将其存储在 VO 中。我如何比较两个日期的时差。
例如:
假设 date1 = 01-SEP-17 10:00:00
和 date2 = 05-SEP-17 12:00:00
。我需要比较两个日期并执行一些操作,例如:
if(hours>10){
//do something
}
if(hours<10){
//do something else
}
我只能将小时数 (date2-date1) 计算为 2,但如何在计算小时数时也考虑日期?
我现在的密码:
Date dateA = someVO.getDate();
long date = System.currentTimeMillis();
SimpleDateFormat df = new SimpleDateFormat("dd-MM-YY HH:mm:ss");
Date date1 = new Date(date);
Date date2 = df.parse(dateA.toString());
long date1Hours = date1.getHours();
long date2Hours = date2.getHours();
long dateDiff = date1Hours-date2Hours;
if(dateDiff>10){
//something
}
else if(dateDiff<10){
//something else
}
使用 Java 8:
中添加的新 Java-Time API 很容易做到
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("dd-MMM-yy HH:mm:ss")
.toFormatter(Locale.US);
LocalDateTime date1 = LocalDateTime.parse("01-SEP-17 10:00:00", fmt);
LocalDateTime date2 = LocalDateTime.parse("05-SEP-17 12:00:00", fmt);
long hours = ChronoUnit.HOURS.between(date1, date2);
System.out.println(hours);
输出
98
你基本上已经得到了以毫秒为单位的时间。您总是可以直接比较毫秒数。
long tenHoursInMillis = 36000000;
long dateVOMillis = someVO.getDate().getTime();
long dateSysMillis = System.currentTimeMillis();
if(dateSysMillis - dateAMillis > tenHoursInMillis) {
// do something
}
else if(dateSysMillis - dateAMillis < tenHoursInMillis) {
// do something else
}
// do something when they're equal
首先你需要更改SimpleDateFormat
中使用的模式,并使用java.util.Locale
指定月份名称为英文(否则它使用系统默认语言环境,而不是保证永远是英文)。
然后你得到每个 Date
的相应毫秒值,计算它们之间的差异并将其转换为小时,使用 java.util.concurrent.TimeUnit
:
SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yy HH:mm:ss", Locale.ENGLISH);
Date date1 = df.parse("01-SEP-17 10:00:00");
Date date2 = df.parse("05-SEP-17 12:00:00");
// get the difference in hours
long dateDiff = TimeUnit.MILLISECONDS.toHours(date2.getTime() - date1.getTime());
dateDiff
将是 98
。
如果要与当前日期进行比较,只需使用new Date()
。
夏令时问题
这种方法有一个问题。尽管在一年中的大部分时间里没有什么不同,但可能会因 Daylight Saving Time 变化而有所不同。
默认情况下,SimpleDateFormat
使用 JVM 默认时区。如果在这两个日期之间有夏令时转换(或只是偏移量更改),结果可能会有所不同。
示例:在 Africa/Windhoek
timezone,2017 年 9 月 3 日rd,凌晨 2 点,时钟向前移动了 1 小时,从凌晨 2 点到凌晨 3 点(和偏移从 +01:00
更改为 +02:00
)。这意味着,在那一天,该时区不存在凌晨 2 点到 2:59 AM 之间的所有当地时间(就像他们“跳过”了这个小时)。
所以,如果 JVM 默认时区是 Africa/Windhoek
,那么使用上面的代码时差将是 97 小时(而不是 98)。
即使您的 JVM 默认时区不是 Africa/Windhoek
,这仍然可能发生,具体取决于所涉及的时区和日期。
不仅如此,还有默认时区 。最好指定您正在使用的时区,而不是仅仅依赖默认时区。
您无法避免 DST 影响(除非您使用 UTC),但至少您可以选择要使用的时区,而不是依赖于系统默认值(可以在不通知的情况下更改)。
可以在格式化程序中设置时区,因此在分析所有日期时都会考虑到该时区。在下面的示例中,我使用的是 Europe/London
,当然您可以更改为最适合您的情况:
// set Europe/London timezone in the SimpleDateFormat
df.setTimeZone(TimeZone.getTimeZone("Europe/London"));
现在所有解析的日期都将被视为伦敦时区(但提醒仍会考虑 DST 影响 - 优点是您知道您正在使用的时区并且 JVM 默认值的任何更改都不会'不要让你的代码突然开始给出不同的和意想不到的结果)。
始终使用 IANA timezones names(始终采用 Continent/City
格式,例如 America/Sao_Paulo
或 Europe/Berlin
)。
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 ambiguous and not standard.
您可以使用 TimeZone.getAvailableIDs()
获取所有时区的列表 - 然后您可以选择最适合您的情况。
如果不想考虑夏令时的影响,可以使用TimeZone.getTimeZone("UTC")
——因为UTC is a standard without DST changes.
Java新Date/TimeAPI
旧的 classes(Date
、Calendar
和 SimpleDateFormat
)有 lots of problems and design issues,它们正在被新的 APIs.
如果您使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.
如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it ).
下面的代码适用于两者。
唯一的区别是 包名称 (在 Java 8 中是 java.time
而在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp
),但是 classes 和方法 names 是相同的。
首先您需要解析输入(使用 DateTimeFormatter
)并指定它们所在的时区。由于日期也有时区,我使用 ZonedDateTime
,这是这种情况的最佳选择。
然后您可以使用 ChronoUnit
轻松计算小时差。在下面的例子中,我也以伦敦时区为例:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// case insensitive for month name in all caps
.parseCaseInsensitive()
// date/time pattern
.appendPattern("dd-MMM-yy HH:mm:ss")
// use English locale for month name
.toFormatter(Locale.ENGLISH)
// set a timezone
.withZone(ZoneId.of("Europe/London"));
// parse the dates
ZonedDateTime z1 = ZonedDateTime.parse("01-SEP-17 10:00:00", fmt);
ZonedDateTime z2 = ZonedDateTime.parse("05-SEP-17 12:00:00", fmt);
// calculate the difference in hours
long diffHours = ChronoUnit.HOURS.between(z1, z2);
如果要使用 UTC,只需将 ZoneId
更改为 ZoneOffset.UTC
常量即可。如果要与当前日期进行比较,只需使用:
// use the same ZoneId used in the formatter if you want to consider DST effects
ZonedDateTime.now(ZoneId.of("Europe/London"));
转化次数to/from日期
如果您仍然需要使用 java.util.Date
,可以将 from/to 转换为新的 API。在 Java 8 中,您可以使用本地方法,在 Java <=7 中,ThreeTen Backport 具有 org.threeten.bp.DateTimeUtils
class.
要将 Date
转换为新的 classes:
Date date = // java.util.Date
// convert to zoneddatetime (java 8)
ZonedDateTime z = date.toInstant().atZone(ZoneId.of("Europe/London"));
// convert to zoneddatetime (java 7 ThreeTen Backport)
ZonedDateTime z = DateTimeUtils.toInstant(date).atZone(ZoneId.of("Europe/London"));
要将 ZonedDateTime
转换回日期:
// convert to zoneddatetime (java 8)
Date date = Date.from(z.toInstant());
// convert to zoneddatetime (java 7 ThreeTen Backport)
Date date = DateTimeUtils.toDate(z.toInstant());
我需要从数据库中提取日期字段并将其存储在 VO 中。我如何比较两个日期的时差。
例如:
假设 date1 = 01-SEP-17 10:00:00
和 date2 = 05-SEP-17 12:00:00
。我需要比较两个日期并执行一些操作,例如:
if(hours>10){
//do something
}
if(hours<10){
//do something else
}
我只能将小时数 (date2-date1) 计算为 2,但如何在计算小时数时也考虑日期?
我现在的密码:
Date dateA = someVO.getDate();
long date = System.currentTimeMillis();
SimpleDateFormat df = new SimpleDateFormat("dd-MM-YY HH:mm:ss");
Date date1 = new Date(date);
Date date2 = df.parse(dateA.toString());
long date1Hours = date1.getHours();
long date2Hours = date2.getHours();
long dateDiff = date1Hours-date2Hours;
if(dateDiff>10){
//something
}
else if(dateDiff<10){
//something else
}
使用 Java 8:
中添加的新 Java-Time API 很容易做到DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("dd-MMM-yy HH:mm:ss")
.toFormatter(Locale.US);
LocalDateTime date1 = LocalDateTime.parse("01-SEP-17 10:00:00", fmt);
LocalDateTime date2 = LocalDateTime.parse("05-SEP-17 12:00:00", fmt);
long hours = ChronoUnit.HOURS.between(date1, date2);
System.out.println(hours);
输出
98
你基本上已经得到了以毫秒为单位的时间。您总是可以直接比较毫秒数。
long tenHoursInMillis = 36000000;
long dateVOMillis = someVO.getDate().getTime();
long dateSysMillis = System.currentTimeMillis();
if(dateSysMillis - dateAMillis > tenHoursInMillis) {
// do something
}
else if(dateSysMillis - dateAMillis < tenHoursInMillis) {
// do something else
}
// do something when they're equal
首先你需要更改SimpleDateFormat
中使用的模式,并使用java.util.Locale
指定月份名称为英文(否则它使用系统默认语言环境,而不是保证永远是英文)。
然后你得到每个 Date
的相应毫秒值,计算它们之间的差异并将其转换为小时,使用 java.util.concurrent.TimeUnit
:
SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yy HH:mm:ss", Locale.ENGLISH);
Date date1 = df.parse("01-SEP-17 10:00:00");
Date date2 = df.parse("05-SEP-17 12:00:00");
// get the difference in hours
long dateDiff = TimeUnit.MILLISECONDS.toHours(date2.getTime() - date1.getTime());
dateDiff
将是 98
。
如果要与当前日期进行比较,只需使用new Date()
。
夏令时问题
这种方法有一个问题。尽管在一年中的大部分时间里没有什么不同,但可能会因 Daylight Saving Time 变化而有所不同。
默认情况下,SimpleDateFormat
使用 JVM 默认时区。如果在这两个日期之间有夏令时转换(或只是偏移量更改),结果可能会有所不同。
示例:在 Africa/Windhoek
timezone,2017 年 9 月 3 日rd,凌晨 2 点,时钟向前移动了 1 小时,从凌晨 2 点到凌晨 3 点(和偏移从 +01:00
更改为 +02:00
)。这意味着,在那一天,该时区不存在凌晨 2 点到 2:59 AM 之间的所有当地时间(就像他们“跳过”了这个小时)。
所以,如果 JVM 默认时区是 Africa/Windhoek
,那么使用上面的代码时差将是 97 小时(而不是 98)。
即使您的 JVM 默认时区不是 Africa/Windhoek
,这仍然可能发生,具体取决于所涉及的时区和日期。
不仅如此,还有默认时区
您无法避免 DST 影响(除非您使用 UTC),但至少您可以选择要使用的时区,而不是依赖于系统默认值(可以在不通知的情况下更改)。
可以在格式化程序中设置时区,因此在分析所有日期时都会考虑到该时区。在下面的示例中,我使用的是 Europe/London
,当然您可以更改为最适合您的情况:
// set Europe/London timezone in the SimpleDateFormat
df.setTimeZone(TimeZone.getTimeZone("Europe/London"));
现在所有解析的日期都将被视为伦敦时区(但提醒仍会考虑 DST 影响 - 优点是您知道您正在使用的时区并且 JVM 默认值的任何更改都不会'不要让你的代码突然开始给出不同的和意想不到的结果)。
始终使用 IANA timezones names(始终采用 Continent/City
格式,例如 America/Sao_Paulo
或 Europe/Berlin
)。
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 ambiguous and not standard.
您可以使用 TimeZone.getAvailableIDs()
获取所有时区的列表 - 然后您可以选择最适合您的情况。
如果不想考虑夏令时的影响,可以使用TimeZone.getTimeZone("UTC")
——因为UTC is a standard without DST changes.
Java新Date/TimeAPI
旧的 classes(Date
、Calendar
和 SimpleDateFormat
)有 lots of problems and design issues,它们正在被新的 APIs.
如果您使用 Java 8,请考虑使用 new java.time API. It's easier, less bugged and less error-prone than the old APIs.
如果您使用 Java <= 7,您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it
下面的代码适用于两者。
唯一的区别是 包名称 (在 Java 8 中是 java.time
而在 ThreeTen Backport(或 Android 的 ThreeTenABP)中是 org.threeten.bp
),但是 classes 和方法 names 是相同的。
首先您需要解析输入(使用 DateTimeFormatter
)并指定它们所在的时区。由于日期也有时区,我使用 ZonedDateTime
,这是这种情况的最佳选择。
然后您可以使用 ChronoUnit
轻松计算小时差。在下面的例子中,我也以伦敦时区为例:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// case insensitive for month name in all caps
.parseCaseInsensitive()
// date/time pattern
.appendPattern("dd-MMM-yy HH:mm:ss")
// use English locale for month name
.toFormatter(Locale.ENGLISH)
// set a timezone
.withZone(ZoneId.of("Europe/London"));
// parse the dates
ZonedDateTime z1 = ZonedDateTime.parse("01-SEP-17 10:00:00", fmt);
ZonedDateTime z2 = ZonedDateTime.parse("05-SEP-17 12:00:00", fmt);
// calculate the difference in hours
long diffHours = ChronoUnit.HOURS.between(z1, z2);
如果要使用 UTC,只需将 ZoneId
更改为 ZoneOffset.UTC
常量即可。如果要与当前日期进行比较,只需使用:
// use the same ZoneId used in the formatter if you want to consider DST effects
ZonedDateTime.now(ZoneId.of("Europe/London"));
转化次数to/from日期
如果您仍然需要使用 java.util.Date
,可以将 from/to 转换为新的 API。在 Java 8 中,您可以使用本地方法,在 Java <=7 中,ThreeTen Backport 具有 org.threeten.bp.DateTimeUtils
class.
要将 Date
转换为新的 classes:
Date date = // java.util.Date
// convert to zoneddatetime (java 8)
ZonedDateTime z = date.toInstant().atZone(ZoneId.of("Europe/London"));
// convert to zoneddatetime (java 7 ThreeTen Backport)
ZonedDateTime z = DateTimeUtils.toInstant(date).atZone(ZoneId.of("Europe/London"));
要将 ZonedDateTime
转换回日期:
// convert to zoneddatetime (java 8)
Date date = Date.from(z.toInstant());
// convert to zoneddatetime (java 7 ThreeTen Backport)
Date date = DateTimeUtils.toDate(z.toInstant());