<dependency>
<groupId>joda-timegroupId>
<artifactId>joda-timeartifactId>
<version>2.10version>
dependency>
通过org.joda.time.DateTime的类图我们可以看到,其核心的Field主要是iMillis和iChronology。DateTime的核心是UTC 1970年1月1日以来的毫秒值 iMillis,而所有对日期的操作通过 年表 iChronology 来实现。从根本上讲,年表是一种日历系统 — 一种计算时间的特殊方式 — 并且是一种在其中执行日历算法的框架。
org.joda.time.base.BaseDateTime
/** The millis from 1970-01-01T00:00:00Z */
private volatile long iMillis;
/** The chronology to use */
private volatile Chronology iChronology;
目前Joda-Time提供8种日历系统:
图1 org.joda.time.DateTime的类图
图2 年表核心类图
org.joda.time.chrono.AssembledChronology
Abstract Chronology that enables chronologies to be assembled from a container of fields.
org.joda.time.chrono.ZonedChronology
Wraps another Chronology to add support for time zones.
org.joda.time.chrono.ZonedChronology.ZonedDateTimeField
A DateTimeField that decorates another to add timezone behaviour.
org.joda.time.chrono.ISOChronology
Implements a chronology that follows the rules of the ISO8601 standard, which is compatible with Gregorian for all modern dates.
org.joda.time.chrono.GregorianChronology
Implements a pure proleptic Gregorian calendar system, which defines every fourth year as leap, unless the year is divisible by 100 and not by 400. This improves upon the Julian calendar leap year rule.
其核心是通过 java.lang.System#currentTimeMillis 获取系统当前毫秒值。相关构造方法:
相关构造方法:
解析给定对象,并转换成Joda-Time对象。支持的对象包括java.util.Calendar, java.lang.Long, java.lang.String, java.util.Date, org.joda.time.ReadableInstant等。其中转换通过对象转换器实现,公共接口为org.joda.time.convert.InstantConverter。
通过制定年、月、日、时、分、秒、毫秒值创建对象。
Joda-Time对日期的操作是通过org.joda.time.DateTime.Property来实现的,Property将DateTime与日历系统的org.joda.time.DateTimeField进行绑定,并最终通过DateTimeField的操作来实现对日期的操作。
以下摘录自org.joda.time.DateTime.Property的JavaDoc。
DateTime.Property binds a DateTime to a DateTimeField allowing powerful datetime functionality to be easily accessed.
The simplest use of this class is as an alternative get method, here used to get the year ‘1972’ (as an int) and the month ‘December’ (as a String).
DateTime dt = new DateTime(1972, 12, 3, 0, 0, 0, 0);
int year = dt.year().get();
String monthStr = dt.month().getAsText();
Methods are also provided that allow date modification. These return new instances of DateTime - they do not modify the original.
The example below yields two independent immutable date objects 20 years apart.
DateTime dt = new DateTime(1972, 12, 3, 0, 0, 0, 0);
DateTime dt20 = dt.year().addToCopy(20);
Serious modification of dates (ie. more than just changing one or two fields) should use the MutableDateTime class.
DateTime.Propery itself is thread-safe and immutable, as well as the DateTime being operated on.
根据格式化pattern创建org.joda.time.format.DateTimeFormatter
根据时区调整Joda-Time(变更毫秒值)
调用日历系统获取各字段值,构建格式化日期串
org.joda.time.format.DateTimeFormatterBuilder.Composite#printTo(java.lang.Appendable, long, org.joda.time.Chronology, int, org.joda.time.DateTimeZone, java.util.Locale)
public void printTo(
Appendable appendable, long instant, Chronology chrono,
int displayOffset, DateTimeZone displayZone, Locale locale) throws IOException {
InternalPrinter[] elements = iPrinters;
if (elements == null) {
throw new UnsupportedOperationException();
}
if (locale == null) {
// Guard against default locale changing concurrently.
locale = Locale.getDefault();
}
int len = elements.length;
for (int i = 0; i < len; i++) {
elements[i].printTo(appendable, instant, chrono, displayOffset, displayZone, locale);
}
}
org.joda.time.chrono.GregorianChronology#calculateFirstDayOfYearMillis
long calculateFirstDayOfYearMillis(int year) {
// Initial value is just temporary.
int leapYears = year / 100;
if (year < 0) {
// Add 3 before shifting right since /4 and >>2 behave differently
// on negative numbers. When the expression is written as
// (year / 4) - (year / 100) + (year / 400),
// it works for both positive and negative values, except this optimization
// eliminates two divisions.
leapYears = ((year + 3) >> 2) - leapYears + ((leapYears + 3) >> 2) - 1;
} else {
leapYears = (year >> 2) - leapYears + (leapYears >> 2);
if (isLeapYear(year)) {
leapYears--;
}
}
return (year * 365L + (leapYears - DAYS_0000_TO_1970)) * DateTimeConstants.MILLIS_PER_DAY;
}
org.joda.time.DateTimeZone#convertUTCToLocal
/**
* Converts a standard UTC instant to a local instant with the same
* local time. This conversion is used before performing a calculation
* so that the calculation can be done using a simple local zone.
*
* @param instantUTC the UTC instant to convert to local
* @return the local instant with the same local time
* @throws ArithmeticException if the result overflows a long
* @since 1.5
*/
public long convertUTCToLocal(long instantUTC) {
int offset = getOffset(instantUTC);
long instantLocal = instantUTC + offset;
// If there is a sign change, but the two values have the same sign...
if ((instantUTC ^ instantLocal) < 0 && (instantUTC ^ offset) >= 0) {
throw new ArithmeticException("Adding time zone offset caused overflow");
}
return instantLocal;
}
@Test
public void week() {
// 获取当前日期
DateTime dateTime = new DateTime();
String pattern = "yyyy-MM-dd HH:mm:ss";
// 本周开始时间 00:00:00
String monday = dateTime.dayOfWeek().withMinimumValue().withTimeAtStartOfDay().toString(pattern);
// 本周结束时间 23:59:59
String sunday = dateTime.dayOfWeek().withMaximumValue().millisOfDay().withMaximumValue().toString(pattern);
System.out.println(monday);
System.out.println(sunday);
}
@Test
public void getWeek() {
// 创建DateTime
DateTime dateTime = new DateTime(2018, 12, 31, 0, 0, 0);
// 获取年
int weekyear = dateTime.getWeekyear();
// 获取周
int weekOfWeekyear = dateTime.getWeekOfWeekyear();
// 结果:1
System.out.println(weekyear);
// 结果:2019
System.out.println(weekOfWeekyear);
}
@Test
public void format() {
// toString
String date = new DateTime().toString("yyyy-MM-dd");
System.out.println(date);
}
@Test
public void parse() {
// create formatter
DateTimeFormatter formater = DateTimeFormat.forPattern("yyyy-MM-dd");
// parse date string
DateTime dateTime = formater.parseDateTime("2019-04-25");
// 2019-04-25T00:00:00.000+08:00
System.out.println(dateTime);
}
Joda-Time 简介
Calendar systems
joda-time