io.github.wycst.wast.common.beans.GregorianDate Maven / Gradle / Ivy
Show all versions of wast Show documentation
/*
* Copyright [2020-2024] [wangyunchao]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package io.github.wycst.wast.common.beans;
import java.util.TimeZone;
/**
* 公历日期类
*
*
* - 关于闰年算法
* 闰年只是为了修正平年(365天)和回归年(太阳转一圈)误差,没有绝对的算法。
* 地球公转一周 365.24219天(回归年)
* 什么时候该闰年? 平年365天,闰年366天
* 每4年实际天数: 365.24219 * 4 = 365 * 4 + 0.96876,即4个平年少算:0.96876天,所以把这差不多一天少算时间放在第四年作为闰年
*
* - 为什么被100整除但不被400整除的年不是闰年原因?
* 假定0004年是闰年,多算0.03124天(0.03124 = 1 - 0.96876,把实际不够一天的时间当一天算,等于借了未来时间)
* 按四年一闰则每个闰周期多算0.03124天,经过25个周期(假定100年是闰年)则会多算 25 * 0.03124 = 0.781天(借未来时间),估算差不多1天
* 此时将100年定为平年来抵消(除去本来应该定为闰年的一天还上未来时间0.781天,还剩下0.219天),所以100年变成平年了(排除早期闰年算法错误原因)。
*
* - 为什么被400整除的又是闰年?
* 按100年是平年则每100年多出来0.219天(上文剩下来的),经过4个周期(400年),则会多出来0.876天,估算差不多一天,所以又将400年定为闰年。
* 其实500年误差似乎更小一些,但500年会超过一天,不利于后面的周期运算。
*
* - 3200年是否为闰年?
* 按400年一闰法则来计算,每400年向未来借0.124天(0.124 = 1 - 0.876)经过8个周期则会借0.992天,则把3200年定为平年能正好抵消(还完还剩下0.008天)
* 所以理论上3200是平年。
*
*
*
* 以3200年为一个周期(按3200年为平年)(每个周期剩下0.008天,则经过125个周期后即40万年,整整剩出1天(0.008 * 125)
* 如按此周期年算,40万年又是闰年 ??
* 至此按365.24219的精度来算误差已经没了,即40万年可能是一个完整的闰年周期。
*
*
注: 目前支持公元元年开始以后的日期(0001-01-01 00:00:00.000+)
*
* @author wangyunchao
* @see java.util.Calendar
*/
public class GregorianDate extends GeneralDate implements java.io.Serializable, Comparable {
// 星期
protected int dayOfWeek;
// 当月第几个星期
protected int weekOfMonth;
// 当年第几个星期
protected int weekOfYear;
// // 以下2个属性后续放在LunarDate中
// // 以2019年24节气中时刻(毫秒数)作为参考
// // 从小寒开始,冬至结束
// public final static long[] SOLAR_TERMS_2019 = new long[24];
// 闰年算法: 0~1582之前按每4年一润 1582-? 按最新算法
// 删除1582年 10月5日至14日共 10天
public GregorianDate() {
this(System.currentTimeMillis());
}
public GregorianDate(long timeMills) {
this(timeMills, null);
}
public GregorianDate(TimeZone timeZone) {
this(System.currentTimeMillis(), timeZone);
}
public GregorianDate(long timeMills, TimeZone timeZone) {
super(timeZone);
setTime(timeMills);
}
public GregorianDate(int year, int month, int day) {
this(year, month, day, 0, 0, 0, 0, (TimeZone) null);
}
public GregorianDate(int year, int month, int day, int hour, int minute, int second,
int millsecond) {
this(year, month, day, hour, minute, second, millsecond, (TimeZone) null);
}
public GregorianDate(int year, int month, int day, int hour, int minute, int second,
int millisecond, TimeZone timeZone) {
super(timeZone);
this.set(year, month, day, hour, minute, second, millisecond, timeZone);
}
/**
* 支持格式: {'yyyy-MM-dd', 'yyyy-MM-dd HH:mm:ss'}
* 年月日必须
*
* @param dateStr
* @return
*/
public static GregorianDate parse(String dateStr) {
GeneralDate generalDate = parseGeneralDate(dateStr, null);
return new GregorianDate(generalDate.year, generalDate.month, generalDate.dayOfMonth, generalDate.hourOfDay, generalDate.minute, generalDate.second, 0);
}
/**
* 以指定模板解析日期
* 和format方法逆向处理
*
* @param dateStr
* @param template
* @return
* @see GregorianDate#format(String)
*/
public static GregorianDate parse(String dateStr, String template) {
if (template == null)
return parse(dateStr);
char[] dateBuf = dateStr.toCharArray();
return parse(dateBuf, 0, dateBuf.length, template);
}
/**
* 提取日期字符转化为日期对象
*
* @param buf
* @param offset
* @param len
* @param template
* @return
*/
public static GregorianDate parse(char[] buf, int offset, int len, String template) {
DateTemplate dateTemplate = new DateTemplate(template);
return parse(buf, offset, len, dateTemplate);
}
public static GregorianDate parse(char[] buf, int offset, int len, DateTemplate dateTemplate) {
dateTemplate.getClass();
return dateTemplate.parse(buf, offset, len);
}
/**
* 以当前时间为轴左右
*
* @param type
* @param count
*/
public GregorianDate add(int type, int count) {
switch (type) {
case YEAR:
this.year += count;
this.updateTime();
break;
case MONTH:
this.month += count;
this.updateTime();
break;
case DAY_OF_MONTH:
// 如果是添加的天数
long timeMills = this.timeMills + count * 24l * 3600 * 1000;
setTime(timeMills);
break;
default:
break;
}
return this;
}
public long interval(GregorianDate target) {
return target.getTime() - this.timeMills;
}
public long intervalDays(GregorianDate target) {
return (target.getTime() - this.timeMills) / 86400000l;
}
public long intervalHours(GregorianDate target) {
return (target.getTime() - this.timeMills) / 3600000l;
}
public GregorianDate set(int year, int month, int day) {
return this.set(year, month, day, hourOfDay, minute, second, millisecond, null);
}
public GregorianDate set(int year, int month, int day, int hour, int minute, int second,
int millisecond) {
return this.set(year, month, day, hour, minute, second, millisecond, null);
}
public GregorianDate set(int year, int month, int day, int hour, int minute, int second,
int millisecond, TimeZone timeZone) {
ofTimeZone(timeZone);
this.year = year;
this.month = month;
this.dayOfMonth = day;
this.hourOfDay = hour;
this.minute = minute;
this.second = second;
this.millisecond = millisecond;
updateTime();
return this;
}
protected void updateTime() {
super.updateTime();
int dayOfWeek = (int) ((RELATIVE_DAY_OF_WEEK + currentDays - RELATIVE_DAYS - 1) % 7 + 1);
if (dayOfWeek <= 0) {
dayOfWeek = dayOfWeek + 7;
}
// 星期
this.dayOfWeek = dayOfWeek;
// 当年第几个星期
this.weekOfYear = daysOfYear % 7 == 0 ? daysOfYear / 7 : daysOfYear / 7 + 1;
// 校验日期回设
if (!validate()) {
setTime(this.timeMills, true);
}
// 当月第几个星期
this.weekOfMonth = dayOfMonth % 7 == 0 ? dayOfMonth / 7 : dayOfMonth / 7 + 1;
afterDateChange();
}
protected void afterDateChange() {
}
private boolean validate() {
// 去除year的校验(<1),支持公元元年之前的日期
if (/*this.year <= 0
||*/ this.month < 1 || this.month > 12
|| this.dayOfMonth < 1 || this.dayOfMonth > 31
|| this.hourOfDay < 0 || this.hourOfDay > 23
|| this.minute < 0 || this.minute > 59
|| this.second < 0 || this.second > 59
|| this.millisecond < 0 || this.millisecond > 999
)
return false;
if (this.month == 4 || this.month == 6 || this.month == 9 || this.month == 11) {
if (dayOfMonth == 31) return false;
} else if (this.month == 2) {
if (dayOfMonth > 29) return false;
if (!leapYear && dayOfMonth == 29) return false;
}
return true;
}
public GregorianDate setYear(int year) {
if (this.year == year) {
return this;
}
this.year = year;
this.updateTime();
return this;
}
public GregorianDate setMonth(int month) {
if (this.month == month)
return this;
this.month = month;
updateTime();
return this;
}
public GregorianDate setDay(int day) {
if (this.dayOfMonth == day) return this;
this.dayOfMonth = day;
updateTime();
return this;
}
public GregorianDate setHourOfDay(int hourOfDay) {
if (this.hourOfDay == hourOfDay) return this;
this.hourOfDay = hourOfDay;
updateTime();
return this;
}
public GregorianDate setMinute(int minute) {
if (this.minute == minute) return this;
this.minute = minute;
updateTime();
return this;
}
public GregorianDate setSecond(int second) {
if (this.second == second) return this;
this.second = second;
updateTime();
return this;
}
public GregorianDate setMillisecond(int millisecond) {
if (this.millisecond == millisecond) return this;
this.millisecond = millisecond;
updateTime();
return this;
}
public int getDaysOfYear() {
return daysOfYear;
}
public int getDayOfWeek() {
return dayOfWeek;
}
public boolean isLeapYear() {
return leapYear;
}
public void setTime(long timeMills, boolean reset) {
// 如果毫秒没有变化不计算(注意-1不做处理)
if (this.timeMills == timeMills && !reset) {
return;
}
super.setTime(timeMills, reset);
// 以星期四(5)作为参考,星期六(7) 星期日(1) -6~0 1-7
int dayOfWeek = (int) ((RELATIVE_DAY_OF_WEEK + currentDays - RELATIVE_DAYS - 1) % 7 + 1);
if (dayOfWeek <= 0) {
dayOfWeek = dayOfWeek + 7;
}
this.dayOfWeek = dayOfWeek;
this.weekOfMonth = dayOfMonth % 7 == 0 ? dayOfMonth / 7 : dayOfMonth / 7 + 1;
this.weekOfYear = daysOfYear % 7 == 0 ? daysOfYear / 7 : daysOfYear / 7 + 1;
this.afterDateChange();
}
public void setTime(long timeMills) {
setTime(timeMills, false);
}
/**
* 是否同一天(不考虑时辰),需要加上时差
*
* @param sourceTimemills
* @param targetTimeMills
* @return
*/
public static boolean isSameDay(long sourceTimemills, long targetTimeMills) {
return (sourceTimemills + RELATIVE_MILLS + getDefaultOffset()) / 86400000 == (targetTimeMills + RELATIVE_MILLS + getDefaultOffset()) / 86400000;
}
@Override
public String toString() {
return format();
}
public String toDateString() {
return year + "-" + month + "-" + dayOfMonth + " " + hourOfDay + ":" + minute + ":" + second + "." + millisecond;
}
/**
* 格式化
*
* @return
*/
public String format() {
return format('-', ':');
}
/**
* 格式化
*
* @param dateSyntax 年月日分隔符 默认'-'
* @param timeSyntax 时分秒分隔符 默认':'
* @return
*/
public String format(char dateSyntax, char timeSyntax) {
StringBuilder builder = new StringBuilder(19);
int year = this.year;
if (year < 0) {
builder.append("-");
year = -year;
}
int y1 = year / 100;
int y2 = year % 100;
builder.append(DateTemplate.DigitTens[y1]);
builder.append(DateTemplate.DigitOnes[y1]);
builder.append(DateTemplate.DigitTens[y2]);
builder.append(DateTemplate.DigitOnes[y2]);
builder.append(dateSyntax);
builder.append(DateTemplate.DigitTens[month]);
builder.append(DateTemplate.DigitOnes[month]);
builder.append(dateSyntax);
builder.append(DateTemplate.DigitTens[dayOfMonth]);
builder.append(DateTemplate.DigitOnes[dayOfMonth]);
builder.append(' ');
builder.append(DateTemplate.DigitTens[hourOfDay]);
builder.append(DateTemplate.DigitOnes[hourOfDay]);
builder.append(timeSyntax);
builder.append(DateTemplate.DigitTens[minute]);
builder.append(DateTemplate.DigitOnes[minute]);
builder.append(timeSyntax);
builder.append(DateTemplate.DigitTens[second]);
builder.append(DateTemplate.DigitOnes[second]);
return builder.toString();
}
/**
* Y 4位数年份
*
y 2位年份
*
M 格式化2位月份
*
d 格式化2位天
*
H 格式化24制小时数
*
m 格式化2位分钟
*
s 格式化2位秒
*
S 格式化3位毫秒
*
a 上午/下午
*
* @param template
* @return
*/
public String format(String template) {
if (template == null) return format();
StringBuilder writer = new StringBuilder();
formatTo(template, writer);
return writer.toString();
}
public void formatTo(String template, Appendable appendable) {
DateTemplate.formatTo(year, month, dayOfMonth, hourOfDay, minute, second, millisecond, dayOfWeek, daysOfYear, weekOfMonth, weekOfYear, timeZone, template, appendable);
}
public void formatTo(String template, Appendable appendable, boolean escapeQuot) {
DateTemplate.formatTo(year, month, dayOfMonth, hourOfDay, minute, second, millisecond, dayOfWeek, daysOfYear, weekOfMonth, weekOfYear, timeZone, template, appendable, escapeQuot);
}
public int compareTo(GregorianDate o) {
if (timeMills == o.timeMills) {
return 0;
}
return timeMills > o.timeMills ? 1 : -1;
}
}