Introduction to basic types

Table Of Contents

Four basic types describing a temporal entity
What is common to these types?
Immutability
Value object semantics
ISO-8601
How to create objects?
All types inherit from the super class ChronoEntity
All types inherit from the super class TimePoint, too
Format support
What is the main difference between these types?
Local versus global
How to convert between the basic types?

Four basic types describing a temporal entity

What is common to these types?

Immutability

Objects of these basic types cannot be changed themselves. Instead you create changed copies via with(...)-methods and need to assign the results to new variables - like in the Java-class java.math.BigDecimal: So never write date.with(DAY_OF_MONTH, 5); but date = date.with(DAY_OF_MONTH, 5); You can also chain these manipulation methods, but finally there must be an assignment to a variable.

A final note about immutability: This feature does not mean that you always get a new instance for manipulated entities. Sometimes you can also get a cached instance for better performance. The immutability just denotes that you cannot change the original object itself. Another aspect is that all immutable classes are final to ensure that nobody can make mutable classes out of it. This design enables complete safety in multiple-thread-environments.

Value object semantics

All temporal entities represent value objects. That means that they have identities based on their temporal state and not on object identity. So all entities must be compared by the use of equals(Object)-method and never by identity comparison operator ==.

Furthermore: Although there are interfaces or abstract super classes the basic types implement, applications should not use these interfaces for assignments and then work with the interfaces respective super classes. Instead applications always work directly with the final immutable types. You will also get more flexibility and power using the concrete classes to benefit from many extra methods. Note also that the abstract classes in Time4J are strongly generified. You can avoid using type parameters in most cases if you always use the concrete classes. Examples:

	// Bad example in old Java: Wide-spread use of java.util.Calendar, NOT RECOMMENDED
	Calendar cal = Calendar.getInstance();
	// do something with cal

	// Bad example in Time4J: DON'T DO THIS
	Calendrical<?, ?> date = PlainDate.of(2014, 3, 21);
	int dayOfMonth = date.get(PlainDate.DAY_OF_MONTH);
	TimePoint<?, ?> tsp = ... // difficult to construct from date!

	// Good example:
	PlainDate date = PlainDate.of(2014, 3, 21);
	int dayOfMonth = date.get(PlainDate.DAY_OF_MONTH);
	PlainTimestamp tsp = date.atStartOfDay();

Moral: Super types in Time4J are not designed for direct use by applications but for the construction of new chronological types or calendar systems. This is also strongly discouraged by excessive use of generic type parameters. However, the abstract super classes define common methods which can be used in concrete final types.

ISO-8601

All basic types closely follow the ISO-8601-standard. This is represented by the canonical output of its toString()-method. If you sort a list of temporal objects by this representation you will always get a chronological order. ISO-8601 is well designed for the international exchange of chronological data independent from locale settings.

Special details of ISO-8601-standard like the three different date representations (calendar date, ordinal date and week date) or the wall-time 24:00 are also implemented by PlainDate and PlainTime. Examples:

	System.out.println(PlainDate.of(2014, 4, 10));
	// output: 2014-04-10

	System.out.println(PlainTime.midnightAtEndOfDay());
	// output: T24

	System.out.println(PlainDate.of(2014, 4, 10).atTime(11, 45).atUTC());
	// output: 2014-04-10T11:45:00Z

How to create objects?

Generally, there are no public constructors because of the value object semantics and sometimes for better caching behaviour. Instead all temporal types offer static factory methods with prefix "of". That is the application-controlled way of creation. Another way is using a TimeSource as clock (machine-controlled) producing global timestamps. Examples:

	import static net.time4j.Month.MARCH;

	// application-controlled
	PlainDate date = PlainDate.of(2014, MARCH, 5); // 2014-03-05
	PlainTime time = PlainTime.of(14, 45); // 14:45
	PlainTimestamp tsp = PlainTimestamp.of(2014, 3, 5, 14, 45); // 2014-03-05T14:45
	Moment utc = Moment.of(86400, TimeScale.POSIX); // 1970-01-02T00:00:00,000000000Z

	// machine-controlled
	PlainDate today = SystemClock.inLocalView().today(); // current date created by a clock
	Moment now = SystemClock.currentMoment(); // machine timestamp created by a clock

All types inherit from the super class ChronoEntity

A ChronoEntity is simply a collection of partial temporal values. These partial values are called "elements". PlainDate consists mainly of year, month and day-of-month, but also contains other secondary elements like day-of-week. In a similar way, PlainTime contains various elements like hour-of-day, minute-of-hour etc. PlainTimestamp as combination of date and time contains both kinds of elements. Moment does not refer directly to these elements, but needs an additional timezone to process such local elements.

The partial values resp. element values of an entity are accessed by a simple getter. There are also various with(...)-methods as kind of immutable setters. In every case: You refer to a special element by giving an element-id (as constant). Such partial values are not limited to int- or long-primitives. They can also be enums or any other immutable type V. Examples:

	import static net.time4j.PlainDate.DAY_OF_WEEK;
	import static net.time4j.Month.MARCH;
	import static net.time4j.Weekday.MONDAY;

	PlainDate date = PlainDate.of(2014, MARCH, 5);
	Weekday dayOfWeek = date.get(DAY_OF_WEEK); // getter for a partial value: here the day of week
	date = date.with(DAY_OF_WEEK, MONDAY; // immutable setter: here go to monday within the same ISO-week

All types inherit from the super class TimePoint, too

There is also a time axis associated with the four basic types. A time axis in Time4J is defined as unique and bijective mapping from the internal state of such a type to a single long counter (sometimes technically represented as combination of two integers). Every time axis has an associated unit type U which controls the scaling steps how to measure distances on the axis.

You can apply a time arithmetic along such a time axis. That means, you can add or subtract time units creating changed immutable copies. Technically the associated counter of the time axis is incremented or decremented. Users can also calculate the distance on a given time axis in terms of units.

Adding time units is performed by methods with prefix "plus", subtracting by methods with prefix "minus". Distances on such a time axis can be calculated either in long primitives using a single time unit by the method start.until(end, {U - unit type}) or as time span (duration) by the method start.until(end, {TimeMetric}). In most cases unit types offer methods with the signature between(start, end) as alternative to the until(...)-methods. Examples:

	import static net.time4j.CalendarUnit.DAYS;
	import static net.time4j.Month.MARCH;

	// plus-arithmetic
	PlainDate start = PlainDate.of(2014, MARCH, 5);
	PlainDate end = start.plus(35, DAYS);
	System.out.println(end); // 2014-04-09

	// reverse way (minus)
	PlainDate earlier = end.minus(35, DAYS);
	System.out.println(earlier); // 2014-03-05

	// calculate the distance by until(...)
	long distance1 = start.until(end, DAYS);
	System.out.println(distance1 + " days"); // 35 days

	// alternative way of calculating the distance in days
	long distance2 = DAYS.between(start, end);
	System.out.println(distance2 + " days"); // 35 days

Format support

All basic types offer a print- and a static parse-method with the signatures public String print(TemporalFormatter<T> formatter) or public static T static parse(String input, TemporalFormatter<T> formatter). Example:

    String s =
	PlainDate.of(2014, 3, 21).print(
		ChronoFormatter.ofDatePattern(
			"MMMM/dd/uuuu", 
			PatternType.CLDR, 
			Locale.ENGLISH
		)
	);
    System.out.println(s); // March/21/2014

More details see the page The format and parse engine.

What is the main difference between these types?

Local versus global

The types PlainDate, PlainTime and PlainTimestamp represent local types which do not contain any timezone reference. Such local types can adapt the same representation in different regions of the world but nevertheless not happen at the same moment. For example the wall time "13:45" at the same calendar date is earlier in Australia than in Europe.

The type Moment is a global type which really represents an instant and can be expressed in different time scales, preferrably in POSIX and UTC. Technicaly spoken, a Moment is pure UTC, that is a UniversalTime which always refers to timezone offset UTC+00:00. However, such a global type can process local elements or format and parse any local input if you pass an additional timezone or offset information.

How to convert between the basic types?

In order to get a PlainTimestamp as combination of PlainDate and PlainTime, the class PlainDate offers various methods with prefix "at". Conversions between local and global types however require timezone-related methods. More details see the page Conversions between basic types.