2

I understand that the main concepts in Joda time are:

  • instant in time with millisecond granularity (represented by DateTime)
  • Interval representing the span of two instances
  • duration which is really only an long number of milliseconds
  • a Period which is like an un-anchored length of time defined not in terms of a precise number of milliseconds but according to common civil usage of concepts like "month", "day", etc.

How should I then represent, e.g. "February 2014". Is there a type for that? I understand I can represent it as a tuple of an instant in time (at the beginning or end of that month) and a Period of one month. But this means that I define my own types on top of Joda and I have to decide on a convention on whether the instant should be at the beginning or the end of the period I wish to define (since both options are clearly viable).

The use case I am having is that I need to produce reports and it does make a difference (on the report header / description) if the user:

  • simply happened to select two instances in time which were exactly at the start and end of February 2014
  • selected to produce a monthly report for February 2014.

So, is there a single type for that? and, failing, that, what would be the best approach to represent that.

Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
  • I had the same requirement when implementing reports. I ended up with implementing my own class, containing startdate, enddate, periodName, periodType(week,month,quarter,...) and some methods like `isInPeriod(DateTime), isAfterPeriod(DateTime), isBeforePeriod(DateTime)`. – dognose Feb 21 '14 at 18:04
  • FYI, the [*Joda-Time*](http://www.joda.org/joda-time/) project is now in [maintenance mode](https://en.wikipedia.org/wiki/Maintenance_mode), with the team advising migration to the [*java.time*](http://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes. See [Tutorial by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Apr 24 '18 at 04:49

3 Answers3

2

Class YearMonth in Joda-Time provides this functionality, for months.

So, to get the January 1970 and the seconds-since-the-epoch values at the start and end instances of the first month of the Epoch:

    YearMonth yearMonth = new YearMonth (1970, 1);
    Interval interval = yearMonth.toInterval(DateTimeZone.UTC);
    int startSSE = (int) TimeUnit.MILLISECONDS.toSeconds(interval.getStartMillis());
    int   endSSE = (int) TimeUnit.MILLISECONDS.toSeconds(interval.getEndMillis());
    System.out.printf("%d -> %d (%4.6f days)\n", startSSE, endSSE, ((float)(endSSE-startSSE))/(24*60*60));
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Marcus Junius Brutus
  • 26,087
  • 41
  • 189
  • 331
  • FYI, the [*Joda-Time*](http://www.joda.org/joda-time/) project is now in [maintenance mode](https://en.wikipedia.org/wiki/Maintenance_mode), with the team advising migration to the [*java.time*](http://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes. See [Tutorial by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). The *java.time* framework offers its own version of [`YearMonth`](https://docs.oracle.com/javase/10/docs/api/java/time/YearMonth.html). – Basil Bourque Apr 24 '18 at 04:47
1

tl;dr

Use the modern java.time classes that supplant both the Joda-Time project and the troublesome old legacy date-time classes originally bundled with the earliest versions of Java.

YearMonth.of( 2014 , Month.FEBRUARY )

java.time

The modern approach uses the java.time (JSR 310) classes that are the official successor to the Joda-Time project. Both projects are led by the same man, Stephen Colebourne.

YearMonth

How should I then represent, e.g. "February 2014".

Use the YearMonth, purpose-built for that.

You can specify a year and month by number, using sane numbering 1-12 for January-December (unlike the legacy classes).

YearMonth ym = YearMonth.of( 2014 , 2 ) ;  // 2014 February.

Or use the handy Month enum.

YearMonth ym = YearMonth.of( 2014 , Month.FEBRUARY ) ;

ISO 8601

To represent the year-month as text, use the standard ISO 8601 format: YYYY-MM

String output = ym.toString() ;

2014-02

And parse such strings.

YearMonth ym = YearMonth.parse( "2014-02" ) ; 

No database I know of has a year-month data type. So I suggest using a text column in your table that holds ISO 8601 formatted strings. Note that the alphabetical sorting of this ISO 8601 format happens to also be chronologically sorted.

Period

a Period of one month.

A Period is not attached to the timeline. Something like Period.ofMonths( 1 ) represents a generic month unrelated to any date or calendar.

In contrast, a YearMonth represents one specific month. A YearMonth is attached to the timeline.

Moments

If you need specific date-with-time moments such as for a query into a database, you will need to determine dates. The LocalDate class represents a date-only value without time-of-day and without time zone.

LocalDate ldStart = ym.atDay( 1 ) ;                 // First of the month.
LocalDate ldStop = ym.plusMonths( 1 ).atDay( 1 ) ;  // First day of the following month.

Determining date-with-time values requires a time zone.

A time zone is crucial in determining a date-with-time. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.

If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment, so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.

Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Montreal" ) ;  

Use that zone to let java.time determine the first moment of the day. Do not assume the day starts at 00:00:00. Some dates in some zones start at another time-of-day such as 01:00:00.

ZonedDateTime zdtStart = ldStart.atZone( z ) ;  // First moment of the day starting in the wall-clock time used by the people of a particular region (a time zone).
ZonedDateTime zdtStop = ldStop.atZone( z ) ;

You can then extract UTC values, which are usually used by databases to store a value in a column of SQL-standard TIMESTAMP WITH TIME ZONE.

Instant instantStart = zdtStart.toInstant() ;
Instant instantStop = zdtStop.toInstant() ;

JDBC

As of JDBC 4.2 and later, you can exchange java.time objects with your database.

myPreparedStatement.setObject( … , instantStart ) ;
myPreparedStatement.setObject( … , instantStop ) ;

Retrieval.

Instant instant = myResultSet.getObject( … , Instant.class ) ;

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
0

Currently my luck with the Joda-Time YearMonth class is not good so here is an alternative example using DateTimeFormatter.

public static DateTimeFormatter DATEFORMATTER = new DateTimeFormatterBuilder()
    .appendPattern("MMM, YYYY").toFormatter();

An example piece of code that I added a println() for your benefit.

/**
     * calculates the number of months between the upper and lower bounds 
     * example: int m = numberOfMonths("January, 2009", "May, 2000");   
     *          >m=104
     * Uses Joda
     * Months are full month names
     * @return
     */
    public int numberOfMonths(String upperMMMMcommaYYYY, String lowerMMMMcommaYYYY){
        DateTime upper = DATEFORMATTER.parseDateTime(upperMMMMcommaYYYY);
        DateTime lower = DATEFORMATTER.parseDateTime(lowerMMMMcommaYYYY);
        Months m = Months.monthsBetween(lower, upper);
        System.out.println(upperMMMMcommaYYYY.concat("printed from DateTime looks like ".concat(upper.toString(DATEFORMATTER))));
        return m.getMonths;

    }

the println result -> January, 2009 using DateTimeFormatter + DateTime looks like Jan, 2009

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Bill Thayer
  • 321
  • 2
  • 9
  • FYI, the [*Joda-Time*](http://www.joda.org/joda-time/) project is now in [maintenance mode](https://en.wikipedia.org/wiki/Maintenance_mode), with the team advising migration to the [*java.time*](http://docs.oracle.com/javase/10/docs/api/java/time/package-summary.html) classes. See [Tutorial by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). The *java.time* framework offers its own version of [`YearMonth`](https://docs.oracle.com/javase/10/docs/api/java/time/YearMonth.html). – Basil Bourque Apr 24 '18 at 04:48