Java 日期类学习笔记
java日期类
java.util.Date#
- 对java.util.Date中的操作实际上都是对BaseCalendar.Date的操作
- java.util.Date是对于一个时间点上的抽象, 表示时间戳上这个具体的时间点, 可以根据这个时间戳时间点提取出各个属性
Date类的常用构造
public Date() {
this(System.currentTimeMillis()); //默认传入当前系统时间戳
}
public Date(long date) {
fastTime = date;
}
获取Date中的各种信息
/**
* 使用日期类中的获取属性并打印信息
* @param date 日期类
*/
public void printDateMsg(Date date){
System.out.println("getTime: " + date.getTime()); //获取时间戳
System.out.println("getYear: " + date.getYear()); //获取年份, 以历元为标准 1900
System.out.println("getMonth: " + date.getMonth()); //当前月 0-11
System.out.println("getDate: " + date.getDate()); //月份中的第几天 1-31
System.out.println("getDay: " + date.getDay()); //当前星期数
System.out.println("getHours: " + date.getHours()); //当前小时 0-23
System.out.println("getMinutes: " + date.getMinutes()); //当前分钟 0-59
System.out.println("getSeconds: " + date.getSeconds()); //当前秒数 0-59
System.out.println("getTimezoneOffset: " + date.getTimezoneOffset()); //UTC 时间和本地时间之间的时差(分钟为单位)
}
get/setMonth
public int getMonth() {
return normalize().getMonth() - 1; // adjust 1-based to 0-based
}
public void setMonth(int month) {
int y = 0;
if (month >= 12) {
y = month / 12; //大于11的化作下一年的月份
month %= 12;
} else if (month < 0) {
y = CalendarUtils.floorDivide(month, 12);
month = CalendarUtils.mod(month, 12);
}
BaseCalendar.Date d = getCalendarDate();
if (y != 0) {
d.setNormalizedYear(d.getNormalizedYear() + y);
}
d.setMonth(month + 1); // adjust 0-based to 1-based month numbering
}
get/setDate
public int getDate() { //获取时间戳的时间是本月中的第几天
return normalize().getDayOfMonth();
}
public void setDate(int date) {
getCalendarDate().setDayOfMonth(date);
}
getDay
public int getDay() { //获取时间戳的时间中当天是星期几
return normalize().getDayOfWeek() - BaseCalendar.SUNDAY;
}
get/setHours
public int getHours() { //获取时间戳的时间中当时是几点
return normalize().getHours();
}
public void setHours(int hours) {
getCalendarDate().setHours(hours);
}
get/setMinutes
public int getMinutes() { //获取时间戳的时间中当时是小时中的第几分钟
return normalize().getMinutes();
}
public void setMinutes(int minutes) {
getCalendarDate().setMinutes(minutes);
}
get/setSeconds
public int getSeconds() { //获取时间戳的时间中当时是分钟中的第几秒钟
return normalize().getSeconds();
}
public void setSeconds(int seconds) {
getCalendarDate().setSeconds(seconds);
}
normalize()
private final BaseCalendar.Date normalize() { //规范化返回BaseCalendar.Date类
if (cdate == null) {
BaseCalendar cal = getCalendarSystem(fastTime);
cdate = (BaseCalendar.Date) cal.getCalendarDate(fastTime,
TimeZone.getDefaultRef());
return cdate;
}
hashCode()
public int hashCode() {
long ht = this.getTime();
return (int) ht ^ (int) (ht >> 32); //异或时间左移32位(消去低位, 将高位后移高位用0补充)
}
比较日期顺序
public boolean before(Date when) { //当前Date是否在传入Date之前
return getMillisOf(this) < getMillisOf(when);
}
public boolean after(Date when) { //当前Date是否在传入Date之后
return getMillisOf(this) > getMillisOf(when);
}
public int compareTo(Date anotherDate) { //直接比较
long thisTime = getMillisOf(this);
long anotherTime = getMillisOf(anotherDate);
return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
}
重新设置时间戳
public void setTime(long time) {
fastTime = time; //重新设置时间戳
cdate = null; //BaseCalendar.Date 重新指向null
}
获取时间戳
public long getTime() {
return getTimeImpl();
}
private final long getTimeImpl() {
if (cdate != null && !cdate.isNormalized()) { //若当前日期未被规范化则进行规范化
normalize(); //进行规范化
}
return fastTime;
}
java.sql.Date#
-
Date extends java.util.Date
java.sql.Date是java.util.Date的子类, -
java.sql.Date中get和set关于时间戳的方法都被标记为过时且直接抛出IllegalArgumentException异常
构造方法
和父类不同, 父类含有无参构造可以以当前时间做为默认时间戳.
子类必须传入指定时间.
@Deprecated
public Date(int year, int month, int day) { //过时的构造方法
super(year, month, day);
}
public Date(long date) { //调用父类构造方法并传入时间戳
// If the millisecond date value contains time info, mask it out.
super(date);
}
setTime()
提供了重新设置时间戳的方法
public void setTime(long date) {
// If the millisecond date value contains time info, mask it out.
super.setTime(date);
}
valueOf()
将jdbc日期格式的字符串转化为日期类型
/**
* Converts a string in JDBC date escape format to
* a <code>Date</code> value.
*
* @param s a <code>String</code> object representing a date in
* in the format "yyyy-[m]m-[d]d". The leading zero for <code>mm</code>
* and <code>dd</code> may also be omitted.
* @return a <code>java.sql.Date</code> object representing the
* given date
* @throws IllegalArgumentException if the date given is not in the
* JDBC date escape format (yyyy-[m]m-[d]d)
*/
public static Date valueOf(String s) { //传入jdbc的日期格式字符串
final int YEAR_LENGTH = 4; //年占长度为4
final int MONTH_LENGTH = 2; //月份占长度为2
final int DAY_LENGTH = 2; //天数占长度为12
final int MAX_MONTH = 12; //最大月份
final int MAX_DAY = 31; //最大天数
int firstDash; //第一个破折号分隔符
int secondDash; //第二个破折号分隔符
Date d = null;
if (s == null) {
throw new java.lang.IllegalArgumentException();
}
firstDash = s.indexOf('-'); //查找出第一二个破折号的索引
secondDash = s.indexOf('-', firstDash + 1);
if ((firstDash > 0) && (secondDash > 0) && (secondDash < s.length() - 1)) {
String yyyy = s.substring(0, firstDash); //截取第一个破折号之前的为年份
String mm = s.substring(firstDash + 1, secondDash); //截取第一个和第二个破折号之间为月份
String dd = s.substring(secondDash + 1); //截取第二个破折号到最后的为天数
if (yyyy.length() == YEAR_LENGTH &&
(mm.length() >= 1 && mm.length() <= MONTH_LENGTH) &&
(dd.length() >= 1 && dd.length() <= DAY_LENGTH)) { //长度合法性检测和类型转换
int year = Integer.parseInt(yyyy);
int month = Integer.parseInt(mm);
int day = Integer.parseInt(dd);
if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) { //数据合法性检测
d = new Date(year - 1900, month - 1, day); //返回一个父类Date对象
}
}
}
if (d == null) {
throw new java.lang.IllegalArgumentException();
}
return d;
}
toString()
将日期转换为yyyy-mm-dd
格式的字符串
/**
* Formats a date in the date escape format yyyy-mm-dd.
* <P>
* @return a String in yyyy-mm-dd format
*/
@SuppressWarnings("deprecation")
public String toString () {
int year = super.getYear() + 1900; //获取的年份要加上元历年份
int month = super.getMonth() + 1; //月份从0~11, 所以要+1
int day = super.getDate();
char buf[] = "2000-00-00".toCharArray();
buf[0] = Character.forDigit(year/1000,10);
buf[1] = Character.forDigit((year/100)%10,10);
buf[2] = Character.forDigit((year/10)%10,10);
buf[3] = Character.forDigit(year%10,10);
buf[5] = Character.forDigit(month/10,10);
buf[6] = Character.forDigit(month%10,10);
buf[8] = Character.forDigit(day/10,10);
buf[9] = Character.forDigit(day%10,10);
return new String(buf);
}
//Character.forDigit() 用于确定指定基数中特定数字的字符表示形式。
/**
* 这个方法用于将数字转为相应的字符, 要将数字转换为这个数字所对应的字符需要加上以
* @param digit 要转换成字符的数字
* @param radix 转换成数字基于的进制
*/
public static char forDigit(int digit, int radix) {
if ((digit >= radix) || (digit < 0)) { //要转换的数字需要大于进制且不能为负
return '\0'; //返回空字符
}
// public static final int MIN_RADIX = 2; 最小二进制
// public static final int MAX_RADIX = 36; 最大36进制, 因为英文字母最多26个+数字0-9一共36个
if ((radix < Character.MIN_RADIX) || (radix > Character.MAX_RADIX)) {
return '\0';
}
if (digit < 10) {
return (char)('0' + digit); //若小于10则直接返回该数字形状的字符就行了
}
return (char)('a' - 10 + digit); //大于9就有英文字符表示的数字, 需要先减去10(大于10的部分从a开始)剩下的部分为余数加上'a'
}
和LocalDate之间的互相转化
public static Date valueOf(LocalDate date) {
return new Date(date.getYear() - 1900, date.getMonthValue() -1,
date.getDayOfMonth());
}
public LocalDate toLocalDate() {
return LocalDate.of(getYear() + 1900, getMonth() + 1, getDate());
}
java.util.Calendar#
- Calendar是通过工厂方式创建对象
几种创建Calendar对象的方式:
public static Calendar getInstance() //无参构造使用默认时区默认地区创建
{
return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
}
public static Calendar getInstance(TimeZone zone) //指定时区创建
{
return createCalendar(zone, Locale.getDefault(Locale.Category.FORMAT));
}
public static Calendar getInstance(Locale aLocale) //指定地区创建
{
return createCalendar(TimeZone.getDefault(), aLocale);
}
public static Calendar getInstance(TimeZone zone, Locale aLocale)//指定时区和地区创建
{
return createCalendar(zone, aLocale);
}
createCalendar()
private static Calendar createCalendar(TimeZone zone, Locale aLocale)
{
CalendarProvider provider = LocaleProviderAdapter
.getAdapter(CalendarProvider.class, aLocale)
.getCalendarProvider(); //使用配适器模式获得Calendar的实体类
if (provider != null) {
try {
return provider.getInstance(zone, aLocale); //若获取到了配适器则根据配适器获取Calendar类
} catch (IllegalArgumentException iae) { //使用默认实例化
// fall back to the default instantiation
}
}
Calendar cal = null;
if (aLocale.hasExtensions()) { //如果存在能识别的键
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) { //如果没有找到对应类型的Calendar, 则判断日本和泰国地区如果不是则使用公历
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") { //泰国使用佛历
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" &&
aLocale.getLanguage() == "ja" &&
aLocale.getCountry() == "JP") { //日本使用日本历
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale); //其他所有使用公历
}
}
return cal;
}
Calendar中的getTime()返回java.util.Date和setTime()
public final Date getTime() {
return new Date(getTimeInMillis());
}
public final void setTime(Date date) {
setTimeInMillis(date.getTime());
}
int get(int field)
用于获取Calendar实例中储存的各个信息,
public int get(int field) //获取字段属性
{
complete(); //先对实例字段进行计算和赋值
return internalGet(field);
}
protected final int internalGet(int field) //返回指定字段的值
{
return fields[field];
}
//根据
protected void complete()
{
if (!isTimeSet) {
updateTime();
}
if (!areFieldsSet || !areAllFieldsSet) {
computeFields(); // fills in unset fields
areAllFieldsSet = areFieldsSet = true;
}
}
/**
* Converts the current millisecond time value {@link #time}
* to calendar field values in {@link #fields fields[]}.
* This allows you to sync up the calendar field values with
* a new time that is set for the calendar. The time is <em>not</em>
* recomputed first; to recompute the time, then the fields, call the
* {@link #complete()} method.
*
* @see #computeTime()
*/
protected abstract void computeFields(); //根据当前时间戳对字段属性进行转换和赋值
Calendar中的属性字段
字段 | |
---|---|
ERA = 0 | BC 0 表示公元前, AD 1 表示公元 |
YEAR = 1 | 当前年份, 年 |
MONTH = 2 | 当前月份 0~11, 月 |
WEEK_OF_YEAR = 3 | 当前时间是这一年的第几周 |
WEEK_OF_MONTH = 4 | 当前时间是这一月的第几周 |
DATE = 5 | 日 |
DAY_OF_MONTH = 5 | 日 |
DAY_OF_YEAR = 6 | 当前时间是这一年的第多少天 |
DAY_OF_WEEK = 7 | 星期几 |
DAY_OF_WEEK_IN_MONTH = 8 | 当前月份内这一周的第几天的序号 |
AM_PM = 9 | 0 上午; 1 下午; |
HOUR = 10 | 时 12小时制 |
HOUR_OF_DAY = 11 | 时 24小时制 |
MINUTE = 12 | 分 |
SECOND = 13 | 秒 |
MILLISECOND = 14 | 毫秒 |
ZONE_OFFSET = 15 | 距GMT的毫秒偏移量 |
DST_OFFSET = 16 | 距夏令时的毫秒偏移量 |
FIELD_COUNT = 17 | 字段总数 |
关于星期的表示:
星期 |
---|
SUNDAY = 1 |
MONDAY = 2 |
TUESDAY = 3 |
WEDNESDAY = 4 |
THURSDAY = 5 |
FRIDAY = 6 |
SATURDAY = 7 |
Calendar中, 星期日为周的起始值为1, 到星期六为一周的末尾值为7
月份 |
---|
JANUARY = 0 |
FEBRUARY = 1 |
MARCH = 2 |
APRIL = 3 |
MAY = 4 |
JUNE = 5 |
JULY = 6 |
AUGUST = 7 |
SEPTEMBER = 8 |
OCTOBER = 9 |
NOVEMBER = 10 |
DECEMBER = 11 |
UNDECIMBER = 12 |
Calendar中月份从一月开始值为0, 到12一共有十三个月的表示法
上午和下午AMAndPM |
---|
AM = 0 |
PM = 1 |
Calendar中上午为0下午为1 |
Calendar修改属性的值
根据日历的规则增加或者减去指定的时间
/**
* Adds or subtracts the specified amount of time to the given calendar field,
* based on the calendar's rules. For example, to subtract 5 days from
* the current time of the calendar, you can achieve it by calling:
* <p><code>add(Calendar.DAY_OF_MONTH, -5)</code>.
*
* @param field the calendar field.
* @param amount the amount of date or time to be added to the field.
* @see #roll(int,int)
* @see #set(int,int)
*/
abstract public void add(int field, int amount);
将日历字段field更改为value
public void set(int field, int value)
{
// If the fields are partially normalized, calculate all the
// fields before changing any fields.
if (areFieldsSet && !areAllFieldsSet) {
computeFields();
}
internalSet(field, value);
isTimeSet = false;
areFieldsSet = false;
isSet[field] = true;
stamp[field] = nextStamp++;
if (nextStamp == Integer.MAX_VALUE) {
adjustStamp();
}
}
//设置字段
public final void set(int year, int month, int date, int hourOfDay, int minute)
{
set(YEAR, year);
set(MONTH, month);
set(DATE, date);
set(HOUR_OF_DAY, hourOfDay);
set(MINUTE, minute);
}
public final void set(int year, int month, int date, int hourOfDay, int minute,
int second)
{
set(YEAR, year);
set(MONTH, month);
set(DATE, date);
set(HOUR_OF_DAY, hourOfDay);
set(MINUTE, minute);
set(SECOND, second);
}
将递增增加字段field的值amount次, 从而不修改更高的时间单位的值
/**
* Adds or subtracts (up/down) a single unit of time on the given time
* field without changing larger fields. For example, to roll the current
* date up by one day, you can achieve it by calling:
* <p>roll(Calendar.DATE, true).
* When rolling on the year or Calendar.YEAR field, it will roll the year
* value in the range between 1 and the value returned by calling
* <code>getMaximum(Calendar.YEAR)</code>.
* When rolling on the month or Calendar.MONTH field, other fields like
* date might conflict and, need to be changed. For instance,
* rolling the month on the date 01/31/96 will result in 02/29/96.
* When rolling on the hour-in-day or Calendar.HOUR_OF_DAY field, it will
* roll the hour value in the range between 0 and 23, which is zero-based.
*
* @param field the time field.
* @param up indicates if the value of the specified time field is to be
* rolled up or rolled down. Use true if rolling up, false otherwise.
* @see Calendar#add(int,int)
* @see Calendar#set(int,int)
*/
abstract public void roll(int field, boolean up);
public void roll(int field, int amount)
{
while (amount > 0) {
roll(field, true);
amount--;
}
while (amount < 0) {
roll(field, false);
amount++;
}
}
Calendar的比较函数
public boolean before(Object when) { //比较是否在传入对象之前
return when instanceof Calendar
&& compareTo((Calendar)when) < 0;
}
public boolean after(Object when) { //比较是否在传入对象之后
return when instanceof Calendar
&& compareTo((Calendar)when) > 0;
}
public int compareTo(Calendar anotherCalendar) { //和另一个Calendar实例进行比较
return compareTo(getMillisOf(anotherCalendar));
}
private int compareTo(long t) {
long thisTime = getMillisOf(this);
return (thisTime > t) ? 1 : (thisTime == t) ? 0 : -1;
}
java.time.LocalDate#
- 一个本地日期类型, 不包含时区信息的日期类型
(待补充)
DateFormat#
- SimpleDateFormate
SimpleDateFormate#
- 用于格式化日期形式, 是DateFormat的子类
pattern的参数解析:
yyyy:年
MM:月
dd:日
hh:1~12小时制(1-12)
HH:24小时制(0-23)
mm:分
ss:秒
S:毫秒
E:星期几
D:一年中的第几天
F:一月中的第几个星期(会把这个月总共过的天数除以7)
w:一年中的第几个星期
W:一月中的第几星期(会根据实际情况来算)
a:上下午标识
k:和HH差不多,表示一天24小时制(1-24)。
K:和hh差不多,表示一天12小时制(0-11)。
z:表示时区
这里对于SimpleDateFormate只写一个小例子:
public static void main(String[] args) {
Date date = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat = new SimpleDateFormat("'yyyy':yyyy年 'MM':MM月 'dd'dd日 'HH':HH小时 'mm':mm分 'ss':ss秒 'S':S毫秒 'E':E 'z':z");
System.out.println("格式一: " + dateFormat.format(date));
SimpleDateFormat dateFormatNormal = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("格式二: " + dateFormatNormal.format(date));
}
输出:
格式一: yyyy:2022年 MM:04月 dd13日 HH:20小时 mm:52分 ss:50秒 S:591毫秒 E:星期三 z:CST
格式二: 2022-04-13 20:52:50
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!