Java 中的日期与时间
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时间在各个时区显示不同的数值).因此,在设计数据库表结构时需要小心谨慎,不能简单使用数据库提供的TIMESTAMP或DATETIME(详见:廖雪峰博客-如何正确地处理时间),比较推荐的是选用一个整数类型(如BIGINT64位)来存储从1970-01-01 00:00:00到当前时间所经过毫秒数的一个绝对数值.
- 优点:
读取的时间(Long整数)只需要按照用户所在的时区格式化为字符串就能正确地显示出来. - 缺点
当开发人员、DBA直接查看DB时,只能看到一串数字.
线程安全formatter
时间的格式化和解析格式在一个项目中往往只用一种,因此没有必要每次使用时都new出一个Formatter来. 但SimpleDateFormat的format()与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 中的日期与时间的更多相关文章
- Java中的日期和时间
Java中的日期和时间 Java在java.util包中提供了Date类,这个类封装了当前的日期和时间. Date类支持两种构造函数.第一个构造函数初始化对象的当前日期和时间. Date() 下面的构 ...
- java中获取日期和时间的方法总结
1.获取当前时间,和某个时间进行比较.此时主要拿long型的时间值. 方法如下: 要使用 java.util.Date .获取当前时间的代码如下 Date date = new Date(); da ...
- Java中的日期与时间
日期与时间 最常用的几个类,Date.DateFormat.Calendar.Locale Date 1.无参构造方法 //根据当前系统默认的毫秒值创建时间对象 public Date() { thi ...
- Java中的日期、时间操作
每次在处理日期时间的时候都要打开chrome查找一番,索性自己找一下满意的记录下来. 一.时间格式 // hh表示12小时制: HH表示24小时制 SimpleDateFormat format1 = ...
- 填坑:Java 中的日期转换
我们之前讨论过时间,在Java 中有一些方法会出现横线?比如Date 过期方法. 参考文章:知识点:java一些方法会有横线?以Date 过期方法为例 Java中的日期和时间处理方法 Date类(官方 ...
- JAVA中获取当前系统时间及格式转换
JAVA中获取当前系统时间 一. 获取当前系统时间和日期并格式化输出: import java.util.Date;import java.text.SimpleDateFormat; publi ...
- JAVA中获取当前系统时间
一. 获取当前系统时间和日期并格式化输出: import java.util.Date;import java.text.SimpleDateFormat; public class NowStrin ...
- Java中的日期操作
在日志中常用的记录当前时间及程序运行时长的方法: public void inject(Path urlDir) throws Exception { SimpleDateFormat sdf = n ...
- 计算机程序的思维逻辑 (95) - Java 8的日期和时间API
本节继续探讨Java 8的新特性,主要是介绍Java 8对日期和时间API的增强,关于日期和时间,我们在之前已经介绍过两节了,32节介绍了Java 1.8以前的日期和时间API,主要的类是Date和 ...
随机推荐
- [NOI 2014]动物园
Description 题库链接 \(t\) 组询问.每组询问给出一个字符串 \(S\) .要求求出一个 \(num\) 数组一一对于字符串 \(S\) 的前 \(i\) 个字符构成的子串,既是它的后 ...
- [HAOI 2011]Problem b
Description 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. Input 第一行一个整数 ...
- [PA 2014]Bohater
Description 在一款电脑游戏中,你需要打败n只怪物(从1到n编号).为了打败第i只怪物,你需要消耗d[i]点生命值,但怪物死后会掉落血药,使你恢复a[i]点生命值.任何时候你的生命值都不能降 ...
- Educational Codeforces Round 17F Tree nesting
来自FallDream的博客,未经允许,请勿转载, 谢谢. 给你两棵树,一棵比较大(n<=1000),一棵比较小(m<=12) 问第一棵树中有多少个连通子树和第二棵同构. 答案取膜1e9+ ...
- [Noi2013]向量内积
来自FallDream的博客,未经允许,请勿转载,谢谢. 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: $\sum_{i=1 ...
- Spring MVC - 静态页面
环境搭建 以下示例显示如何使用Spring MVC Framework编写一个简单的基于Web的应用程序,它可以使用<mvc:resources>标记访问静态页面和动态页面.首先使用Int ...
- Python中模块之queue的功能介绍
模块之queue的功能介绍 队列的分类: 队列主要要分为两种 1.双向队列 2.单项队列 1. 单项队列 创建单项队列 格式:queue.Queue(obj) 例如:que = queue.Queue ...
- Mianbot:基于向量匹配的情境式聊天机器人
Mianbot是采用样板与检索式模型搭建的聊天机器人,目前有两种产生回覆的方式,专案仍在开发中:) 其一(左图)是以词向量进行短语分类,针对分类的目标模组实现特征抽取与记忆回覆功能,以进行多轮对话,匹 ...
- (概念)多个CPU和多核CPU以及超线程(Hyper-Threading)
引言 在这篇文章中我会主要介绍CPU相关的一些重要概念和技术.如果你想更好地了解操作系统,那就从本文开始吧. 中央处理器(Central processing unit) 在我们了解其它概念之前,我们 ...
- php+xdebug远程调试(单人)
目录 服务器上安装 XDebug 及配置 客户端 PHPstorm 配置 浏览器安装插件 服务器上安装 XDebug 及配置 XDebug 安装 略 配置: 打开 php.ini 配置文件: vim ...