Element centric approach

Table Of Contents

What is this about?
Direct access to temporal state
Query or manipulation
Support for elements
How to query: Choose the right element for your problem
Minimum and maximum of element values
Manipulations - part one: Setting element values
Manipulations - part two: Using chronological operators
Manipulations - part three: Timezone-related operators
Summary

What is this about?

Chronological queries and manipulations can be extremely complex. Many different variations are possible. In order to avoid API-bloat by an excessive count of specialized query- and manipulation-methods, Time4J has choosen a design mainly based on chronological elements which serve as main parameters to common queries and manipulations. Every element implements the basic interface ChronoElement. It also has a value type indicated by its type parameter V. This page serves as general guideline how to use elements in Time4J. As side remark: Other libraries use the term "field" instead of "element".

The traditional Java-class java.util.Calendar also uses a parameter-driven approach, but it is not type-safe at all, unfortunately.

The libraries java.time.* (built in Java 8) and Joda-Time have gone another way: They want the user to call specialized methods like getHourOfDay() and consider the element-centric approach as low-level, designed mainly for framework-developers. Users will therefore find many more such special methods in classes like java.time.ZonedDateTime. If you are a beginner then you might value that design as more convenient on first glance. But wait: You can easily get lost in method jungle, and the count of offered specialized methods is despite of the size of the API often not sufficient. As we will see later, the element-centric approach enables far more features with less methods.

Direct access to temporal state

In contrast to most other libraries, Time4J only knows a very limited count of direct getters, in practice only for the primary temporal state of a chronological entity. Example:

	PlainDate date = PlainDate.of(2014, 3, 21);
	int dayOfMonth = date.getDayOfMonth();
	int month = date.getMonth();
	int year = date.getYear();
	// now you might process these primary state values and send to external interfaces

Query or manipulation

If you have any kind of chronological problem/task you must first decide if you want to query for chronological details or manipulate it. When querying you usually use get(...)-methods, in some few boolean-orientated cases also methods with "is"-prefix. Otherwise you use with(...)-methods. Element identifiers serve as main parameter.

Support for elements

You can find out if any element is supported by the concrete temporal type in question as follows:

	ChronoElement<Integer> element = ...;
	PlainDate date = PlainDate.of(2014, 3, 21);
	boolean supported = date.contains(element);

Not every element is supported by every type. For example: PlainDate does not support the element PlainTime.MINUTE_OF_HOUR. The javadoc of every basic type class documents the supported elements in detail.

How to query: Choose the right element for your problem

As said before, in most cases you use getters. If you are interested in the day of a calendar month then you choose the element PlainDate.DAY_OF_MONTH. Finally you just call the method date.get(DAY_OF_MONTH). This expression uses a static import for the constant to assist for shorter readable code. The result of such a getter has always the value return type of given element. Many elements have the value type Integer. But it can also be any other kind of immutable type, for example enums like net.time4j.Month or net.time4j.Weekday. Examples:

	PlainDate date = PlainDate.of(2014, 3, 21);

	ChronoElement<Integer> element = Weekmodel.of(Locale.US).weekOfYear();
	int usWeekOfYear = date.get(element); // 12th week of year in US

	Weekday dayOfWeek = date.get(PlainDate.DAY_OF_WEEK); // FRIDAY

Now the question arises where to find such element identifiers. You find the most common elements as static constants in the surrounding chronological entity. If the element is calendrical then you find it in most cases directly in the class PlainDate. If the element is related to wall time then you usually find it in the class PlainTime. Week-related elements are located in the extra class Weekmodel via methods due to their localized background. Most of these elements can also be used in the classes PlainTimestamp and Moment. In case of doubt just refer to the documentation of the classes or call the method contains(element).

Minimum and maximum of element values

All elements have a default minimum and maximum. You can get these estimated range constraints by calling getDefaultMinimum() or getDefaultMaximum() on a given element. These constraints only represent typical values for minimum and maximum. To get the real context-dependent minima and maxima, you can do this:

	PlainDate date = PlainDate.of(2012, 2, 4);
	int minDayOfFebruary = date.getMinimum(DAY_OF_MONTH); // 1
	int maxDayOfFebruary = date.getMaximum(DAY_OF_MONTH); // 29

Manipulations - part one: Setting element values

Like with queries, you have to decide which element you want to manipulate. The standard way is just to set the partial value associated with the element. This can have side effects however. For example setting the month to another month can have impact on the day of the month if latter one does not fit into new month length.

	import static net.time4j.PlainDate.MONTH_OF_YEAR;
	import static net.time4j.Month.FEBRUARY;

	PlainDate date = PlainDate.of(2014, 3, 31);
	date = date.with(MONTH_OF_YEAR, FEBRUARY); // 2014-02-28

Note that also the with(ChronoElement<V>, V)-method allows and even requires values of the type V what the element is designed for, not just integers. In given example above, the value type is the enum net.time4j.Month.

Manipulations - part two: Using chronological operators

The simple use-case of just setting element values is often not sufficient. More complex manipulations are needed. Time4J offers many element-based operators of type ChronoOperator. Its type parameter determines on which temporal types such an operator can be applied. If you want to know which operators are offered by a given element then look at the type of the element interface which is a subtype of ChronoElement and defines additional operator methods. Or just use the auto completion feature of your IDE and type the dot key... After having done this, you can use this operator as argument for the alternative method with(ChronoOperator<T>).

Let us now consider the frequent use-case of setting a date to the end of a month. This problem is related to the day-of-month which must be adjusted. That means we have to choose the element PlainDate.DAY_OF_MONTH which is of type ProportionalElement. Its super type AdjustableElement defines the method maximized() which exactly does what is needed, maximizing the day of month in context of current month:

	import static net.time4j.PlainDate.DAY_OF_MONTH;

	PlainDate date = PlainDate.of(2014, 4, 14);
	date = date.with(DAY_OF_MONTH.maximized()); // 2014-04-30

Manipulations - part three: Timezone-related operators

Many operators are not just of type ChronoOperator<T> but of type ElementOperator<T>. These operators have extra methods for the conversion to a version which is either applicable on PlainTimestamp or - by help of a timezone - on Moment. Examples:

	import static net.time4j.PlainTime.DIGITAL_HOUR_OF_DAY;

	PlainTimestamp tsp = PlainDate.of(2014, 10, 19).atTime(13, 45);
	tsp = tsp.with(DIGITAL_HOUR_OF_DAY.incremented()); // 2014-10-19T14:45

	TZID tz = AMERICA.SAO_PAULO; // Brazil timezone
	Moment moment = tsp.inTimezone(tz); // 2014-10-19T16:45:00,000000000Z
	moment = moment.with(DIGITAL_HOUR_OF_DAY.minimized().inTimezone(tz));
	// note: change to summer time in Brazil, the minimum hour is T01, not midnight
	System.out.println(moment.toZonalTimestamp(tz)); // 2014-10-19T01:45

Summary

A standard approach for solving many problems and tasks looks like:

  1. Describe your task in terms of chronological elements
  2. Do you want to query or manipulate? => get() or with()
  3. Decide which element to use for your problem
  4. In case of manipulation: Set the element value or use an operator?
  5. If you use an operator then look at the offered operator methods in the element interface.

A parameter-based approach is of course an old idea which was already realized in the class java.util.Calendar using integer constants. Time4J has introduced following innovations, however: