【问题标题】:Get milliseconds until midnight获取直到午夜的毫秒数
【发布时间】:2012-08-12 22:38:58
【问题描述】:

我正在创建一个想要在每晚午夜更新的 Android 小部件。我正在使用AlarmManager,我需要找出从当前时间到午夜还剩多少毫秒。这是我的代码:

AlarmManager mAlarmManager = (AlarmManager)context.getSystemService(android.content.Context.ALARM_SERVICE);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, millisecondsUntilMidnight, mSrvcPendingingIntent);

如何计算距离午夜还有多少毫秒?

提前谢谢你。

【问题讨论】:

标签: java android date milliseconds


【解决方案1】:

tl;博士

java.time.Duration
.between( now , tomorrowStart )
.toMillis()

java.time

Java 8 及更高版本带有内置的java.time 框架。这些新类取代了与 Java 捆绑在一起的旧日期时间类 (java.util.Date/.Calendar)。也取代了由同一个人开发的 Joda-Time。大部分 java.time 功能都向后移植到 Java 6 和 7,并进一步适应了 Android(见下文)。

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因区域而异。例如,Paris France 中午夜后几分钟是新的一天,而 Montréal Québec 中仍然是“昨天”。

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now( z );

我们想要获取到第二天第一刻的毫秒数,但不包括。

我们必须通过LocalDate 课程才能在一天的第一时间获得。所以在这里我们从ZonedDateTime 开始得到LocalDate,然后得到另一个ZonedDateTime。关键是在LocalDate 上调用atStartOfDay

LocalDate tomorrow = now.toLocalDate().plusDays(1);
ZonedDateTime tomorrowStart = tomorrow.atStartOfDay( z );

请注意,我们不会在 00:00:00 对一天中的时间进行硬编码。由于诸如夏令时 (DST) 之类的异常情况,一天可能会从另一个时间开始,例如 01:00:00。让 java.time 确定第一个时刻。

现在我们可以计算经过的时间。在 java.time 中,我们使用 Duration 类。 java.time 框架具有更精细的纳秒分辨率,而不是 java.util.Date 和 Joda-Time 使用的更粗略的毫秒。但是Duration 包含一个方便的getMillis 方法,正如问题所要求的那样。

Duration duration = Duration.between( now , tomorrowStart );
long millisecondsUntilTomorrow = duration.toMillis();

看到这个code run live at IdeOne.com

now.toString(): 2017-05-02T12:13:59.379-04:00[美国/蒙特利尔]

tomorrowStart.toString(): 2017-05-03T00:00-04:00[美国/蒙特利尔]

毫秒到明天:42360621


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获取 java.time 类?


乔达时间

更新:Joda-Time 项目现在位于maintenance mode,团队建议迁移到java.time 类。为了历史起见,我保留此部分完整,但建议使用上面讨论的 java.time 和 ThreeTenABP。

在 Android 中,您应该使用 Joda-Time 库而不是臭名昭著的麻烦 Java.util.Date/.Calendar 类。

Joda-Time 提供milliseconds-of-day command。但我们这里真的不需要。

相反,我们只需要一个Duration 对象来表示直到第二天第一刻的时间跨度。

时区对于确定“明天”何时开始至关重要。通常最好指定而不是隐式依赖 JVM 当前的默认时区,该时区可以随时更改。或者,如果您真的想要 JVM 的默认值,请通过调用 DateTimeZone.getDefault 明确要求,以使您的代码自记录。

可能是双线。

DateTime now = DateTime.now( DateTimeZone.forID( "America/Montreal" ) );
long milliSecondsUntilTomorrow = new Duration( now , now.plusDays( 1 ).withTimeAtStartOfDay() ).getMillis();

让我们把它分开。

DateTimeZone zone = DateTimeZone.forID( "America/Montreal" ); // Or, DateTimeZone.getDefault()
DateTime now = DateTime.now( zone );
DateTime tomorrow = now.plusDays( 1 ).withTimeAtStartOfDay();  // FYI the day does not *always* start at 00:00:00.0 time.
Duration untilTomorrow = new Duration( now , tomorrow );
long millisecondsUntilTomorrow = untilTomorrow.getMillis();

转储到控制台。

System.out.println( "From now : " + now + " until tomorrow : " + tomorrow + " is " + millisecondsUntilTomorrow + " ms." );

运行时。

从现在:2015-09-20T19:45:43.432-04:00 到明天:2015-09-21T00:00:00.000-04:00 是 15256568 毫秒。

【讨论】:

  • Java 8 实现让您大开眼界。我一直在寻找那个。非常感谢。顺便说一句,你应该把你的答案分成一个单独的答案,因为当标题是 Joda 时很难找到。尽管 java 8 规范非常相似。
  • @ChrisHinshaw 我根据您的建议修改了此答案。将 java.time 信息移到顶部,将 Joda-Time 部分标记为已过时。
【解决方案2】:

使用下面的方式,就不用担心设置DAY_OF_MONTH了。

    long msInDay = 86400000;
    Calendar c = Calendar.getInstance();
    c.set(Calendar.HOUR_OF_DAY, 0);
    c.set(Calendar.MINUTE, 0);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MILLISECOND, 0);

    System.out.print(msInDay - (System.currentTimeMillis() - c.getTimeInMillis()));

【讨论】:

    【解决方案3】:

    使用日历计算它:

            Calendar c = Calendar.getInstance();
            c.add(Calendar.DAY_OF_MONTH, 1);
            c.set(Calendar.HOUR_OF_DAY, 0);
            c.set(Calendar.MINUTE, 0);
            c.set(Calendar.SECOND, 0);
            c.set(Calendar.MILLISECOND, 0);
            long howMany = (c.getTimeInMillis()-System.currentTimeMillis());
    

    【讨论】:

    • 您应该确保使用 Calendar.HOUR_OF_DAY 而不是 Calendar.HOUR,因为 Calendar.HOUR 基于 12 小时制。您的午夜计算可能是错误的,具体取决于您运行代码的时间。我通过观察发现了这一点。
    • Calendar.getInstance() 已设置为当前日期和时间,因此 c.setTime(now) 是不必要的。您也可以使用System.currentTimeMillis() 与您的日历值进行比较
    • 嗨,您的解决方案是否需要 onReceive 方法中的唤醒锁定方法?这:PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "任何/任何标签"); //获取锁 wl.acquire(); Log.d("widget", "在警报管理器中。接下来会出现静态方法"); //释放锁 wl.release();
    • 这将在一个月的最后一天失败,您应该使用 add(Calendar.DATE, 1) 而不是 DAY_OF_MONTH
    • @GregEnnis 不,不会的。日历实现在 ̀add` 中对 DATE、DAY_OF_MONTH、DAY_OF_YEAR 和 DAY_OF_WEEK 执行完全相同的操作(您可以查看来源)。
    【解决方案4】:

    您可以使用AlarmManager.RTC 而不是AlarmManager.ELAPSED_REALTIME,只需将日历设置为您想要的时间:

    // Create a calendar for midnight
    Calendar todayMidnight = Calendar.getInstance();
    todayMidnight.add(Calendar.DATE, 1);
    todayMidnight.set(Calendar.HOUR_OF_DAY, 0);
    todayMidnight.set(Calendar.MINUTE, 0);
    todayMidnight.set(Calendar.SECOND, 0);
    
    // Create an alarm going off at midnight
    mAlarmManager.set(
       AlarmManager.RTC, 
       todayMidnight.getTimeInMillis(), 
       mSrvcPendingingIntent
    );
    

    【讨论】:

    • 嗨,您的解决方案是否需要 onReceive 方法中的唤醒锁定方法?这:PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "任何/任何标签"); //获取锁 wl.acquire(); Log.d("widget", "在警报管理器中。接下来会出现静态方法"); //释放锁 wl.release();
    • 来自文档:“只要警报接收器的 onReceive() 方法正在执行,警报管理器就会持有 CPU 唤醒锁”。所以你不需要自己处理唤醒锁。 developer.android.com/reference/android/app/AlarmManager.html
    • 你是怎么做到的?可以给我一个代码 sn-ps 吗?我认为在所有情况下都是相似的。
    • 我是怎么做的?我觉得在 Stack Overflow 上创建自己的问题可能会更好地为您服务,cmets 并不能真正胜任这项任务
    • 即使在 onReceive 中有唤醒锁定,我的警报管理器也不会更新我的小部件: PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "widgetWL"); wakeLock.acquire(); wakeLock.release()
    【解决方案5】:

    尝试以下方法:

    Calendar c = Calendar.getInstance();
    long now = c.getTimeInMillis();
    c.add(Calendar.DATE, 1);
    c.set(Calendar.HOUR_OF_DAY, 0);
    c.set(Calendar.MINUTE, 0);
    c.set(Calendar.SECOND, 0);
    c.set(Calendar.MILLISECOND, 0);
    
    long millisecondsUntilMidnight = c.getTimeInMillis() - now;
    
    AlarmManager mAlarmManager = (AlarmManager)context.getSystemService(android.content.Context.ALARM_SERVICE);
    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, millisecondsUntilMidnight, mSrvcPendingingIntent);
    

    【讨论】:

    • (c.getTimeInMillis - now) 会是负数吗?
    • 计算到最后午夜的时间。你必须增加一天(见我的回答)。
    • 我已经编辑了我的答案(一天递增一)。感谢您的警告。
    • 文档显示“SystemClock.elapsedRealtime() + 60 * 1000”将警报与 AlarmManager.ELAPSED_REALTIME 设置为未来一分钟。那么在你的例子中不应该是“SystemClock.elapsedRealtime() + millisecondsUntilMidnight”吗?
    【解决方案6】:

    这行得通吗?

    long MILLIS_IN_DAY = 86400000;
    
    long currentTime = System.currentTimeInMillis();
    
    long millisTillNow = currentTime % MILLIS_IN_DAY;
    
    long millisecondsUntilMidnight = MILLIS_IN_DAY - millisTillNow;
    

    【讨论】:

    • 哼。我认为这与现实无关(备用秒数等)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    • 2010-11-07
    • 2020-09-30
    相关资源
    最近更新 更多