【发布时间】:2010-12-08 16:00:05
【问题描述】:
我需要一个函数来告诉我从午夜过去了多少秒。我目前正在使用System.currentTimeMillis(),但它给了我类似 UNIX 的时间戳。
如果我也能得到毫秒,那对我来说将是一个奖励。
【问题讨论】:
-
哪个时区?当前的默认时区、UTC 或其他时区?
我需要一个函数来告诉我从午夜过去了多少秒。我目前正在使用System.currentTimeMillis(),但它给了我类似 UNIX 的时间戳。
如果我也能得到毫秒,那对我来说将是一个奖励。
【问题讨论】:
如果您使用 Java >= 8,这很容易做到:
ZonedDateTime nowZoned = ZonedDateTime.now();
Instant midnight = nowZoned.toLocalDate().atStartOfDay(nowZoned.getZone()).toInstant();
Duration duration = Duration.between(midnight, Instant.now());
long seconds = duration.getSeconds();
如果您使用的是 Java 7 或更低版本,则必须通过日历获取从午夜开始的日期,然后进行减法。
Calendar c = Calendar.getInstance();
long now = c.getTimeInMillis();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
long passed = now - c.getTimeInMillis();
long secondsPassed = passed / 1000;
【讨论】:
Calendar 和 Joda-Time 现在都已过时。 (a) 麻烦的旧日期时间类,例如 java.util.Calendar 现在是 legacy,被 java.time 类取代。 (b) Joda-Time 项目现在位于 maintenance mode,团队建议迁移到 java.time 类。
Calendar/Date。没必要,那些遗留类是一团糟。
java.time.ZonedDateTime 没有atStartOfDay() 如您的回答所暗示的方法。 java.time.LocalDate 有。
【讨论】:
LocalTime通常表示一天中的时间。这个类只知道一般的 24 小时工作日。由于缺乏特定时区的详细信息,此类无法了解有关夏令时 (DST) 等异常情况的任何信息。例如,在美国,夏令时意味着一天可以是 23、24 或 25 小时。因此,在美国,直到午夜的时间跨度从一天到另一天可能相差 ± 1 小时。
“午夜”是一个模糊的术语,最好不要说。专注于一天的第一刻。
捕捉特定地区(时区)人们使用的挂钟时间中看到的当前时刻。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
通过调用ZonedDateTime::get 并传递ChronoField 枚举对象,计算从一天的第一刻开始经过的时间。
整个seconds。
int secondOfDay = zdt.get( ChronoField.SECOND_OF_DAY ) ;
int milliOfDay = zdt.get( ChronoField.MILLI_OF_DAY ) ;
int microOfDay = zdt.get( ChronoField.MICRO_OF_DAY ) ;
int nanoOfDay = zdt.get( ChronoField.NANO_OF_DAY ) ;
确定一天中的第一刻。
请注意,一天并不总是从 00:00:00 开始。某些时区中的某些日期可能从一天中的另一个时间开始,例如 01:00:00。始终让 java.time 使用 atStartOfDay 方法确定一天中的第一刻。
Instant then = // Represent a moment in UTC.
ZonedDateTime // Represent a moment as seen through the wall-clock time used by the people of a particular region (a time zone).
.now( // Capture the current moment. Holds up to nanosecond resolution, but current hardware computer clocks limited to microseconds for telling current time.
ZoneId.of( "Africa/Casablanca" ) // Specify the time zone. Never use 2-4 letter pseudo-zones such as `IST`, `PST`, `EST`.
) // Returns a `ZonedDateTime` object.
.toLocalDate() // Extract the date-only portion, without time-of-day and without time zone.
.atStartOfDay( // Deterimine the first moment of the day on that date in that time zone. Beware: The day does *not* always begin at 00:00:00.
ZoneId.of( "Africa/Casablanca" ) // Specify the time zone for which we want the first moment of the day on that date.
) // Returns a `ZonedDateTime` object.
.toInstant() // Adjusts from that time zone to UTC. Same moment, same point on the timeline, different wall-clock time.
;
将经过的时间表示为Duration,这是一个与时间线无关的时间跨度。
Duration // Represent a span-of-time unattached to the timeline in terms of hours-minutes-seconds.
.between( // Specify start and stop moments.
then , // Calculated in code seen above.
Instant.now() // Capture current moment in UTC.
) // Returns a `Duration` object.
.getSeconds() // Extract the total number of whole seconds accross this entire span-of-time.
Java 8 及更高版本已嵌入 java.time 框架。
通过使用ZonedDateTime 和时区,我们正在处理Daylight Saving Time (DST) 等异常情况。例如,在美国一天可以是 23、24 或 25 小时。因此,到明天的时间可能会因一天而异,相差 ±1 小时。
首先获取当前时刻。
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now( z );
现在提取仅日期部分,LocalDate,并使用该日期询问 java.time 当天开始的时间以获取我们所需的时区。不要假设一天从 00:00:00 开始。夏令时 (DST) 等异常意味着一天可能从另一个时间开始,例如 01:00:00。
ZonedDateTime todayStart = now.toLocalDate().atStartOfDay( z ); // Crucial to specify our desired time zone!
现在我们可以得到当前时刻和今天开始之间的增量。这种与时间线无关的时间跨度由Duration 类表示。
Duration duration = Duration.between( todayStart , now );
向Duration 对象询问整个时间跨度内的总秒数。
long secondsSoFarToday = duration.getSeconds();
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。
【讨论】:
获取当前时区自午夜以来秒数的最简单、最快的方法:
一次性设置:
static final long utcOffset = TimeZone.getDefault().getOffset(System.currentTimeMillis());
如果你使用 Apache Commons,你可以使用 DateUtils.DAY_IN_MILLIS,否则定义:
static final long DAY_IN_MILLIS = 24 * 60 * 60 * 1000;
然后,只要你需要时间......:
int seconds = (int)((System.currentTimeMillis() + utcOffset) % DateUtils.DAY_IN_MILLIS / 1000);
请注意,如果您的程序有可能运行足够长的时间,并且夏令时发生更改...
【讨论】:
DateUtils.DAY_IN_MILLIS 常量而不是计算它。
使用 JodaTime 你可以调用:
int seconds = DateTime.now().secondOfDay().get();
int millis = DateTime.now().millisOfDay().get();
【讨论】:
millisOfDay 在java.time 中在哪里?
ZonedDateTime::get( ChronoField )
如果您收到有关日历受到保护的错误,请使用 getTime().getTime() 而不是 getTimeInMillis()。记住你的导入:
import java.util.*;
将在您调试时将它们全部包含在内:
Calendar now = Calendar.getInstance();
Calendar midnight = Calendar.getInstance();
midnight.set(Calendar.HOUR_OF_DAY, 0);
midnight.set(Calendar.MINUTE, 0);
midnight.set(Calendar.SECOND, 0);
midnight.set(Calendar.MILLISECOND, 0);
long ms = now.getTime().getTime() - midnight.getTime().getTime();
totalMinutesSinceMidnight = (int) (ms / 1000 / 60);
【讨论】:
(System.currentTimeMillis()/1000) % (24 * 60 * 60)
【讨论】:
和@secmask 一样,如果您需要距离格林威治标准时间午夜的毫秒数,请尝试
long millisSinceGMTMidnight = System.currentTimeMillis() % (24*60*60*1000);
【讨论】: