I have a time range starting with a start date and end date represented in milliseconds since 1970:
long start;
long end;
And I want to know if and how often a specific daytime is contained in this range. So let's say the daytime is 09.00 am DST - how often is it contained in the range.
Is there an easy and elegant way to calculate this in Java?
java.time
The modern approach uses the java.time classes.
Convert your count-from-epoch numbers. An Instant is a point on the timeline in UTC with a resolution of nanoseconds.
Instant startInstant = Instant.ofEpochMilli( startLong ) ;
Instant stopInstant = Instant.ofEpochMilli( stopLong ) ;
Adjust into your desired time zone.
Always use true time zone names structured as continent/region.
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtStart = instantStart.atZone( z ) ;
ZonedDateTime zdtStop = instantStop.atZone( z ) ;
Represent your target time-of-day.
LocalTime lt = LocalTime.of( 9 , 0 ) ;
Get a ZonedDateTime with your starting date and your target time.
ZonedDateTime zdtStartAtTargetTime = ZonedDateTime.of( zdtStart.toLocalDate() , lt , z ) ;
Compare that to your interval’s start to see if you should count this starting date.
Do the same process for the last day.
The count the days between the start of the second day and first moment of the last day.
long days = ChronoUnit.DAYS.between( start , stop ) ;
If they count, add your first day, and your last day.
Compare to the
If you're using Java 8, you could use java.time.OffsetDateTime, starting from your start point, adjusting the time based on toOffsetTime, then repeatedly using plusDays until you've exceeded your end time.
If not, then you can achieve something similar with Joda's DateTime class.
Don't try it without a library that handles calendars properly.
Related
In our database, we have few long values like below
modified=1636334664000
created=1636334664000
if i use below code to convert, it doesnt show the format in millisec in it, it shows only up to seconds.
i have used below code
long modified = 1636334664000l;
LocalDateTime ldt = LocalDateTime.ofInstant(
Instant.ofEpochMilli(modified), ZoneId.systemDefault());
LocalDateTime dateTime = LocalDateTime.parse(ldt.toString());
dateTime = dateTime.atZone(ZoneId.systemDefault())
.withZoneSameInstant(ZoneOffset.UTC)
.toLocalDateTime();
Instant insStr = dateTime.toInstant(ZoneOffset.UTC);
this gives me output as "2021-11-08T01:24:24Z" but i was expecting as "2021-11-08T01:24:24.000Z".
used Java 8 date conversion as above.
tl;dr
After 👉🏽 correcting multiple typos in the example data of your Question, we find no errors, no surprises, when running the code. Your millisecond appears as expected.
Instant.ofEpochMilli( 1_636_334_664_001L ).toString()
2021-11-08T01:24:24.001Z
LocalDateTime not helpful in your case
LocalDateTime is the wrong class to use here. That class represents a date with time-of-day but lacks the context of a time zone or offset-from-UTC. So that class cannot represent a moment, a specific point on the timeline.
To track moments use:
Instant
OffsetDateTime
ZonedDateTime
Use Instant
Assuming your long values represent a count of milliseconds since the epoch reference of first moment of 1970 in UTC, 1970-01-01T00:00Z, use Instant.ofEpochMilli.
Your 1636334664000l example presumably has a typo, one too many zero digits. I will go with 163633466400l.
When hard-coding such long values, use the optional underscore (_) digit grouping feature in Java. And 👉🏽 append an L to ensure parsing as a long primitive.
Instant created = Instant.ofEpochMilli( 1_636_334_664_000L ) ;
Instant modified = Instant.ofEpochMilli( 1_636_334_664_001L ) ;
Calculate elapsed time.
Duration duration = Duration.between( created , modified ) ;
We expect to see a single millisecond as our result. The result is presented in standard ISO 8601 format.
Dump to console.
System.out.println( created ) ;
System.out.println( modified ) ;
System.out.println( duration ) ;
Execute at Ideone.com. We see your expected fractional second, a millisecond.
2021-11-08T01:24:24Z
2021-11-08T01:24:24.001Z
PT0.001S
ZonedDateTime
See that same moment through the wall-clock time of a particular time zone.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdtModifiedTokyo = instant.atZone( z ) ;
We continue to see your fractional second, a single millisecond.
zdtModifiedTokyo.toString(): 2021-11-08T10:24:24.001+09:00[Asia/Tokyo]
I could not find any hand method on java.util.Date function in order to get number of days between 2 dates?
How should I get number of days?
First, convert your legacy java.util.Date objects to their modern replacements, java.time.Instant. Call new methods added to the old classes.
Instant start = myJavaUtilDate.toInstant() ;
If by “number of days” you mean “number of 24-hour chunks of time”, without regard for calendar, use Duration.
long days = Duration.between( start , end ).toDays() ;
If you meant calendar days, you need to specify the time zone by which you want to perceive dates.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
Apply that zone to Instant to produce a ZonedDateTime. Same moment, same point on the timeline, different wall-clock time and date.
ZonedDateTime startZdt = start.atZone( z ) ;
Calculate elapsed days using ChronoUnit enum DAYS.
long days = ChronoUnit.between( startZdt , endZdt ) ;
LocalDate beginDate = LocalDate.now()
.with(ChronoField.DAY_OF_WEEK, 1)
.atStartOfDay()
.minusDays(8)
.toLocalDate();
I am getting the previous week begin date using the above code line. However I want to add HH:MM:SS format to this. I have tried different ways to get this. Tried using LocalDateTime instead of Localdate. But could not find atStartOfDay() method for LocalDateTime. Help me to add HH:MM:SS to beginDate variable
tl;dr
LocalDate // Represents a date only, without a time of day, without a time zone or offset.
.now( ZoneId.of( "Asia/Amman" ) ) // Returns a `LocalDate`.
.minusDays( 8 ) // Returns another `LocalDate` object.
.atStartOfDay( ZoneId.of( "Asia/Amman" ) ) // Returns a `ZonedDateTime`.
.toString() // Returns a `String` object, with text in standard ISO 8601 format wisely extended to append the name of time zone in brackets.
See this code run at Ideone.com. Notice that on that date in that zone, the day began at 01:00, not 00:00.
2022-02-22T01:00+03:00[Asia/Amman]
No “format” involved
Date-time objects do not have a “format”. Text has a format. Date-time objects are not text.
LocalDate has no time of day
You said:
add HH:MM:SS format to [a LocalDate object]
A LocalDate represents a date only, without a time of day, without a time zone or offset.
ZonedDateTime
Apparently you want the first moment of the day eight days ago as seen in your locality.
First, specify your desired/expected time zone.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
Or use your JVM‘s current default time zone.
ZoneId z = ZoneId.systemDefault() ;
Capture the current date as seen in that zone.
LocalDate today = LocalDate.now( z ) ;
Go back eight days.
LocalDate eightDaysAgo = today.minusDays( 8 ) ;
If you meant to go back to the previous Monday, use a TemporalAdjuster.
LocalDate previousMonday = today.with( TemporalAdjusters.previous( DayOfWeek.MONDAY ) ) ;
Get the first moment of that day. Pass your time zone.
ZonedDateTime zdt = eightDaysAgo.atStartOfDay( z ) ;
The time-of-day may be 00:00:00, but not necessarily. Some days on some dates in some zones start at another time such as 01:00:00.
All of this has been covered many times already on Stack Overflow. Search to learn more.
What you want is a LocalDateTime, which is a LocalDate with a time component (including timezone).
LocalDate does as it says on the tin, it gives you a date, not a date and time.
LocalDateTime
.of(LocalDate.now().with(ChronoField.DAY_OF_WEEK, 1), LocalTime.MIDNIGHT)
.minusWeeks(1)
Gives you start of last week at midnight (local time).
#DateTimeFormat("HH:MM:SS")
#JsonFormat("HH:MM:SS")
I am trying to get the start time (00:00:00) and the end time (23:59:59) of a day in the PST time zone. I have tried the following code, but for some reason, I am only getting the start and end times in UTC. I have tried changing the timezone to include "America/Los_angeles", but the output timestamp is always showing start and end times for GMT/UTC.
My code:
val time_zone = ZoneId.of("America/Los_Angeles")
val today_date = LocalDate.now(time_zone).plusDays(0)
val start_time = today_date + " " + "00:00:00"
val end_time = today_date + " " + "23:59:59"
val date_format = new SimpleDateFormat("yyyy-MM-dd");
val start_millis = date_format.parse(start_time).getTime();
val end_millis = date_format.parse(end_time).getTime();
start_millis
Output:
res375: Long = 1656460799000
In the epoch converter, 1656460799000 gives me this:
Anything I am missing here? Should I update any package, etc.?
java.time
The modern approach uses the java.time classes only.
No need to ever use SimpleDateFormat, Date, Calendar, and the other terrible legacy date-time classes. If need be, you can convert to and fro via new conversion methods added to the old classes.
Start of day
I am trying to get the start time (00:00:00)
Do not assume the day starts at 00:00. Some dates in some zones start at another time such as 01:00. Let java.time determine the first moment of the day using LocalDate#atStartOfDay.
End of day
the end time (23:59:59) of a day
You would be missing an entire last second of the day with that approach.
Date-time work is commonly done with the Half-Open approach. In Half-Open, the beginning is inclusive while the ending is exclusive. So a day starts with the first moment of the day, and runs up to, but does not include, the first moment of the following day. Half-Open approach neatly contains that full last second of the day.
Time zones
PST time zone.
There is no such thing as a time zone named PST. Such 2-4 letter pseudo-zones are used by the popular media to indicate a hint about the time zone. But these pseudo-zones are not standardized, and are not even unique! Use only for localized presentation to humans, never for data storage or data exchange.
Real time zones are named with Continent/Region.
Perhaps by “PST” you meant “Pacific Standard Time”, which often indicates America/Tijuana, or America/Los_Angeles or America/Vancouver or others.
Or perhaps by “PST” you meant “Philippines Standard Time” covering the Asia/Manila time zone.
Example code
Capture the current moment as seen in a time zone.
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Extract the date.
LocalDate today = zdt.toLocalDate() ;
Determine the first moment of the day.
ZonedDateTime zdtStartOfDay = today.atStartOfDay( z ) ;
And determine the first moment of the following day.
ZonedDateTime zdtStartOfFollowingDay = today.plusDays( 1 ).atStartOfDay( z ) ;
You may want to see the length of time. Not all days are 24 hours.
Duration d = Duration.between( zdtStartOfDay , zdtStartOfFollowingDay ) ;
Adjust both moments to UTC by extracting an Instant object. That class represents a moment as seen in UTC.
Instant start = zdtStartOfDay.toInstant() ;
Instant end = zdtStartOfFollowingDay.toInstant() ;
For each, get the count of milliseconds since the epoch reference of first moment of 1970 as seen in UTC, 1970-01-01T00:00Z.
long startMilli = start.toEpochMilli() ;
long endMilli = end.toEpochMilli() ;
However, I strongly recommend against tracking time as a count of milliseconds. This approach is confusing, as at least a couple dozen epoch reference points are commonly used. And a long cannot be interpreted by a human reader, so mistakes may go unnoticed.
Instead, data storage and data exchange should generally be done as text using the standard ISO 8601 formats. The java.time classes use these standard formats by default when parsing/generating text.
String startText = start.toString() ;
String endText = end.toString() ;
ThreeTen-Extra
You may want to add the ThreeTen-Extra library to your project. This gives you access to the Interval class, to represent a span of time as a pair of Instant objects.
Interval allDayLongToday = org.threeten.extra.Interval.of( start , end ) ;
This class provides several helpful methods. These include contains, encloses, abuts, union, intersection, and more.
Instant invoiceRecorded = … some `Instant` ;
boolean invoiceRecordedToday = allDayLongToday.contains( invoiceRecorded ) ;
Just add this section to your code:
date_format.setTimeZone(TimeZone.getTimeZone("PST"));
Then it will work as you want :)
I'm getting start date as "2016-06-01" and end date as "2016-07-01" (in string format) for searching records in MongoDB. Need pointer/guidance to append start time (00:00:00.000) to start date and maximum time(23.59.59.999) to end date as below in Java using java.util.Date or any others which supported by MongoDB.
Example :
Start Date+with time : 2016-06-01T00:00:00.000
End Date+with time : 2016-07-01T23:59:59.999
You could use the DateTimeFormatter.ISO_LOCAL_DATE_TIME for this. Here is an example that might shed some light on what you are trying to do:
DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String startTime = "T00:00:00.000";
String endTime = "T23:59:59.999";
//here I used the LocalDateTime parser to parse the data+startTime/endTime
LocalDateTime startLocalDateTime = LocalDateTime.parse("2016-07-01"+startTime);
LocalDateTime endLocalDateTime = LocalDateTime.parse("2016-07-01"+endTime );
//with the LocalDateTime, you can then to whatever you want
//as an example, I am parsing it using ISO_LOCAL_DATE_TIME :
String strinStartTime= dtf.format(LocalDateTime.parse("2016-07-22"+startTime));
I hope this helps;
tl;dr
ZoneId zoneId = ZoneId.of( "Europe/Paris" ) ;
LocalDate startDate = LocalDate.of( "2016-06-01" ) ;
ZonedDateTime zdt start = startDate.atStartOfDay( zoneId ) ;
ZonedDateTime zdt stop = startDate.plusMonths(1).atStartOfDay( zoneId ) ;
// Perform database search where ( ( x >= start ) AND ( x < stop ) ) . Notice '>=' versus '<' with no 'equals' on the latter.
If you need strings…
String outputStart = start.toInstant().toString() ; // 2016-05-31T22:00:00Z Paris in the summer is two hours ahead of UTC.
String outputStop = stop.toInstant().toString() ; // 2016-06-30T22:00:00Z
Details
The Answer by ishmaelMakitla is good in that it points to using the java.time classes built into Java 8 and later. But it focuses on strings rather than objects. Also it does not discuss the crucial issue of time zone.
The java.time classes include:
LocalDate for a date-only value with no time-of-day and no time zone.
LocalTime for a time-of-day value without a date and without a time zone.
LocalDate startDate = LocalDate.parse( "2016-06-01" ); // Parsing ISO 8601 standard date format.
LocalTime startTime = LocalTime.MIN; // '00:00'.
Both of those classes can be used in factory methods to instantiate LocalDateTime and other classes.
LocalDateTime ldt = LocalDateTime.of( startDate , startTime );
In code above we used LocalTime.MIN to get 00:00. To directly answer your Question, you can also use LocalTime.MAX in the same way to get 23:59:59.999999999. But I do not recommend doing so. Read below about "Half-Open".
Time zone
Time zone is crucial in determining a date and a time. For any given moment the date and the hour-of-day both vary by time zone. A few minutes after midnight in Paris is a new day while still “yesterday” in Montréal.
The Local… types are not actual moments on the timeline. They represent a vague idea about possible moments. As noted above, the first moment of June 1st in Paris is simultaneously May 31st at 6 PM in Montréal. So before performing your database search you need to assign a time zone to your LocalDateTime. Applying a ZoneId produces a ZonedDateTime object.
Perhaps your date-time was intended to be Paris.
ZoneId zoneId = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdt = ldt.atZone( zoneId );
Or perhaps you intended UTC. This all depends on your business rules, the context in which your app operates. For UTC, we use OffsetDateTime as UTC is not a full time zone but rather a mere offset-from-UTC. A time zone is an offset plus a set of rules for handling anomalies such as Daylight Saving Time (DST).
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC );
To get a string as asked for in the Question, extract LocalDate and call toString(). But I do not recommend this as it ignores time zone (read on down below).
String output = odt.toLocalDateTime.toString(); // Not likely to be what you really need.
Best practice in databases is to store the date-time in UTC. I don't know about MongoDB. Be sure to read the doc on how your database driver in Java may be affecting/translating the values you specify.
Start of Day
Be aware that a day does not always start at 00:00:00. In some time zones DST or other anomalies means the day may start at some other time such as 01:00.
The java.time classes will make adjustments as needed in some situations. Be sure to read the class doc so you see if the behavior matches your expectations & needs.
You can ask java.time to find the starting time.
ZonedDateTime zdt = LocalDate.of( "2016-06-01" ).atStartOfDay( zoneId );
Half-Open
Your attempt to determine the end of the day is a problem. That last second is infinitely divisible. Traditional Unix-oriented libraries resolve to whole seconds, the old date-time classes in Java resolve to milliseconds, some databases like Postgres may resolve to microseconds, and java.time and other databases such as H2 resolve to nanoseconds. Do not get in the middle of that.
Generally in date-time programming of a span of time, the best practice is "Half-Open". The beginning of the span is inclusive while the ending is exclusive.
So searching for a month of data in Paris zone means searching for records where the date-time is equal to or later than the start and less than (but not including) the stop.
ZoneId zoneId = ZoneId.of( "Europe/Paris" );
LocalDate startDate = LocalDate.of( "2016-06-01" );
ZonedDateTime zdt start = startDate.atStartOfDay( zoneId );
ZonedDateTime zdt stop = startDate.plusMonths(1).atStartOfDay( zoneId );
// Perform database search where ( ( x >= start ) AND ( x < stop ) ) . Notice '>=' versus '<' with no 'equals' on the latter.
Similarly, the month of records for UTC rather than Paris.
ZoneOffset zoneOffset = ZoneOffset.UTC;
LocalDate startDate = LocalDate.of( "2016-06-01" );
OffsetDateTime start = OffsetDateTime.of( startDate , zoneOffset );
OffsetDateTime stop = OffsetDateTime.plusMonths(1).of( startDate , zoneOffset );
// Perform database search where ( ( x >= start ) AND ( x < stop ) ) . Notice '>=' versus '<' with no 'equals' on the latter.
Using the Half-Open approach consistently throughout your app where handling spans of time will make your code more sensible and easier to understand. You can also train your users to think this way. We all use Half-Open intuitively in situations situations like "Lunch break is from 12:00 to 13:00". We all know this means be back from lunch before the clock strikes 13:00:00.0.
public class DateSample {
public static void main(String[] args) throws ParseException {
String startDate = "2016-06-01";
String endDate = "2016-07-01";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date strDate = formatter.parse(startDate+" 00:00:00.000");
Date enDate = formatter.parse(endDate+" 23:59:59.999");
System.out.println(formatter.format(strDate));
System.out.println(formatter.format(enDate));
}
}
You will get
2016-06-01 00:00:00
2016-07-01 23:59:59
If you are running under jdk 1.8, use LocalDateTime
LocalDateTime is an embedded api of jdk 1.8. You can found explaination here docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html. You can use minus* or plus*, and parse methods