【问题标题】:Getting the Time component of a Java Date or Calendar获取 Java 日期或日历的时间组件
【发布时间】:2009-09-01 16:03:09
【问题描述】:

是否有一种简单或优雅的方法来仅获取 Java 日期(或日历,对我来说真的不重要)的一天中的时间(小时/分钟/秒/毫秒)部分?我正在寻找一种单独考虑日期(年/月/日)和时间部分的好方法,但据我所知,我坚持单独访问每个字段。

我知道我可以编写自己的方法来单独获取我感兴趣的字段,但我会将其作为静态实用程序方法来执行,这很难看。另外,我知道 Date 和 Calendar 对象具有毫秒精度,但在这两种情况下我都看不到访问毫秒组件的方法。


编辑:我不清楚这一点:使用 Date::getTime() 或 Calendar::getTimeInMillis 之一对我来说并不是非常有用,因为它们返回的毫秒数纪元(由该日期或日历表示),它实际上并未将一天中的时间与其余信息分开。

@Jherico 的答案是最接近的,我认为,但绝对是我仍然必须将其融入我自己编写的方法中。这不完全是我想要的,因为它仍然在返回的毫秒值中包含小时、分钟和秒 - 尽管我可能可以让它为我的目的工作。

我仍然认为每个组件都是独立的,当然,它们不是。您可以将时间写入自任意参考日期以来的毫秒数,也可以写入与year/month/day hours:minutes:seconds.milliseconds 完全相同的时间。

这不是为了显示目的。我知道如何使用DateFormat 来制作漂亮的日期字符串。


编辑 2: 我最初的问题来自我发现自己编写的一小部分实用程序函数 - 例如:

  • 检查两个Dates 是否代表同一天的日期时间;
  • 检查某个日期是否在其他两个日期指定的范围内,但有时会包含检查,有时则不检查,具体取决于时间部分。

Joda Time 有这种功能吗?


编辑 3: @Jon 关于我的第二个要求的问题,只是为了澄清:第二个要求是使用我的 Dates 有时代表一整天的结果 - 其中时间部分并不重要全部 - 有时表示日期时间(即,IMO,对于包含 year/month/day hours:minutes:seconds:... 的事物的最准确词)。

当 Date 表示一整天时,它的时间部分为零(例如,Date 的“时间部分”是午夜),但语义规定范围检查是在结束日期包含在内完成的。因为我只是将此检查保留到 Date::before 和 Date::after,所以我必须将 1 天添加到结束日期 - 因此日期的时间部分为零时的特殊情况。

希望这没有让事情变得不那么清楚。

【问题讨论】:

  • 这是为了展示目的吗?
  • 来自文档:System.currentTimeMillis() 以毫秒为单位返回当前时间。
  • Joda 肯定会让第一个变得简单。我不确定我是否理解第二个要求。
  • wrt 编辑 2:Joda 具有 Interval 的概念,它可以测试是否包含日期或之前/之后等。它还允许直观地操作其 DateTime 对象,以便您可以轻松测试“同一天的日期时间”。
  • 我只能梦想,如果一切都在一个共同的基础(首选 2、10 或 16)中,并且整个世界都是一个单一的,那么使用日期和时间会变得多么容易时区。 :P

标签: java date time calendar


【解决方案1】:

好的,我知道这是一个可预测的答案,但是……请使用 Joda Time。它对“日期”、“瞬间”、“一天中的某个时间”等有单独的表示。它是一个比内置类 IMO 更丰富且通常更理智的 API。

如果这是您感兴趣的唯一位日期/时间操作,那么它可能有点矫枉过正......但如果您使用内置日期/时间 API 来处理任何事情重要的是,我强烈建议您尽快离开它,转而使用 Joda。

顺便说一句,您应该考虑您感兴趣的时区。Calendar 具有关联的时区,但 Date 没有(它仅代表时间的瞬间,以毫秒为单位测量Unix 时代)。

【讨论】:

  • 实际上,我从来没有听说过 Joda Time(或寻找过能提起它的东西)。我不确定是否要使用新的时间库,因为该项目的许多代码已经基于 Java 日期和日历。不过,我同意它们都是非常糟糕的 API。也许在下一个项目中......
  • +1 因为 Java 日期/时间 API 在 JDK 中获得了我对单个最差 API 和实现的投票。我相信我们所有的 Java 开发人员都记得我们问自己时的第一印象,“那么我如何在 Java 中处理日期/时间?”然后看了看Javadoc...
  • @Matt org.joda.time.base.AbstractInstant 有一个 toDate() 方法 [(joda-time.sourceforge.net/api-release/org/joda/time/base/… 可用于将 Joda 日期转换为 java.util.Date 对象,因此这两个 ( Java Date API 和 Joda)可以和谐相处。
  • 是的,您可以轻松地从 java.util.Date (或它的毫秒值;我忘记是哪个副手)创建一个 DateTime。 Joda(大部分情况下)会强迫你找出你真正感兴趣的概念,以及更恰当地处理时区等问题。
【解决方案2】:

提取一天中的时间部分应该是得到除以每天的毫秒数后剩下的毫秒数。

long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
Date now = Calendar.getInstance().getTime();
long timePortion = now.getTime() % MILLIS_PER_DAY;

或者,考虑使用 joda-time,一个功能更全面的时间库。

【讨论】:

  • 这取决于您所说的“一天中的时间部分”是什么意思 - 它非常很大程度上取决于您感兴趣的时区...
  • 这个答案假设一天有 24* 60 * 60 秒。您会惊讶地发现并非总是如此!原因是由于地球的自转周期,几个世纪以来它正在减慢(非常非常慢!)。天文测量需要调整我们的时钟。例如,在 2015 年 6 月 30 日,它在当天增加了一秒。从 1972 年到 2012 年,平均每 18 个月插入一次闰秒。 en.wikipedia.org/wiki/Leap_second
  • 如果这是一个不好的做法,那么应该怎么做呢?
  • 这一切都取决于目的......如果所有日历对象都在同一时区(例如,日落(现在)、日出(现在)和“现在”本身),而你只是想要对比一下,没问题。
【解决方案3】:

使用日历 API -

解决方案 1-

Calendar c = Calendar.getInstance();
String timeComp = c.get(Calendar.HOUR_OF_DAY)+":"+c.get(Calendar.MINUTE)+":"+c.get(Calendar.SECOND)+":"+c.get(Calendar.MILLISECOND);
System.out.println(timeComp);  

输出 - 13:24:54:212

解决方案 2-

SimpleDateFormat time_format = new SimpleDateFormat("HH:mm:ss.SSS");
String timeComp = time_format.format(Calendar.getInstance().getTime());

输出 - 15:57:25.518

【讨论】:

    【解决方案4】:

    要回答部分问题,访问毫秒组件是这样完成的:

    long mill = Calendar.getInstance().getTime();
    

    我不知道您想具体做什么,但如果用于文本输出,您可以使用 java.text.SimpleDateFormat 类。

    【讨论】:

    • +1:Calendar.getInstance().get(Calendar.DAY_OF_MONTH) 以整数形式返回。
    • 这不是毫秒组件。这将获取整个日期时间,以自纪元以​​来的毫秒数表示。
    • @PHeath,你又少了一个.getTime()
    【解决方案5】:

    您可以在 Calendar 对象上调用 getTimeInMillis() 函数来获取以毫秒为单位的时间。您可以在日历对象上调用 get(Calendar.MILLISECOND) 以获取秒的毫秒数。如果要显示来自 Date 或 Calendar 对象的时间,请使用 DateFormat 类。示例:DateFormat.getTimeInstance().format(now)。您还可以使用 SimpleDateFormat 类。

    【讨论】:

      【解决方案6】:

      要使用 Joda-Time 获取时间,请使用 org.joda.time.LocalTime 类,如本问题中所述,Joda-Time, Time without date

      至于仅在有效忽略时间的情况下比较日期,在 Joda-Time 中,在每个 DateTime 实例上调用 withTimeAtStartOfDay() 方法来设置相同的时间值。这是一些使用 Joda-Time 2.3 的示例代码,类似于我今天在另一个答案上发布的内容。

          // © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
      
          // Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 7 and earlier.
          // http://www.joda.org/joda-time/
      
          // Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
          // JSR 310 was inspired by Joda-Time but is not directly based on it.
          // http://jcp.org/en/jsr/detail?id=310
      
          // By default, Joda-Time produces strings in the standard ISO 8601 format.
          // https://en.wikipedia.org/wiki/ISO_8601
      
          // Capture one moment in time.
          org.joda.time.DateTime now = new org.joda.time.DateTime();
          System.out.println("Now: " + now);
      
          // Calculate approximately same time yesterday.
          org.joda.time.DateTime yesterday = now.minusDays(1);
          System.out.println("Yesterday: " + yesterday);
      
          // Compare dates. A DateTime includes time (hence the name).
          // So effectively eliminate the time by setting to start of day.
          Boolean isTodaySameDateAsYesterday = now.withTimeAtStartOfDay().isEqual(yesterday.withTimeAtStartOfDay());
          System.out.println("Is today same date as yesterday: " + isTodaySameDateAsYesterday);
      
          org.joda.time.DateTime halloweenInUnitedStates = new org.joda.time.DateTime(2013, 10, 31, 0, 0);
          Boolean isFirstMomentSameDateAsHalloween = now.withTimeAtStartOfDay().isEqual(halloweenInUnitedStates.withTimeAtStartOfDay());
          System.out.println("Is now the same date as Halloween in the US: " + isFirstMomentSameDateAsHalloween);
      

      【讨论】:

        【解决方案7】:

        如果您只是担心将其转换为字符串进行显示或保存,那么只需创建一个仅显示时间部分的 SimpleDateFormat,例如 new SimpleDateFormat("HH:mm:ss")。当然,日期仍然在 Date 对象中,但你不在乎。

        如果您想对其进行算术运算,例如取两个 Date 对象并在忽略日期部分的同时找出它们相隔多少秒,因此“2009-09-01 11:00:00”减去“1941-12 -07 09:00:00" 等于 2 小时,那么我认为您需要使用 Jherico 之类的解决方案:花很长时间,并在 1 天后完成模块。

        【讨论】:

          【解决方案8】:

          为什么要将它们分开?如果您打算对时间部分进行任何算术运算,您很快就会遇到麻烦。如果你抽出晚上 11:59 并添加一分钟,既然你的时间和日期是分开的,你就搞砸了——你会得到一个无效的时间和一个不正确的日期。

          如果你只是想显示它们,那么应用各种简单的日期格式应该会得到你想要的。

          如果您想操纵日期,我建议您获取长值并以此为基础。在任何时候,您都可以花很长时间并应用一种格式来轻松显示分钟/小时/秒。

          但我只是有点关心分别操纵日期和时间的概念,似乎打开了一个蠕虫罐头。 (更不用说时区问题了!)。

          我很确定这就是为什么 Java 没有简单的方法来做到这一点。

          【讨论】:

          • 幸运的是,我并没有试图分别操纵日期和时间 - 只是检查它们。
          【解决方案9】:

          在下面找到使用 Joda Time 并支持时区的解决方案。 因此,您将在 JVM 中当前配置的时区中获取日期和时间(到 currentDatecurrentTime)。

          请注意,Joda Time 不支持闰秒。因此,您可能会偏离真实值大约 26 或 27 秒。这可能要在未来 50 年内才能解决,届时累积误差将接近 1 分钟,人们会开始关心它。

          另请参阅:https://en.wikipedia.org/wiki/Leap_second

          /**
           * This class splits the current date/time (now!) and an informed date/time into their components:
           * <lu>
           *     <li>schedulable: if the informed date/time is in the present (now!) or in future.</li>
           *     <li>informedDate: the date (only) part of the informed date/time</li>
           *     <li>informedTime: the time (only) part of the informed date/time</li>
           *     <li>currentDate: the date (only) part of the current date/time (now!)</li>
           *     <li>currentTime: the time (only) part of the current date/time (now!)</li>
           * </lu>
           */
          public class ScheduleDateTime {
              public final boolean schedulable;
              public final long millis;
              public final java.util.Date informedDate;
              public final java.util.Date informedTime;
              public final java.util.Date currentDate;
              public final java.util.Date currentTime;
          
              public ScheduleDateTime(long millis) {
                  final long now = System.currentTimeMillis();
                  this.schedulable = (millis > -1L) && (millis >= now);
          
                  final TimeZoneUtils tz = new TimeZoneUtils();
          
                  final java.util.Date          dmillis   = new java.util.Date( (millis > -1L) ? millis : now );
                  final java.time.ZonedDateTime zdtmillis = java.time.ZonedDateTime.ofInstant(dmillis.toInstant(), java.time.ZoneId.systemDefault());
                  final java.util.Date          zdmillis  = java.util.Date.from(tz.tzdate(zdtmillis));
                  final java.util.Date          ztmillis  = new java.util.Date(tz.tztime(zdtmillis));
          
                  final java.util.Date          dnow   = new java.util.Date(now);
                  final java.time.ZonedDateTime zdtnow = java.time.ZonedDateTime.ofInstant(dnow.toInstant(), java.time.ZoneId.systemDefault());
                  final java.util.Date          zdnow  = java.util.Date.from(tz.tzdate(zdtnow));
                  final java.util.Date          ztnow  = new java.util.Date(tz.tztime(zdtnow));
          
                  this.millis       = millis;
                  this.informedDate = zdmillis;
                  this.informedTime = ztmillis;
                  this.currentDate  = zdnow;
                  this.currentTime  = ztnow;
              }
          }
          
          
          
          public class TimeZoneUtils {
          
              public java.time.Instant tzdate() {
                  final java.time.ZonedDateTime zdtime = java.time.ZonedDateTime.now();
                  return tzdate(zdtime);
              }
              public java.time.Instant tzdate(java.time.ZonedDateTime zdtime) {
                  final java.time.ZonedDateTime zddate = zdtime.truncatedTo(java.time.temporal.ChronoUnit.DAYS);
                  final java.time.Instant instant = zddate.toInstant();
                  return instant;
              }
          
              public long tztime() {
                  final java.time.ZonedDateTime zdtime = java.time.ZonedDateTime.now();
                  return tztime(zdtime);
               }
              public long tztime(java.time.ZonedDateTime zdtime) {
                  final java.time.ZonedDateTime zddate = zdtime.truncatedTo(java.time.temporal.ChronoUnit.DAYS);
                  final long millis = zddate.until(zdtime, java.time.temporal.ChronoUnit.MILLIS);
                  return millis;
              }
          }
          

          【讨论】:

          • 知道java.time 类(Java 8 中引入)是否考虑闰秒?
          • 新的 java.time 框架提供了在一天的最后一千秒内涂抹闰秒的规定。请参阅 JavaDoc。但这仅适用于观察 L.S. 的 JVM 实现。当前的常规实现(例如 OpenJDK 或 Oracle Java)都没有遵守 L.S;我不知道实时 Java 实现,他们可能会。
          【解决方案10】:

          tl;博士

          LocalTime lt = myUtilDate.toInstant().atZone( ZoneId.of( "America/Montreal" ) ).toLocalTime() ;
          

          避免使用旧的日期时间类

          您正在使用旧的旧日期时间类。它们既麻烦又令人困惑;避免他们。

          改为使用 java.time 类。这些取代了旧的类以及 Joda-Time 库。

          转换

          将您的java.util.Date 转换为Instant

          Instant 类代表UTC 中时间轴上的一个时刻,分辨率为nanoseconds

          Instant instant = myUtilDate.toInstant();
          

          时区

          应用时区。时区至关重要。对于任何给定的时刻,日期在全球范围内因区域而异。例如,法国巴黎午夜过后几分钟是新的一天,而魁北克蒙特利尔则是“昨天”。

          应用ZoneId 以获取ZonedDateTime 对象。

          ZoneId z = ZoneId.of( "America/Montreal" );
          ZonedDateTime zdt = instant.atZone( z );
          

          Local… 类型

          LocalDate 类表示没有时间和时区的仅日期值。同样,LocalTime 表示没有日期和时区的时间。您可以将它们视为两个组件,它们与ZoneId 一起构成ZonedDateTime。您可以从ZonedDateTime 中提取这些内容。

          LocalDate ld = zdt.toLocalDate();
          LocalTime lt = zdt.toLocalTime();
          

          字符串

          如果您的目标只是生成字符串以呈现给用户,则不需要 Local… 类型。相反,使用DateTimeFormatter 生成仅表示日期部分或时间部分的字符串。该类足够聪明,可以在生成字符串时自动localize

          指定Locale 以确定 (a) 用于翻译日期名称、月份名称等的人类语言,以及 (b) 用于决定诸如缩写、大写、标点符号等问题的文化规范.

          Locale l = Locale.CANADA_FRENCH ;  // Or Locale.US, Locale.ITALY, etc.
          
          DateTimeFormatter fDate = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( locale );
          String outputDate = zdt.format( fDate );
          
          DateTimeFormatter fTime = DateTimeFormatter.ofLocalizedTime( FormatStyle.MEDIUM ).withLocale( locale );
          String outputTime = zdt.format( fTime );
          

          关于java.time

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

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

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

          大部分 java.time 功能在ThreeTen-Backport 中向后移植到Java 6 和7,并进一步适应ThreeTenABP 中的Android(参见How to use…)。

          ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。

          【讨论】:

            猜你喜欢
            • 2012-10-23
            • 1970-01-01
            • 2018-06-18
            • 2018-04-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-03-24
            相关资源
            最近更新 更多