TL; DR
ZoneId z = ZoneId.of( "America/Montreal" ); LocalDate today = LocalDate.now( z ); ZonedDateTime start = today.with( TemporalAdjusters.firstDayOfMonth() ) .atStartOfDay( z ) ; ZonedDateTime stop = today.with( TemporalAdjusters.firstDayOfNextMonth() ) .atStartOfDay( z ) ;
- A time zone is required to determine the date.
- Itβs better to specify explicitly than rely implicitly on the current time zone of JVMs.
Avoid obsolete classes
You are using the nasty old time classes, now obsolete, superseded by the java.time classes.
Using java.time
This job is much simpler with java.time classes.
LocalDate
The LocalDate class represents a date value only without time and without a time zone.
Timezone
The time zone is critical for determining the date. At any given moment, the date changes around the world by zone. For example, a few minutes after midnight in Paris, France is a new day, still "yesterday" in Montreal Quebec .
Specify the time zone name in continent/region format, such as America/Montreal , Africa/Casablanca or Pacific/Auckland . Never use an abbreviation of 3-4 characters, for example EST or IST , as they are not real time zones, and are not standardized or even unique (!).
ZoneId z = ZoneId.of( "America/Montreal" ); LocalDate today = LocalDate.now( z );
Always specify the time zone explicitly. If this parameter is omitted, the current default JVMs timezone is implicitly applied. This default value can be changed at any time by any code in any application in the JVM. Therefore, if this is important, ask the user to specify the desired / expected time zone. If this is not important, you can ask to explicitly state explicitly your intentions in your code, rather than ambiguity, relying on an implicit default value.
ZoneId z = ZoneId.systemDefault();
TemporalAdjuster
The TemporalAdjuster interface provides classes that can adjust date and time values. The TemporalAdjusters class provides several convenient implementations.
LocalDate firstOfThisMonth = today.with( TemporalAdjusters.firstDayOfMonth() );
ZonedDateTime
To include this date only on a date with the time of day, use the ZoneId time zone to get the ZonedDateTime object.
Do not assume that the first moment of the day is 00:00:00. Due to anomalies such as daylight saving time (DST), the first moment may be something like 01:00:00. Let java.time figure this out by calling atStartOfDay .
ZonedDateTime zdtStartOfMonth = firstOfThisMonth.atStartOfDay( z );
Half open
You are mistaken in trying to determine the last moment of the month. This last moment has an infinitely divisible fractional second. Trying to solve a certain level of detail is not recommended, since different systems use different degrees of detail. Older Java time classes use milliseconds, some databases, such as Postgres, use microseconds, java.time classes use nanoseconds, and other systems use other options.
A more reasonable approach, usually used to work with a time date for defining time intervals, is Half-Open, where the beginning is included, while the ending is exceptional. This means that the month begins at the first moment of the day of the first month and passes until the first, but not including the first moment of the next month.
LocalDate firstOfNextMonth = today.with( TemporalAdjusters.firstOfNextMonth() );
Tune in to your time zone to get a specific moment.
ZonedDateTime zdtStartOfNextMonth = firstOfNextMonth.atStartOfDay( z );
The logic of comparing the moment with this period of time: "Is this moment (a) equal to or after the beginning and (b) less than the end?". Note the absence of "or equal to" in part "b". This means that we get to the end, but do not include the ending.
Also, a shorter way of saying part βaβ is βnot earlier than the start.β Therefore, we can ask easier: "This moment is not until the beginning And until the end?".
ZonedDateTime moment = ZonedDateTime.now( z ); Boolean spanContainsMoment = ( ! moment.isBefore( zdtStartOfMonth ) ) && ( moment.isBefore( zdtStartOfNextMonth ) ) ;
By the way, the standard ISO 8601 format for formatting a textual representation of a time span uses a slash to join the beginning and end.
String output = zdtStartOfMonth.toString() + "/" + zdtStartOfNextMonth.toString() ;
Interval
You can imagine this time span using the Interval class in ThreeTen-Extra . This class tracks the start and end in UTC as Instant objects. You can extract Instant from the ZonedDateTime object.
Interval interval = Interval.of( zdtStartOfMonth.toInstant() , zdtStartOfNextMonth.toInstant() );
YearMonth
By the way, you can find the YearMonth class useful in your work.
About java.time
The java.time framework is built into Java 8 and later. These classes supersede the nasty old legacy datetime classes such as java.util.Date , Calendar and SimpleDateFormat .
The Joda-Time project, now in maintenance mode , is advised to switch to the java.time classes.
To learn more, see the Oracle Tutorial . And search for qaru for many examples and explanations. JSR 310 specification .
Where to get java.time classes?
- Java SE 8 and SE 9 and later
- Built in.
- Part of the standard Java API with integrated implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6 and SE 7
- Most of the functionality of java.time is ported back to Java 6 and 7 in ThreeTen-Backport .
- Android
- The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) specifically for Android.
- See How to use ThreeTenABP ....
The ThreeTen-Extra project extends java.time with additional classes. This project is a proof of possible future additions to java.time. Here you can find useful classes such as Interval , YearWeek , YearQuarter and more .