7
votes

I would like to have a class to represent a particular week of year - for example today it's 29 November, which is exactly week number 48 of year 2014. So, one example implementation would be:

import java.time.Year;

public class WorkingWeek {
    private int weekNumber;
    private Year year;
}

Another conception is to have just a week's Monday date in a WorkingWeek class, but I feel it's less intuitive and I will frequently use week-in-year number.

Is there a class in Java 8 Time API that would fit best my requirements? Or if not, what would be recommended approach?

3
What is your question?Sotirios Delimanolis
What is your question?Unheilig
Seems to me the question is "Is there a class to represent week-of-year in java.time or some other such library?".Basil Bourque

3 Answers

12
votes

You need to specify your definition of a week.

String Representation Of A Year-Week

One option is strings. The ISO 8601 standard defines a week as beginning on a Monday, ending on Sunday, with the first week of the year being the first to contain a Thursday, resulting in 52 or 53 weeks a year.

The standard also defines a string representation for this week-of-year span of time in the format of YYYY-Www (or omitting hypen, YYYYWww) such as 2014-W07. A day within the week is represented by a digit where Monday is 1 and Sunday is 7, in the format YYYY-Www-D (or omitting hyphen, YYYYWwwD) such as 2014-W07-2 (a Tuesday in 7th week of year). The W is important to disambiguate from a year-month such as 2014-07 being July of 2014.


java.time

The java.time package built into Java 8 and later is inspired by Joda-Time but entirely re-architected. See Tutorial.

In java.time, an Instant is a moment on the timeline in UTC. Apply a time zone (ZoneId) to get a ZonedDateTime. Use LocalDate to get a date-only value with no time-of-day and no time zone.

Note that determining the first moment of a day in java.time requires an extra step when compared to Joda-Time: We must go through the LocalDate class to call its atStartOfDay method.

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
LocalDate firstDayOfThisWeek = now.toLocalDate ().with ( DayOfWeek.MONDAY );
LocalDate firstDayOfNextWeek = firstDayOfThisWeek.plusWeeks ( 1 );
ZonedDateTime thisWeekStart = firstDayOfThisWeek.atStartOfDay ( zoneId );
ZonedDateTime nextWeekStart = firstDayOfNextWeek.atStartOfDay ( zoneId );

Unfortunately, java.time lacks the equivalent of Joda-Time's Interval.

Fortunately we have ThreeTen Extra, the project that extends java.time (310 being the number of the JSR defining java.time). This library includes an Interval class that integrates with java.time. This Interval class is more limited than that of Joda-Time as it supports only Instant objects without time zones (always in UTC).

Caution: The ThreeTen-Extra project reserves the right to change its interfaces and/or implementations. While intended to be useful as-is, it also serves as an experimental proving ground for classes that may be eventually incorporated into java.time. I gladly make use of ThreeTen-Extra, but you must make your own risk-benefit decision.

// This next line requires adding the `ThreeTen Extra` library to your project.
Interval interval = Interval.of ( thisWeekStart.toInstant () , nextWeekStart.toInstant () );  // "Interval" is part of ThreeTen-Extra project, not built into Java 8.

Dump to console.

System.out.println ( "now: " + now + " thisWeekStart: " + thisWeekStart + " nextWeekStart: " + nextWeekStart + " interval: " + interval );

now: 2016-01-15T18:10:48.143-05:00[America/Montreal] thisWeekStart: 2016-01-11T00:00-05:00[America/Montreal] nextWeekStart: 2016-01-18T00:00-05:00[America/Montreal] interval: 2016-01-11T05:00:00Z/2016-01-18T05:00:00Z

You can determine the week-of-year as defined by the ISO 8601 standard. Note the "week based" terms. Near the beginning or ending of the year, a date will be in one calendar year while its ISO 8601 week’s year may be ±1.

ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime now = ZonedDateTime.now ( zoneId );
int weekOfYear = now.get ( IsoFields.WEEK_OF_WEEK_BASED_YEAR );
int weekBasedYear = now.get ( IsoFields.WEEK_BASED_YEAR );
System.out.println ( "weekOfYear: " + weekOfYear + " of weekBasedYear: " + weekBasedYear );

weekOfYear: 2 of weekBasedYear: 2016


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.


Joda-Time

UPDATE: The Joda-Time project is now in maintenance mode and advises migration to the java.time classes. I am leaving this section intact as history.

The Joda-Time 2.5 library offers the Interval class to represent a span of time as a pair of specific moments in time along the timeline of the Universe. Each moment is represented by the DateTime class.

Half-Open

Joda-Time uses the Half-Open [) approach to defining spans of time. The beginning is inclusive while the ending is exclusive. This is generally the best way to work with such spans of time. Search StackOverflow for many examples and discussions.

ISO 8601 in Joda-Time

Joda-Time uses the ISO 8601 definition of weeks. Also, Joda-Time uses ISO 8601 as its defaults for parsing and generating string representations of date-time values.

DateTimeZone zone = DateTimeZone( "America/Montreal" );
// Get first moment of a Monday. Inclusive.
DateTime start = new DateTime( 2014, 11, 24, 0, 0, 0, zone ); // Handle exception thrown if occurring during a Daylight Saving Time gap.
DateTime stop = start.plusWeeks( 1 ); // First moment of following Monday. Exclusive.
Interval week = new Interval ( start, stop );

First Monday

Search StackOverflow for many questions and answers on finding the first Monday of a week.

LocalDate (date-only)

While you might well be tempted to use LocalDate objects (date only, no time-of-day) to build an Interval. That would be sensible and useful. Unfortunately, the implementation of Interval supports only DateTime objects, not LocalDate.

6
votes

YearWeek Class

The ThreeTen-Extra project has a class for YearQuarter. The original question looks like it is asking for a YearWeek class. Work has been done to add such a class to ThreeTen-Extra.

2
votes

Another possibility is to use the class CalendarWeek in my library Time4J. Example:

CalendarWeek now = SystemClock.inLocalView().now(CalendarWeek.chronology());
int actual = now.getWeek(); // 35 on 2016-08-29
int max = now.getMaximum(CalendarWeek.WEEK_OF_YEAR); // 52 in year 2016

This class is also modelled as date interval and can be converted to a common DateInterval (from Monday to Sunday) offering stream support via its method toFlexInterval().