Last date of next month in Java - java

How to get the last date of next month in Java?
Background: I have project, user only interested in the orders should be completed by the end of next month. So I need to get the last date of next month and compare to the order end date, if the order end date smaller than the last date of next month, that means this order should be selected out.
My solution is like this, but not sure is it the best one:
public static boolean shouldCompleteByNextMonth(final Date endDate) {
final LocalDate now = LocalDate.now(); // Get current local date.
final LocalDate nextMonth = now.plusMonths(1); // Get next month.
final int daysInNextMonth = nextMonth.lengthOfMonth(); // Get the length of next month
final LocalDate lastLocalDateOfNextMonth = nextMonth.plusDays(daysInNextMonth - now.getDayOfMonth()); // Get the last Date of next month
// default time zone
final ZoneId defaultZoneId = ZoneId.systemDefault();
// convert last locale date to a Date
final Date lastDateOfNextMonth = Date.from(lastLocalDateOfNextMonth.atStartOfDay(defaultZoneId).toInstant());
// Compare with the given endDate, if last date of next month is after it, return true, else, return false.
return lastDateOfNextMonth.after(endDate);
}

The TemporalAdjusters class contains some static TemporalAdjusters, amongst them lastDayOfMonth(), so you can do
LocalDate.now()
.plusMonth(1)
.with(TemporalAdjusters.lastDayOfMonth());

It seems easier to calculate the first day of the month after that and then substracting one day...
LocalDate targetDate = LocalDate.now()
.withDayOfMonth(1)
.plusMonths(2)
.minusDays(1);

tl;dr
YearMonth
.now(
ZoneId.of( "Africa/Tunis" )
)
.plusMonths( 1 )
.atEndOfMonth()
Returns a LocalDate.
.toString(): 2019-12-31
But… better to use Half-Open approach rather than last day of month.
YearMonth
The YearMonth class represents a month as a whole.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
YearMonth nextMonth = YearMonth.now( z ).plusMonths( 1 ) ;
Notice the use of time zone, ZoneId. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight on November 30th in Tokyo Japan is a new month, while in Toledo Ohio US it is still “last month”. So if at runtime the current moment is on the first or last day of the month, the time zone is required for accuracy in determining the current month.
Ask for a LocalDate for the last day of month.
LocalDate lastDayOfMonth = nextMonth.atEndOfMonth() ;
Half-Open
The approach commonly taken in defying a span of time is the Half-Open approach. The beginning is inclusive while the ending is exclusive.
This means a day begins with the first moment of the day (usually 00:00, but not always), and runs up to, but does not include, the first moment of the next day.
In your case, a month begi s on the first and runs up to, but does include, the first of the next month.
The Half-Open approach allows for spans of time that nearly abut one another without gaps.
Your search criteria logic should be find orders where the order is equal to or greater than the first of the month AND less than the first of the following month. In Java that would be >= && <.
A simpler form of that logic is find orders where the order date is not before the first of month AND *is before** the first of following month.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
YearMonth nextMonth = YearMonth.now( z ).plusMonths( 1 ) ;
LocalDate start = nextMonth.atDay( 1 ) ;
LocalDate stop = nextMonth.plusMonths( 1 ).atDay( 1 ) ;
LocalDateRange
Add the ThreeTen-Extra library to your project to benefit from LocalDateRange. This class represents a span of time as a pair of LocalDate objects. It offers handy methods for comparisons such as abuts and contains.
LocalDateRange nextMonthRange =
LocalDateRange.of(
nextMonth.atDay( 1 ) ,
nextMonth.plusMonths( 1 ).atDay( 1 )
)
;
Database
You mention a database in your Question.
Here is a rough-draft of example JDBC code for querying for orders through the next month. That is, orders whose due-date is before the first day of the month after next month.
Notice how we add two months to the current month, to get the month after next.
The key part of the SQL is: WHERE due_date_ < ? for a Half-Open query of dates running up to, but not including, the passed date (the first of month after next month).
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
LocalDate firstOfMonthAfterNextMonth = YearMonth.now( z ).plusMonths( 2 ).atDay( 1 ) ;
String sql = "SELECT order_number_ FROM order_ WHERE due_date_ < ? ; " ;
try(
Connection conn = myDataSource.getConnection() ;
PreparedStatement ps = conn.prepareStatement( sql ) ;
)
{
ps.setObject( 1 ; firstOfMonthAfterNextMonth ) ;
try(
ResultSet rs = ps.executeQuery() ;
)
{
while ( rs.next() ) {
…
}
}
} catch ( SQLException e ) {
e.printStackTrace();
…
} catch ( SQLTimeoutException e ) {
e.printStackTrace();
…
}

Related

How can I add 18 as day to current month using java Instant

I can get the current date using
Instant.now()
I am looking to get 18-<current month>-<current year>
I endorse Basil Bourque's answer. However, if you are looking for an Instant object, you can get it by adjusting the current OffsetDateTime at UTC to 18th day of the month as shown below:
public class Main {
public static void main(String[] args) {
Instant thisInstantOn18th = OffsetDateTime.now(ZoneOffset.UTC)
.with(ChronoField.DAY_OF_MONTH, 18)
.toInstant();
System.out.println(thisInstantOn18th);
}
}
Output:
2022-12-18T19:19:20.128313Z
Learn more about the modern Date-Time API from Trail: Date Time.
tl;dr
YearMonth // Represents a year and month only, no day of month.
.now(
ZoneId.of( "America/Edmonton" ) // Returns a `ZoneId` object.
)
.atDay( 18 ) // Returns a `LocalDate` object.
.format(
DateTimeFormatter
.ofPattern( "dd-MM-uuuu" )
) // Returns a `String` object.
Details
As Comments by Ole V.V. explain, you are using the wrong class. No need for Instant here.
To represent a date, use LocalDate.
To represent a year and month, use YearMonth.
Capture the current year-month.
Doing so requires a time zone. For any given moment, the date varies around the globe by time zone. So the current month could be simultaneously “next month” in Tokyo Japan while “last month” in Toledo Ohio.
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
YearMonth ym = YearMonth.now( z ) ;
If you want the current month as seen with an offset of zero hours-minutes-seconds from UTC, use ZoneOffset.UTC constant.
YearMonth ym = YearMonth.now( ZoneOffset.UTC ) ;
Apply a day of month to get a date.
LocalDate ld = ym.atDay( 18 ) ;
Generate text in standard ISO 8601 format.
String output = ld.toString() ;
Generate text in a specific format.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd-MM-uuuu" ) ;
String output = ld.format( f ) ;

How to obtain a list of zoneIds for which their day start is within past 5 minutes?

Say the server timestamp right now is 2022-01-26 16:02:00 PDT, I want to return list of timezones that have just passed the midnight (day start), in this case, 16:02 PDT is 2 minutes past 00:00 UTC, so return [UTC] in this case. How do I return a list of zoneIds (from this set: ZoneId.getAvailableZoneIds()) for a given timestamp T?
“PDT” is not a real time zone. I assume you meant America/Los_Angeles.
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
Define your desired moment as a ZonedDateTime.
LocalDate ld = LocalDate.parse( "2022-01-26" ) ;
LocalTime lt = LocalTime.parse( "16:02" ) ;
ZonedDateTime target = ZonedDateTime.of( ld , lt , z ) ;
Get your set of time zone names by calling ZoneId.getAvailableZoneIds.
Set< String > names = ZoneId.getAvailableZoneIds() ;
Loop those zone names. Obtain a ZoneId object for each. Use that zone object to adjust our target into that zone. Then determine the first moment of the day in that zone on that date.
Your Question makes the mistake of assuming the day starts at 00:00. Not true. Some dates in some zones start at another time such as 01:00. Let java.time determine the first moment of the day with a call to LocalDate#atStartOfDay.
Duration limit = Duration.ofMinutes( 5 ) ;
List< ZoneId > hits = new ArrayList <>( names.size() ) ;
for( String name : names )
{
ZoneId zoneId = ZoneId.of( name ) ;
ZonedDateTime zdt = target.withZoneSameInstant( ZoneId ) ;
ZonedDateTime startOfDay = zdt.toLocalDate().atStartOfDay( zoneId );
Duration d = Duration.between ( startOfDay , zdt ) ;
if( d.compareTo( limit ) <= 0 ) { hits.add( zoneId ) ; }
}
return List.of( hits ) ;
All of this code is rough draft, untested.

Get the start and end timestamp of the given timestamp in java

I had a timestamp in Unix with milliseconds, now I need the start of the day and end of the day with milliseconds for the same given timestamp.
Eg: 1637293359000 - Given timestamp
Start of the day to be 1637280000000
End of the day to be 1637323199000
In System default timezone you could try like this
ZoneId zoneId = ZoneId.systemDefault();
long timestamp = 1637293359000L;
LocalDate date = Instant.ofEpochMilli(timestamp).atZone(zoneId).toLocalDate();
LocalDateTime startDay = date.atStartOfDay();
LocalDateTime endDay = date.atTime(LocalTime.MAX);
System.out.println(startDay);
System.out.println(endDay);
long startDayLongValue = startDay.atZone(zoneId).toInstant().toEpochMilli();
long endDayLongValue = endDay.atZone(zoneId).toInstant().toEpochMilli();
System.out.println(startDayLongValue);
System.out.println(endDayLongValue);
We can achieve this using DateTime
import org.joda.time.DateTime;
long timestamp = 1629454215381L;
DateTime dateTime=new DateTime(timestamp );
long StartOfDayMillis = dateTime.withMillis(System.currentTimeMillis()).withTimeAtStartOfDay().getMillis();
long EndOfDayMillis = dateTime.withMillis(StartOfDayMillis).plusDays(1).minusSeconds(1).getMillis();
tl;dr
Here is the complete code to a record representing the day of a specified moment as seen in a particular time zone. A static factory method contains our logic.
package work.basil.example;
import java.time.*;
import java.util.Objects;
public record DayInMillis( long start , long end )
{
public static DayInMillis from ( final long countOfMillisSinceEpoch , final ZoneId zoneId )
{
Objects.requireNonNull( zoneId );
Instant instant = Instant.ofEpochMilli( countOfMillisSinceEpoch );
ZonedDateTime zdt = instant.atZone( zoneId );
LocalDate ld = zdt.toLocalDate();
ZonedDateTime start = ld.atStartOfDay( zoneId );
ZonedDateTime end = ld.plusDays( 1 ).atStartOfDay( zoneId );
Instant startInstant = start.toInstant();
Instant endInstant = end.toInstant();
long startMilli = startInstant.toEpochMilli();
long endMilli = endInstant.toEpochMilli();
System.out.println( "instant = " + instant );
System.out.println( "zdt = " + zdt );
System.out.println( "start/end = " + start + "/" + end );
System.out.println( "startInstant/endInstant = " + startInstant + "/" + endInstant );
System.out.println( "startMilli/endMilli = " + startMilli + "/" + endMilli );
System.out.println( "Duration (not necessarily 24 hours): " + Duration.between( startInstant , endInstant ) );
return new DayInMillis( startMilli , endMilli );
}
}
Example usage:
DayInMillis.from( 1_637_293_359_000L , ZoneId.of( "Asia/Tokyo" ) )
When run.
instant = 2021-11-19T03:42:39Z
zdt = 2021-11-19T12:42:39+09:00[Asia/Tokyo]
start/end = 2021-11-19T00:00+09:00[Asia/Tokyo]/2021-11-20T00:00+09:00[Asia/Tokyo]
startInstant/endInstant = 2021-11-18T15:00:00Z/2021-11-19T15:00:00Z
startMilli/endMilli = 1637247600000/1637334000000
Duration (not necessarily 24 hours): PT24H
dayInMillis = DayInMillis[start=1637247600000, end=1637334000000]
Parse count of seconds
Parse your input count of milliseconds since epoch of first moment of 1970 in UTC as a Instant object.
Instant instant = Instant.ofEpochMilli( 1_637_293_359_000L ) ;
Adjust into time zone
Specify the time zone by which you want to perceive dates.
ZoneId z = ZoneId.of( "America/Edmonton" ) ;
Adjust from UTC (an offset of zero hours-minutes-seconds) to that time zone.
ZonedDateTime zdt = instant.atZone( z ) ;
Get the date
Extract the date-only portion.
LocalDate ld = zdt.toLocalDate() ;
Start of day
Let java.time determine the first moment of the day. Never assume the day starts at 00:00. Some dates in some zones may start at another time such as 01:00.
ZonedDateTime start = ld.atStartOfDay( z ) ;
Half-Open
The last moment of the day is infinitely divisible. For this and other reasons, spans of time are usually best defined using Half-Open approach. In Half-Open, the beginning is inclusive while the ending is exclusive. This means the span of a day starts at the first moment of the day and runs up to, but does not include, the first moment of the following day.
ZonedDateTime end = ld.plusDays( 1 ).atStartOfDay( z ) ;
Now we have the span covered by a pair of ZonedDateTime objects. But you want to get a count of milliseconds since epoch for both of those.
Tracking count-from-epoch is awkward
Let me say, I do not recommend using a count-since-epoch for time-keeping. The values are inherently ambiguous as to both epoch and granularity. Furthermore, using such counts makes debugging difficult and errors hard to spot, as such values are meaningless to human readers.
ISO 8601
Instead, I suggest communicating such values textually in standard ISO 8601 format. The java.time classes use ISO 8601 formats by default when parsing/generating text.
String outputStart = start.toInstant().toString();
String outputEnd = end.toInstant().toString();
Milliseconds since epoch
But if you insist on the count of milliseconds, first extract Instant objects from our ZonedDateTime objects, effectively adjusting to UTC (offset of zero).
Instant startInstant = start.toInstant() ;
Instant endInstant = end.toInstant() ;
Interrogate for a count of milliseconds since epoch.
long startMilli = startInstant.toEpochMilli() ;
long endMilli = endInstant.toEpochMilli() ;
Avoid LocalDateTime for this problem
Notice that at no point did we use the LocalDateTime class. That class purposely lacks the context of a time zone or offset-from-UTC. So LocalDateTime cannot represent a moment, is not a point on the timeline. That makes LocalDateTime irrelevant to the problem at hand.
Tip: ThreeTen-Extra library
If you routinely work with pairs of moments, considering adding the ThreeTen-Extra library to your project. This provides the Interval class for representing a pair of Instant objects. The class carries several handy comparison methods such as abuts, overlaps, contains, and so on.

Generating reports for different time zones in Java

as a part of my requirement,I have to fire a SQL query which takes yesterday's midnight and today's midnight in the respective time zone as input.Is there a way to achieve this?
Use ZonedDateTime:
String DATE_FORMAT = "dd-M-yyyy hh:mm:ss a";
String dateInString = "22-1-2015 10:15:55 AM";
LocalDateTime ldt = LocalDateTime.parse(dateInString, DateTimeFormatter.ofPattern(DATE_FORMAT));
ZoneId singaporeZoneId = ZoneId.of("Asia/Singapore");
System.out.println("TimeZone : " + singaporeZoneId);
//LocalDateTime + ZoneId = ZonedDateTime
ZonedDateTime asiaZonedDateTime = ldt.atZone(singaporeZoneId);
System.out.println("Date (Singapore) : " + asiaZonedDateTime);
ZoneId newYokZoneId = ZoneId.of("America/New_York");
System.out.println("TimeZone : " + newYokZoneId);
ZonedDateTime nyDateTime = asiaZonedDateTime.withZoneSameInstant(newYokZoneId);
System.out.println("Date (New York) : " + nyDateTime);
DateTimeFormatter format = DateTimeFormatter.ofPattern(DATE_FORMAT);
System.out.println("\n---DateTimeFormatter---");
System.out.println("Date (Singapore) : " + format.format(asiaZonedDateTime));
System.out.println("Date (New York) : " + format.format(nyDateTime));
Output is:
TimeZone : Asia/Singapore
Date (Singapore) : 2015-01-22T10:15:55+08:00[Asia/Singapore]
TimeZone : America/New_York
Date (New York) : 2015-01-21T21:15:55-05:00[America/New_York]
---DateTimeFormatter---
Date (Singapore) : 22-1-2015 10:15:55 AM
Date (New York) : 21-1-2015 09:15:55 PM
Use the methods from here to get what you need
Example taken from: Java – Convert date and time between timezone
simply run your cronjob at every hour and generate only the reports in which timezones the day just ended
java.time
Use a driver compliant with JDBC 4.2 or later, running on Java 8 or later, to benefit from use of the modern java.time classes that supplant the troublesome old legacy date-time classes.
Determining "today" and "yesterday" means determining a date. Determining a date requires a time zone. For any given moment, the date varies around the globe by zone.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
LocalDate yesterday = today.minusDays( 1 ) ;
To query for timestamps in the database, we need specific moments. The Question specifies midnight. The term "midnight" is vague. Let's use "first moment of the day" instead.
Never assume the day starts at 00:00:00. Anomalies such as Daylight Saving Time (DST) mean it may start at another time such as 01:00:00. Let java.time determine first moment of the day.
ZonedDateTime start = yesterday.atStartOfDay( z ) ;
Generally, the best approach to defining a span of time is though the Half-Open approach where the beginning is inclusive while the ending is exclusive. So we want to start at first moment of one day and run up to, but not include, the first moment of the next day.
ZonedDateTime stop = today.atStartOfDay( z ) ;
In Half-Open, we do not use the SQL command BETWEEN.
SQL
SELECT * FROM t
WHERE event >= ? AND event < ? ;
Java
myPreparedStatement.setObject( 1 , start ) ;
myPreparedStatement.setObject( 2 , stop ) ;
To retrieve the timestamps, use getObject.
Instant instant = myResultSet.getObject( "event" , Instant.class ) ;
To move from UTC to a zone, apply a ZoneId.
ZonedDateTime zdt = instant.atZone( z ) ;

Java - Producing a timestamp for every minute of every hour for 24 hours

I am writing a program to produce a timestamp for every minute of every hour for a whole day.
I am using the Calendar class to get the timestamp, I have to use it so no point in suggesting other methods.
My idea to produce the file was to have a for loop for 24 hours and a nested for loop of 60 minutes in which the timestamp would be printed to the .dat file. I thought this would work and would print the data for the whole day and then stop.
However I was wrong, totally wrong!
The result is data being printed for every minute upto a date 2 years from now.
Here is my code so far;
public static void main (String [] args) throws FileNotFoundException
{
try
{
DateFormat df = new SimpleDateFormat("dd-MM-yyyy");
Date date = new Date();
File fileName = new File(df.format(date) + ".dat");
RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.add(Calendar.MILLISECOND, -cal.get(Calendar.MILLISECOND));
cal.add(Calendar.SECOND, -cal.get(Calendar.SECOND));
cal.add(Calendar.MINUTE, -cal.get(Calendar.MINUTE));
cal.add(Calendar.HOUR_OF_DAY, -cal.get(Calendar.HOUR_OF_DAY));
for(int hourInMinutes = 0; hourInMinutes < 1440; hourInMinutes++) //1440 is the total minutes in a day
{
for(int minute = 0; minute <= hourInMinutes; minute++)
{
raf.writeLong(cal.getTimeInMillis()); //Timestamp
cal.add(Calendar.MINUTE, 1);
}
}
raf.close();
}
catch(IOException iOE)
{
System.err.println(iOE);
}
}
The data starts at midnight (last night) and I want it to stop producing data at 11.59pm on the same day.
Anyone have any knowledge on how this is done?
Your for loop looks wrong, here is the updated version:
for(int hourInDay = 0; hourInDay < 24; hourInDay++)
{
for(int minute = 0; minute <= 59; minute++)
{
raf.writeLong(cal.getTimeInMillis());
cal.add(Calendar.MINUTE, 1);
}
}
Or you can get rid of an inner for loop (which has totally wrong second condition) and use the following version:
for(int minutesInDay = 0; minutesInDay < 1440; minutesInDay++) //1440 is the total minutes in a day
{
raf.writeLong(cal.getTimeInMillis());
cal.add(Calendar.MINUTE, 1);
}
And try to give distinct names to your variables. hourInMinutes sounds for me like "the number of hours in one minute". Obviously, it's not what this variable stands for.
You should throw out your inner loop:
for(int hourInMinutes = 0; hourInMinutes < 1440; hourInMinutes++) //1440 is the total minutes in a day
{
raf.writeLong(cal.getTimeInMillis()); //Timestamp
cal.add(Calendar.MINUTE, 1);
}
As you already loop every minute in a day, you shouldn't have a inner loop, that's all.
I prefer quartz-scheduler will be the best one to schedule a task for hour convenience
java.time
You are using old outmoded classes, now supplanted by java.time classes.
Time zone is crucial to determining a date and therefore the hours of that day. For any given moment the date varies around the globe by zone. Specify a ZoneId object.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
ZonedDateTime
Use that time zone to get the current moment in a ZonedDateTime object.
ZonedDateTime now = ZonedDateTime.now ( zoneId );
Get the date-only value (LocalDate) for use in naming the file.
String filename = now.toLocalDate().toString();
To get the first moment of the day, let java.time determine the value. The day does not always start at 00:00:00 in some time zones because of Daylight Saving Time (DST) or other anomalies. To get first moment, we go through the LocalDate class briefly.
ZonedDateTime start = now.toLocalDate ().atStartOfDay ( zoneId );
For there we loop every hour until reaching the next day. Secondarily loop each minute of the hour until reaching the next hour. We soft-code the loops to test going past the limits rather than hard-code a number of hours or a number of minutes. Anomalies such as Daylight Saving Time (DST) can shift the clock by hours or by minutes. For example, a 30-minute change in Venezuela in 2007.
ZonedDateTime nextDay = start.plusDays ( 1 );
ZonedDateTime zdt = start;
while ( zdt.isBefore ( nextDay ) ) {
ZonedDateTime zdtMinute = zdt;
ZonedDateTime zdtNextHour = zdtMinute.plusHours ( 1 );
while ( zdtMinute.isBefore ( zdtNextHour ) ) {
System.out.println ( zdtMinute.toString () );
// Prepare for next loop.
zdtMinute = zdtMinute.plusMinutes ( 1 );
}
// Prepare for next loop.
zdt = zdt.plusHours ( 1 );
}
When run.
2016-08-12T00:00-04:00[America/Montreal]
2016-08-12T00:01-04:00[America/Montreal]
2016-08-12T00:02-04:00[America/Montreal]
2016-08-12T00:03-04:00[America/Montreal]
…
2016-08-12T23:58-04:00[America/Montreal]
2016-08-12T23:59-04:00[America/Montreal]
Instant
If you want generic 24-hour days and 60-minute hours, instead of ZonedDateTime, use the Instant class as it is always in UTC by definition.

Categories

Resources