CodeGym /Java 博客 /随机的 /如何不迷失在时间里:DateTime 和 Calendar
John Squirrels
第 41 级
San Francisco

如何不迷失在时间里:DateTime 和 Calendar

已在 随机的 群组中发布
你好!今天我们将开始使用一种以前从未遇到过的新数据类型,即日期。 如何不迷失在时间里:DateTime 和 Calendar - 1我认为我不需要解释什么是约会。:) 原则上,我们可以将当前日期和时间存储在普通的 Java String 中。

public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
但是这种方法有很多缺点。该类String旨在处理文本,其方法适用于此任务。如果我们需要以某种方式操纵日期(例如,添加 2 小时),则String效果不佳。或者如果我们想在程序编译时显示当前的日期和时间。String在这里也无济于事:当您编写代码并运行它时,时间已经改变并且控制台将显示错误信息。这就是为什么 Java 的创建者提供了几个类来处理日期和时间的原因。其中第一个是java.util.Date

日期类

我们指定了它的全名,因为另一个 Java 包有这个java.sql.Date类。不要混淆它们!您需要了解的第一件事是,它将日期存储为自 1970 年 1 月 1 日以来经过的毫秒数。这个时间系统甚至有自己的名称:“ Unix-time ” 一个相当有趣的方法,不会你不同意吗?:) 第二件值得记住的事情是:如果Date使用默认构造函数创建对象,则结果表示创建对象时的当前日期和时间。还记得我们说过用 a 表示的日期String很难完成这样的任务吗?该类Date轻松处理它。

public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
多次运行此代码,您会看到时间反复变化。:) 这是可能的,因为时间是以毫秒为单位存储的:它们是极小的时间单位,因此结果非常准确。该类Date的另一个构造函数:你可以传递从1970年1月1日00:00到所需日期的确切毫秒数,并创建一个相应的日期对象:

public class Main {
   public static void main(String[] args) {

       Date date = new Date(1212121212121L);
       System.out.println(date);
   }
}
控制台输出: Fri May 30 04:20:12 GMT 2008 我们得到 2008 年 5 月 30 日。“Fri”表示星期几(星期五,duh),GMT 是时区(格林威治标准时间)。毫秒作为longs 传递,因为毫秒数通常不适合int. 那么,我们可能需要执行哪些日期操作?嗯,最明显的当然是比较。确定一个日期是在另一个日期之前还是之后。这可以通过多种方式完成。例如,您可以调用该Date.getTime()方法,它返回自 1970 年 1 月 1 日午夜以来经过的毫秒数。只需在两个 Date 对象上调用它并比较结果:

public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "date1 is later than date2" : "date1 is earlier than date2");
   }
}
输出: date1 早于 date2 但还有一种更方便的方法,即使用 Date 类提供的特殊方法:before(),after()equals()。它们都返回一个布尔值。该before()方法检查我们的日期是否早于作为参数传递的日期:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.before(date2));
   }
}
控制台输出: true 同样,该after()方法检查我们的日期是否晚于作为参数传递的日期:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
控制台输出: false 在我们的示例中,我们“让程序休眠”2 秒,这样两个日期就可以保证不同。在快速计算机上,创建 和 之间的时间date1可能date2小于一毫秒,导致 和 都before()返回after()false。但在这种情况下,该equals()方法将返回 true!毕竟,它会比较每个日期自 1970 年 1 月 1 日 00:00 以来的毫秒数。仅当对象与毫秒匹配时,它们才被认为是相等的:

public static void main(String[] args) {

   Date date1 = new Date();
   Date date2 = new Date();

   System.out.println(date1.getTime());
   System.out.println(date2.getTime());

   System.out.println(date1.equals(date2));
}
这是您需要注意的另一件事。Date如果您在Oracle网站上打开该类的文档,您会看到它的许多方法和构造函数已被标记为Deprecated(即不推荐使用)。以下是 Java 的创建者对已弃用的部分类的看法:
“不建议程序员使用带有 @Deprecated 注释的程序元素,通常是因为它很危险,或者因为有更好的选择。”
这并不意味着这些方法根本不能使用。如果您尝试在 IDE 中使用已弃用的方法运行代码,它很可能会起作用。例如,考虑已弃用的Date.getHours()方法,它返回与对象关联的小时数Date

public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
如果您在 14:21(下午 2:21)启动代码,它将显示数字 14。如您所见,已弃用的方法已被划掉,但它仍然有效。不会删除这些方法是为了不破坏使用它们的大量现有代码。换句话说,这些方法既没有被“破坏”也没有被“移除”。根本不推荐使用它们,因为有更方便的替代方法。顺便说一下,文档特别提到了这个替代方案:
如何不迷失在时间里:DateTime 和 Calendar - 2
该类的大部分Date方法已移至改进和扩展Calendar类。接下来我们将熟悉该类。:)

日历类

JDK 1.1 引入了一个新类:Calendar. 它使在 Java 中处理日期比以前更容易。Calendar我们将使用的类的唯一实现是GregorianCalendar类。它执行世界上大多数国家都遵守的公历。它的主要优点是它可以以更方便的格式处理日期。例如,它可以:
  • 在当前日期上加一个月或一天
  • 检查年份是否为闰年;
  • 返回日期的各个组成部分(例如,从整个日期中提取月份数)
  • 它还包含一个非常方便的常量系统(我们将在下面看到其中的许多)。
该类的另一个重要增强Calendar是它的Calendar.ERA常量:您可以指示共同时代(BC - 基督之前)或共同时代(AD - Anno Domini)之前的日期。让我们用例子来看看这一切。让我们创建一个calendar日期为 2017 年 1 月 25 日的对象:

public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
Calendar类(以及与Date此相关的类)中,月份从零开始,因此我们将数字 0 作为第二个参数传递。在课堂上工作时Calendar,重要的是要了解这只是一个日历,而不是一个单独的日期。 如何不迷失在时间里:DateTime 和 Calendar - 3 日期只是指示特定时间间隔的几个数字。日历是一个完整的系统,可以让你用日期做很多事情。:) 如果您尝试显示Calendar对象,这将非常明显: 输出: java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="欧洲/伦敦",offset=0,dstSavings=0,useDaylight= false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=25,DAY_OF_YEAR=?,DAY_OF_WEEK=? ,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=?,ZONE_OFFSET=?,DST_OFFSET=?] 看看你得到了多少信息 !一个日历有一堆普通日期没有的属性,并且全部显示出来(这就是该方法toString()在类中的工作Calendar)。如果您只需要从日历中获取一个简单的日期,即一个Date对象,请使用Calendar.getTime()方法(名称不是最合乎逻辑的,但你能做什么?):

public static void main(String[] args) {

   Calendar calendar = new GregorianCalendar(2017, 0 , 25);
   Date date = calendar.getTime();
   System.out.println(date);
}
输出: Wed Jan 25 00:00:00 GMT 2017 现在我们将日历“缩减”为一个普通日期。让我们更进一步。除了按数字指定月份外,您还可以使用Calendar类的常量字段值。这些常量是类的静态字段,Calendar具有无法更改的预设值。这实际上是一个更好的选择,因为使用它们可以提高代码的可读性。

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY是表示一年中月份的常量之一。使用这些命名常量,没有人会忘记,例如,数字 3 表示四月,而不是我们喜欢称之为三月的第三个月。只需编写Calendar.APRIL即可。:) 所有日历字段(数字、月份、分钟、秒等)都可以使用该set()方法单独指定。这种方法非常方便,因为Calendar类中的每个字段都有一个常量,生成的代码非常容易阅读。 在上一个示例中,我们创建了一个日期,但没有为其设置时间。让我们把时间定在 19:42:12

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar();
   calendar.set(Calendar.YEAR, 2017);
   calendar.set(Calendar.MONTH, 0);
   calendar.set(Calendar.DAY_OF_MONTH, 25);
   calendar.set(Calendar.HOUR_OF_DAY, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println(calendar.getTime());
}
输出: Wed Jan 25 19:42:12 GMT 2017 我们调用该set()方法,传递一个常量(取决于我们要更改的字段)和该字段的新值。事实证明,这种set()方法是一种“超级设置器”,它知道如何不仅为一个字段设置值,而且为多个字段设置值。:) 该类Calendar使用该add()方法来添加和减去值。您传入要更改的字段和一个数字(确切地说是要从当前值中添加/减去多少)。例如,让我们获取比创建日期早 2 个月的日期:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.add(Calendar.MONTH, -2); // To subtract, pass a negative number
   System.out.println(calendar.getTime());
}
输出: Fri Nov 25 19:42:12 GMT 2016 很好!我们在 2 个月前得到了约会。这不仅导致月份发生变化:年份也从 2017 年变为 2016 年。当然,在转换日期时,会自动计算当前年份,无需您手动跟踪。但如果出于某种原因您需要禁用此行为,您可以这样做。该方法可以在不影响剩余值的情况下roll()添加和减去值。例如,像这样:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(calendar.getTime());
}
我们做了与上一个示例完全相同的事情:我们从当前日期开始计算了 2 个月。但现在代码做了一些不同的事情:月份从一月变成了十一月,但年份保持不变——2017 年!输出: 2017 年 11 月 25 日星期六 10:42:12 GMT 继续前进。正如我们上面所说,我们可以Calendar单独获取所有字段。我们使用以下get()方法执行此操作:

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println("Year: " + calendar.get(Calendar.YEAR));
   System.out.println("Month: " + calendar.get(Calendar.MONTH));
   System.out.println("Week in the month: " + calendar.get(Calendar.WEEK_OF_MONTH));// Week in this month?

   System.out.println("Day: " + calendar.get(Calendar.DAY_OF_MONTH));

   System.out.println("Hours: " + calendar.get(Calendar.HOUR));
   System.out.println("Minutes: " + calendar.get(Calendar.MINUTE));
   System.out.println("Seconds: " + calendar.get(Calendar.SECOND));
   System.out.println("Milliseconds: " + calendar.get(Calendar.MILLISECOND));

}
输出: 年:2017 月:0 月中的星期:5 日:25 时:10 分:42 秒:12 毫秒:0 那么, 除了Calendar班级的“super-setter”,还有一个“super-getter” ”。:) 当然,本课程的另一个有趣方面是使用时代。要创建“BC”日期,您需要使用Calendar.ERA字段 例如,让我们为坎尼战役创建一个日期,汉尼拔在该战役中击败了罗马军队。这发生在公元前 216 年 8 月 2 日:

public static void main(String[] args) {
   GregorianCalendar cannae = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannae.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("MMM dd, yyy GG");
   System.out.println(df.format(cannae.getTime()));
}
这里我们使用SimpleDateFormat类以我们更容易理解的格式打印日期(字母“GG”表示我们希望显示时代)。输出: 公元前 216 年 8 月 2 日。 该类Calendar有更多的方法和常量。您可以在文档中阅读有关它们的信息。如果不喜欢这种日期格式 Sat Nov 25 10:42:12 GMT 2017 ,那么您可以使用SimpleDateFormat它轻松地使其成为您想要的样子。

public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy");
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(dateFormat.format(calendar.getTime()));
}
输出: 2017 年 11 月 25 日,星期六, 好多了,不是吗?:)
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION