Java 日期时间

标签 : Java基础



Date

java.util.Date对象表示一个精确到毫秒的瞬间; 但由于Date从JDK1.0起就开始存在了,历史悠久,而且功能强大(既包含日期,也包含时间),所以他的大部分构造器/方法都已Deprecated,因此就不再推荐使用(如果贸然使用的话,可能会出现性能/安全方面的问题);下面我仅介绍它还剩下的为数不多的几个方法(这些方法的共同点是Date与毫秒值的转换):

  • 构造器

    • Date(): 在底层调用System.currentTimeMillis()作为日期参数.
    • Date(long date): 根据指定的long整数(从1970-1-1 00:00:00以来经过的毫秒数)来生成Date对象.
  • 方法

    • boolean after(Date when): 测试this日期是否在指定日期when之后;
    • boolean before(Date when): 测试this日期是否在指定日期when之前;
    • long getTime(): 获取从1979-01-01 00:00:00 到Date对象之间经过的毫秒值;
    • void setTime(long time): 设置时间,time含义上同.
/**
 * Created by jifang on 15/12/30.
 */
public class DateTest {

    @Test
    public void test() {
        Date dateBefore = new Date();
        Date dateAfter = new Date(System.currentTimeMillis() + 1);

        System.out.println("before: " + dateBefore.getTime());
        System.out.println("after: " + dateAfter.getTime());
        System.out.println(dateBefore.before(dateAfter));
        System.out.println(dateAfter.after(dateBefore));

        dateBefore.setTime(System.currentTimeMillis());
        System.out.println(dateBefore.getTime());
        System.out.println(dateBefore.before(dateAfter));
    }
}

Calendar

由于Date存在缺陷,所以JDK又提供了java.util.Calendar来处理日期和时间.Calendar是一个抽象类,是所有日历类的模板,因此,我们可以继承Calendar来实现其他的历法(比如阴历);

Java中提供了一种Calendar的默认实现java.util.GregorianCalendar格里高利日历(其实JDK还默认提供了一款日本历法java.util.JapaneseImperialCalendar),也就是我们所说的公历. 使用Calendar.getInstance();获取的就是默认的GregorianCalendar,getInstance()方法的内部会调用cal = new GregorianCalendar(zone, aLocale);来生成一个格里高利日历实例.

  • Calendar还可以和Date自由转换.
public class CalendarTest {

    @Test
    public void test() {
        Calendar calendar = Calendar.getInstance();
        Date date = calendar.getTime();
        Calendar newCalendar = Calendar.getInstance();
        newCalendar.setTime(date);

        System.out.println(calendar.get(Calendar.DATE));
    }
}
  • Calendar类提供了大量访问/修改日期/时间的方法, 常用的方法如下:
Method Description
void add(int field, int amount) Adds or subtracts the specified amount of time to the given calendar field, based on the calendar’s rules.
int get(int field) Returns the value of the given calendar field.
int getActualMaximum(int field) Returns the maximum value that the specified calendar field could have, given the time value of this Calendar.
int getActualMinimum(int field) Returns the minimum value that the specified calendar field could have, given the time value of this Calendar.
void roll(int field, int amount) Adds the specified (signed) amount to the specified calendar field without changing larger fields.
void set(int field, int value) Sets the given calendar field to the given value.
void set(int year, int month, int date) Sets the values for the calendar fields YEAR, MONTH, and DAY_OF_MONTH.
void set(int year, int month, int date, int hourOfDay, int minute, int second) Sets the values for the fields YEAR, MONTH, DAY_OF_MONTH, HOUR, MINUTE, and SECOND.
void setTimeInMillis(long millis) Sets this Calendar’s current time from the given long value.
long getTimeInMillis() Returns this Calendar’s time value in milliseconds.

上面的很多方法都需要一个int类型的field参数, field是Calendar类的类变量, 如:Calendar.DATE Calendar.MONTH Calendar.HOUR Calendar.DAY_OF_WEEK, 但需要指出的是Calendar.MONTH月份的起始值不是1, 而是0(一月:0, 二月:1 …), Calendar.DAY_OF_WEEK代表的星期, 起始值是周日(周日:1, 周一:2 …)(其他细节请参考JDK文档).

注意:

  • 如果Calendar没有设置相关的值, 就以当前系统时间来设置.
  • add(int field, int amount)的功能非常强大, 如果需要增加某字段, 则让amount为正数, 如果要减少某字段的值, 让amount为负数. 且当超出他的允许范围时, 会发生进位.
  • roll()的含义与用法和add()的类似,但是当被修改的字段超出它允许的范围时, 他不会进位.
  • set(int field, int value)方法具有延迟修改的功能:他内部设置了一个成员变量,以指示日历字段field已经被修改,但是该Calendar所代表的时间不会立即修改, 他会直到下次调用get/getTime/getTimeInMillis/add/roll时才会重新计算日历时间.
public int get(int field)
{
    complete();
    return internalGet(field);
}
public long getTimeInMillis() {
    if (!isTimeSet) {
        updateTime();
    }
    return time;
}
  • 测试
public class CalendarTest {

    @Test
    public void test() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2011, Calendar.JULY, 31);
        calendar.set(Calendar.MONTH, Calendar.SEPTEMBER);
        // 将下面注释放开, 再测试
        // System.out.println(calendar.get(Calendar.MONTH) + 1 + "月" + calendar.get(Calendar.DATE) + "日");
        calendar.set(Calendar.DATE, 5);
        System.out.println(calendar.get(Calendar.MONTH) + 1 + "月" + calendar.get(Calendar.DATE) + "日");
    }
}

日期格式化

完成字符串与日期对象的转化(format/parse)

DateFormat

java.text.DateFormat是一个抽象类, 他提供了如下几个方法获取DateFormat对象.

方法 描述
static DateFormat getDateInstance() Gets the date formatter with the default formatting style for the default locale.
static DateFormat getDateTimeInstance() Gets the date/time formatter with the default formatting style for the default locale.
static DateFormat getTimeInstance() Gets the time formatter with the default formatting style for the default locale.

其实上面三个方法还可以指定日期/时间的样式, 如FULL/LONG/MEDIUM/SHOT, 通过这四个样式参数可以控制生成的格式化字符串. 但由于在我们的实际开发中很少直接用DateFormat类,因此就不对其做过多的介绍.而我们比较常用的是其子类SimpleDateFormat(其实上面几个getXxxInstance方法返回的也是SimpleDateFormat实例)

DateFormat dateFormat = DateFormat.getTimeInstance();
System.out.println(dateFormat.getClass().getName());

SimpleDateFormat

java.text.SimpleDateFormat可以非常灵活的格式化Date, 也可以用于解析各种格式的日期字符串.创建SimpleDateFormat对象时需要传入一个pattern字符串,这个pattern不是正则表达式,而是一个日期模板字符串.

/**
 * Created by jifang on 15/12/30.
 */
public class FormatTest {

    @Test
    public void client() throws ParseException {

        DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

        // Date -> String
        Date date = new Date(System.currentTimeMillis());
        System.out.println(format.format(date));

        // String -> Date
        String timeString = "2015-12-30 08:53:21";
        Date newDate = format.parse(timeString);
        System.out.println(newDate);
    }
}

在时间日期格式化时, 有下面几个方法是最常用的:

方法 描述 小结
String format(Date date) Formats a Date into a date/time string. Date -> String
Date parse(String source) Parses text from the beginning of the given string to produce a date. String -> Date

当然, pattern我们还可以根据我们的需求有其他的定制形式:

    @Test
    public void client() throws ParseException {

        DateFormat format = new SimpleDateFormat("yy年MM月dd日 hh时mm分ss秒");

        // Date -> String
        Date date = new Date(System.currentTimeMillis());
        System.out.println(format.format(date));

        // String -> Date
        String timeString = "15年12月30日 09时00分29秒";
        Date newDate = format.parse(timeString);
        System.out.println(newDate);
    }

可以看出SimpleDateFormat把日期格式化成怎样的字符串以及能把怎样的字符串解析成Date, 完全取决于创建对象时指定的pattern参数,其他的pattern参数以及SimpleDateFormat的方法可以参考JDK文档.


实战

时间存储

将时间存入DB会涉及到跨时区的问题(同一个UTC时间在各个时区显示不同的数值).因此,在设计数据库表结构时需要小心谨慎,不能简单使用数据库提供的TIMESTAMPDATETIME(详见:廖雪峰博客-如何正确地处理时间),比较推荐的是选用一个整数类型(如BIGINT64位)来存储从1970-01-01 00:00:00到当前时间所经过毫秒数的一个绝对数值.

  • 优点:

    读取的时间(Long整数)只需要按照用户所在的时区格式化为字符串就能正确地显示出来.
  • 缺点

    当开发人员、DBA直接查看DB时,只能看到一串数字.

线程安全formatter

时间的格式化和解析格式在一个项目中往往只用一种,因此没有必要每次使用时都new出一个Formatter来. 但SimpleDateFormatformat()parse()并非线程安全(详见: 关于SimpleDateFormat的非线程安全问题及其解决方案), 因此在并发环境中要注意同步或使用ThreadLocal控制:

  • DateUtil
public class DateUtils {

    // Each thread will have its own copy of the SimpleDateFormat
    private static final ThreadLocal<SimpleDateFormat> formatterMap = new ThreadLocal<SimpleDateFormat>() {
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        }
    };

    public static String format(Object obj) {
        return formatterMap.get().format(obj);
    }

    public static Date parse(String source) throws ParseException {
        return formatterMap.get().parse(source);
    }

    public static void main(String[] args) {

        long start = System.currentTimeMillis();
        final String[] data = {
                "1999-11-01 11:11:11",
                "2001-07-08 06:06:06",
                "2007-01-31 02:28:31",
                "1999-11-01 11:11:11",
                "2001-07-08 06:06:06",
                "2007-01-31 02:28:31"};

        List<Thread> threads = new ArrayList<>(data.length);
        for (int i = 0; i < data.length; ++i) {
            final int i2 = i;
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int j = 0; j < 10_000; j++) {
                            String from = data[i2];
                            Date d = parse(from);
                            String to = format(d);
                            System.out.println("i: " + i2 + "\tj: " + j + "\tThreadID: "
                                    + Thread.currentThread().getId() + "\tThreadName: "
                                    + Thread.currentThread().getName() + "\t" + from + "\t" + to);
                            if (!from.equals(to)) {
                                throw new RuntimeException("date conversion failed after " + j
                                        + " iterations. Expected " + from + " but got " + to);
                            }
                        }
                    } catch (ParseException e) {
                        throw new RuntimeException("parse failed");
                    }
                }
            }, "formatter_test_thread_" + i);

            threads.add(thread);
            thread.start();
        }

        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("total consume [" + (System.currentTimeMillis() - start) + "] ms.");
    }
}

Java 中的日期与时间的更多相关文章

  1. Java中的日期和时间

    Java中的日期和时间 Java在java.util包中提供了Date类,这个类封装了当前的日期和时间. Date类支持两种构造函数.第一个构造函数初始化对象的当前日期和时间. Date() 下面的构 ...

  2. java中获取日期和时间的方法总结

    1.获取当前时间,和某个时间进行比较.此时主要拿long型的时间值. 方法如下:  要使用 java.util.Date .获取当前时间的代码如下 Date date = new Date(); da ...

  3. Java中的日期与时间

    日期与时间 最常用的几个类,Date.DateFormat.Calendar.Locale Date 1.无参构造方法 //根据当前系统默认的毫秒值创建时间对象 public Date() { thi ...

  4. Java中的日期、时间操作

    每次在处理日期时间的时候都要打开chrome查找一番,索性自己找一下满意的记录下来. 一.时间格式 // hh表示12小时制: HH表示24小时制 SimpleDateFormat format1 = ...

  5. 填坑:Java 中的日期转换

    我们之前讨论过时间,在Java 中有一些方法会出现横线?比如Date 过期方法. 参考文章:知识点:java一些方法会有横线?以Date 过期方法为例 Java中的日期和时间处理方法 Date类(官方 ...

  6. JAVA中获取当前系统时间及格式转换

    JAVA中获取当前系统时间   一. 获取当前系统时间和日期并格式化输出: import java.util.Date;import java.text.SimpleDateFormat; publi ...

  7. JAVA中获取当前系统时间

    一. 获取当前系统时间和日期并格式化输出: import java.util.Date;import java.text.SimpleDateFormat; public class NowStrin ...

  8. Java中的日期操作

    在日志中常用的记录当前时间及程序运行时长的方法: public void inject(Path urlDir) throws Exception { SimpleDateFormat sdf = n ...

  9. 计算机程序的思维逻辑 (95) - Java 8的日期和时间API

    ​本节继续探讨Java 8的新特性,主要是介绍Java 8对日期和时间API的增强,关于日期和时间,我们在之前已经介绍过两节了,32节介绍了Java 1.8以前的日期和时间API,主要的类是Date和 ...

随机推荐

  1. 计蒜客NOIP模拟赛4 D1T2小X的密室

    小 X 正困在一个密室里,他希望尽快逃出密室. 密室中有 N 个房间,初始时,小 X 在 1 号房间,而出口在 N 号房间. 密室的每一个房间中可能有着一些钥匙和一些传送门,一个传送门会单向地创造一条 ...

  2. [SCOI2010]幸运数字

    题目描述 在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运 ...

  3. 素数密度_NOI导刊2011提高(04)

    题目描述 给定区间[L, R](L <= R <= 2147483647,R-L <= 1000000),请计算区间中素数的个数. 输入 两个数 L 和 R. 输出 一行,区间中素数 ...

  4. bzoj 5285: [Hnoi2018]寻宝游戏

    Description Solution 把输入的 \(n\) 个二进制数看作一个大小为 \(n*m\) 的矩阵 把每一列压成一个二进制数,其中最高位是最下面的元素 然后就有了 \(m\) 个二进制数 ...

  5. 【USACO】又买饲料 单调队列dp

    题目描述 约翰开车回家,又准备顺路买点饲料了(咦?为啥要说“又”字?)回家的路程一共有 E 公里, 这一路上会经过 N 家商店,第 i 家店里有 F i 吨饲料,售价为每吨 C i 元.约翰打算买 K ...

  6. 51Nod 1555 布丁怪

    题目描述: 布丁怪这一款游戏是在一个n×n 的矩形网格中进行的,里面有n个网格有布丁怪,其它的一些格子有一些其它的游戏对象.游戏的过程中是要在网格中移动这些怪物.如果两个怪物碰到了一起,那么他们就会变 ...

  7. HEOI2017游记

    Day -1: noip与标准时限差了0.02秒,并没有申诉成功,导致NOIWC多交了900元钱. 滚回去准备学考,文科瞎写居然拿了A,可啪. NOIWC颓废记由于我实在太颓了所以懒得填坑了. THU ...

  8. C++11的原子量与内存序浅析

    一.多线程下共享变量的问题 在多线程编程中经常需要在不同线程之间共享一些变量,然而对于共享变量操作却经常造成一些莫名奇妙的错误,除非老老实实加锁对访问保护,否则经常出现一些(看起来)匪夷所思的情况.比 ...

  9. 安装win7提示“我们无法创建新的分区,也找不到现有分区”

    用U盘安装操作系统,但是遇到了这种问题. 来来回删掉主分区,重新建立主分区,都不能搞定.最后还是用古老的方法安装了.安装的方法如下: 大家首先要进入到winpe,这里我用的是大白菜winpe. 将C盘 ...

  10. Java阻塞队列的实现

    阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列 ...