I am bashing my head against Java's Temporal API. I can't find anything in the Java Docs or anywhere else that'll answer what I need to do. I simply need to at 1,000,000,000 seconds to a date and return the date, calculating the moment someone lived a billion seconds (somewhere around 30 years.)
public Temporal getGigasecondDate(Temporal given) {
final long gigasecond = 1000000000;
final long gigaDays = gigasecond / 60 / 60 / 24;
Duration amount = Duration.ofDays(gigaDays);
Temporal date = amount.addTo(given);
return date;
}
I get an error saying Exception in thread "main" java.time.DateTimeException: Unit must be Years, Months or Days, but was Seconds and I can't for the life of me find a way in the JavaDocs, or anywhere else, figure out how to get the unit to years, months, or days.
All I need to do, is return a Temporal object that states the moment someone has lived a billion seconds.
tl;dr
ZonedDateTime.of( // Mom gave birth at 3 AM in year 2000, New Zealand time.
2000 , 1 , 23 , 3 , 45 , 0 , 0 , ZoneId.of( "Pacific/Auckland" )
) // Returns a `ZonedDateTime` object.
.plus(
Duration.ofSeconds( 1_000_000_000L ) // A billion seconds.
) // Returns another `ZonedDateTime` object. Immutable objects in *java.time* means we get a fresh object rather than “mutating” (altering) the original.
.toString() // Generate a `String` representing the value of our `ZonedDateTime` object, using standard ISO 8601 format wisely extended to append the name of the time zone in square brackets.
2031-10-01T05:31:40+13:00[Pacific/Auckland]
Details
The Answer by Andreas is correct. Here are a few more thoughts.
Do not use Temporal
The documentation for java.time explains that generally in an app you should be using the concrete classes rather than the interfaces and superclasses. To quote from Temporal interface:
This interface is a framework-level interface that should not be widely used in application code. Instead, applications should create and pass around instances of concrete types, such as LocalDate. There are many reasons for this, part of which is that implementations of this interface may be in calendar systems other than ISO. See ChronoLocalDate for a fuller discussion of the issues.
Adding a billion seconds to a birth-moment
[add] 1,000,000,000 seconds to a date and return the date,
Determine the moment of the person's birth.
LocalDate ld = LocalDate.of( 2000 , Month.JANUARY , 23 ) ;
LocalTime lt = LocalTime.of( 3 , 45 ) ; // Mom gave birth at 3 AM in New Zealand time.
ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
See that same moment in UTC, if useful.
Instant instant = zdt.toInstant() ;
Define the amount of time to add. The Duration class represents a span of time not attached to the timeline.
Duration d = Duration.ofSeconds( 1_000_000_000L ) ;
Add.
ZonedDateTime zdtLater = zdt.plus( d ) ;
Or, in UTC (same moment, different wall-clock time).
Instant instantLater = instant.plus( d ) ;
See this code run live at IdeOne.com.
zdt.toString(): 2000-01-23T03:45+13:00[Pacific/Auckland]
instant.toString(): 2000-01-22T14:45:00Z
d.toString(): PT277777H46M40S
zdtLater.toString(): 2031-10-01T05:31:40+13:00[Pacific/Auckland]
instantLater.toString(): 2031-09-30T16:31:40Z
If you care for only the date, without the time-of-day and without the time zone, extract a LocalDate.
LocalDate ldLater = zdtLater.toLocalDate() ;
You can call plus(long amountToAdd, TemporalUnit unit), with unit as ChronoUnit.SECONDS:
public static Temporal getGigasecondDate(Temporal given) {
return given.plus(1_000_000_000, ChronoUnit.SECONDS);
}
Test
System.out.println(getGigasecondDate(LocalDateTime.of(2000, 1, 1, 0, 0)));
System.out.println(getGigasecondDate(ZonedDateTime.of(2000, 1, 1, 0, 0, 0, 0, ZoneId.of("America/New_York"))));
Output
2031-09-09T01:46:40
2031-09-09T02:46:40-04:00[America/New_York]
So, someone born at midnight on Jan 1, 2000 will be 1 billion seconds old on Sep 9, 2031 at 1:46:40 AM, assuming no Daylight Savings Time.
If they lived in New York, it would be at 2:46:40 AM EDT.
Related
For example I have sentTime as an input (25 May 2021 02:00:00 PM) and I need to add reviewTime (10 hours) and calculate releasingTime (considering only working hours(9am-5pm) and non weekend days)
25 May 2021 02:00:00 PM + 10 hours would be 26 May 2021 04:00:00 PM
java.time
I do not know of any easy way to do this. The java.time classes have all the parts needed, but you would have to build up some code to do the calculations.
Be aware that you must account for time zone. On some dates, you will encounter anomalies such as days being 23 or 25 hours long, the clock skipping ahead or dropping behind. One example of such anomalies is Daylight Saving Time (DST), but that is not the only cause. Politicians around the world have shown a penchant for redefining the time-keeping of their jurisdictions for varied reasons.
Here is a brief example to get you started, if you choose to go this route.
Besides the java.time classes built into Java, this code also leverages the ThreeTen-Extra library which adds functionality to java.time. We need that library for two classes here:
A TemporalAdjuster for finding the next working day (skipping Saturday-Sunday). See tutorial on temporal adjusters. Tip: You may want to consider implementing a TemporalAdjuster on your own as part of a real solution — but I'm not sure, just an idea I have not thought through.
Interval class to track a pair of moments as seen in UTC (an offset of zero hours-minutes-seconds). Not required here, but might be useful in further work.
Duration work = Duration.ofHours( 10 );
LocalTime shiftStart = LocalTime.of( 9 , 0 );
LocalTime shiftEnd = LocalTime.of( 17 , 0 );
ZoneId z = ZoneId.of( "America/Chicago" );
ZonedDateTime startOfWork = ZonedDateTime.of( 2021 , 5 , 25 , 14 , 0 , 0 , 0 , z );
// Calculate how much time left in the day to work.
ZonedDateTime endOfDayOne = startOfWork.with( shiftEnd );
Duration untilEndOfDayOne = Duration.between( startOfWork , endOfDayOne );
Duration remainingWork = work.minus( untilEndOfDayOne );
// Determine next work-day.
// Add ThreeTen-Extra library to your project to access the `TemporalAdjuster` for `nextWorkingDay()`.
LocalDate nextWorkDay = endOfDayOne.toLocalDate().with( org.threeten.extra.Temporals.nextWorkingDay() );
ZonedDateTime startOfNextWorkingDay = ZonedDateTime.of( nextWorkDay , shiftStart , z );
ZonedDateTime endOfWork = startOfNextWorkingDay.plus( remainingWork );
org.threeten.extra.Interval workInterval =
org.threeten.extra.Interval.of(
startOfWork.toInstant() ,
endOfWork.toInstant()
);
Dump to console. By default, java.time generates text in standard ISO 8601 formats.
System.out.println( "startOfWork = " + startOfWork );
System.out.println( "work = " + work );
System.out.println( "endOfWork = " + endOfWork );
System.out.println( "workInterval = " + workInterval );
When run.
startOfWork = 2021-05-25T14:00-05:00[America/Chicago]
work = PT10H
endOfWork = 2021-05-26T16:00-05:00[America/Chicago]
workInterval = 2021-05-25T19:00:00Z/2021-05-26T21:00:00Z
Project management software
Project Management software is built to do this very job: Calculate elapsed time for various tasks restricted by working hours and working days. One possible solution is trying to leverage such a library for your purposes.
Assuming sentTime is of type java.util.Date, you can may be use the following code that utilizes Java 8's java.time.LocalDateTime
int reviewTime = 10;
List<DayOfWeek> weekends = Arrays.asList(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY);
LocalDateTime start = sentTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
final int workingHoursStart = 9;
final int workingHoursEnd = 17;
int hoursReviewed = 0;
while(reviewTime > hoursReviewed){
DayOfWeek dayOfWeek = start.getDayOfWeek();
if(weekends.contains(dayOfWeek) || start.getHour() < workingHoursStart || start.getHour() > workingHoursEnd){
start = start.plusHours(1);
continue;
}
start = start.plusHours(1);
hoursReviewed++;
}
Your resultant releasingTime time would be in the start object after the loop finishes iterating.
How to find minute of the year in java at any particular time.
It is required to be used in V2X messages as per the below definition:
MinuteOfTheYear ::= INTEGER (0..527040)
Time is tricky. This question cannot be answered without further detail.
Let's go through the terms:
How to find minute of the year in java at a particular time.
Define 'time'. Do you mean: Some instant in the universe, or do you mean: Some readout on a wallclock? These two are not the same.
If I clap my hands right now, then I (living in The Netherlands), would say it is 14 minutes past 5 in the afternoon. But at that exact moment in time, someone in New York would claim that it is 14 minutes past 11 in the morning.
When you say 'time' do you mean more like 'when I clap my hands' or more like '5 past eleven on the 14th of march'?
How to find minute of the year in java at a particular time.
The same applies here - 'minute of the year' implies you wish to know the difference between 'the very first minute of year X' and 'this moment in time', where X is the same year as said moment in time. This too involves timezones.
Said differently: If we go by moment-in-time, and I clap my hands at 5 minutes past the fireworks in london, the answer is '5'. Except someone in New York, at that exact same time, is still waiting (almost) 6 hours for new years; they'd answer your question with 525245; a wildly different answer.
I meant the wallclock time thing
In that case, your input is something like: Friday, 24th of July, 17:15:00, and you'd want to know the answer by defining 'new years' as being in the same zone.
Unfortunately, this is not an answerable question without knowing where on the planet we are. Some timezones move the clock around. Some do not. In europe, asking right now, you have to take into account that someplace in march, the clocks were moved forward (or was it back, I can never remember), that makes 60 minutes worth of difference. But in locales which don't do daylight savings, that never happened. Therefore: Impossible to answer without telling me WHERE.
I meant the instant-in-time thing
In that case, your input is something like '1595603962356 milliseconds since the epoch'. There is no such thing as 'start of the year' in millis since epoch without knowing where on the planet we live. Again, where is important.
I meant: Right now!
That boils down to the previous case; you'd get the current instant-in-time via System.currentTimeMillis() or Instant.now()
And... Right here!
Ah, well, 'right here' is where the java code runs. If we're talking servers and clients that may not be correct (the client could be elsewhere).
ZoneId.systemDefault() gets you the zone ID as identified by the server as 'the local zone id'.
Code please!
First, you must obtain an instant in time, localized in some location:
// inputs
Instant now = Instant.now();
ZoneId zone = ZoneId.systemDefault();
// alternative inputs:
Instant now = Instant.ofEpochMilli(1595603962356);
ZoneId zone = ZoneId.of("Europe/Amsterdam");
// convert to human style
ZonedDateTime zdt = now.atZone(zone);
// obtain first instant in time in this year
ZonedDateTime start = ZonedDateTime.of(zdt.getYear(), 1, 1, 0, 0, 0, 0, zone);
// find the difference, in minutes
int answer = (int) ChronoUnit.MINUTES.between(start, zdt);
System.out.println(answer);
Here's an example for 'right now, right here':
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
public class RightNowRightHere {
public static void main(String[] args) {
Instant now = Instant.now();
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime zdt = now.atZone(zone);
ZonedDateTime start = ZonedDateTime.of(zdt.getYear(), 1, 1, 0, 0, 0, 0, zone);
int answer = (int) ChronoUnit.MINUTES.between(start, zdt);
System.out.println(answer);
}
}
prints 296188 for me.
The thoughtful Answer by rzwitserloot is correct, and worth studying. But I suspect your use-case is specific to UTC rather than a particular time zone.
UTC specifically
Capture the current moment as seen in UTC.
OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ) ;
Extract the year. Use that to determine the first day of year.
Year year = Year.of( now.getYear() ) ;
LocalDate ld = year.atDay( 1 ) ;
Combine with the time 00:00:00 and UTC (offset of zero hours-minutes-seconds) to determine the first moment of the year.
OffsetDateTime startOfYear = OffsetDateTime.of( ld , LocalTime.MIN , ZoneOffset.UTC ) ;
Calculate the time elapsed between the start of year and now. The Duration class represents a span-of-time unattached to the timeline on the scale of generic 24-long days not attached to the calendar, hours, minutes, seconds, and fractional second.
Duration d = Duration.between( startOfYear , now ) ;
Extract a total number of minutes across the entire span of time.
long minutes = d.toMinutes() ;
We could collapse that code.
long minutes =
Duration.between( // Passing a pair of `OffsetDateTime` objects.
OffsetDateTime.of( // Passing date, time, offset.
Year.of( now.getYear() ).atDay( 1 ) , // Returns a `LocalDate` object.
LocalTime.MIN , // A constant `LocalTime` object.
ZoneOffset.UTC // A constant `ZoneOffset` object. Represents an offset of zero hours-minutes-seconds.
) // Returns a `OffsetDateTime` object, the first of our pair of `OffsetDateTime` objects being passed to `Duration.between`.
,
now // The `OffsetDateTime` object we instantiate above, capturing the current moment. The second of our pair of `OffsetDateTime` objects being passed to `Duration.between`.
) // Returns a `Duration` object.
.toMinutes() // Returns a `long`, the total number of minutes across the entire span-of-time. Not to be confused with `Duration::toMinutesPart`.
;
Well, my suggested solution is to build two methods, the first method multiplying by an argument of number (year number like 1) multiplied by the number of minutes every 365 days (one year):
public static long MinutesOfTheYear(final long year) {
return (year * 525600l);
/* For example, to calculate the number of minutes in a year, do the following:
long year = 1;
System.out.println( MinutesOfTheYear(year) );
Output:
525600l (Number of minutes a year)
*/
}
And the second method, which accepts a Calendar object with a specified date as a parameter, and then converts it to minutes relative to the specified year, month, and day:
public static long MinutesOfTheYear(final java.util.Calendar calendar) {
int DaysOfTheMonth = 0;
switch (calendar.get(java.util.Calendar.MONTH) -1) {
case 1:
// (January)
case 3:
// (March)
case 5:
// (May)
case 6:
// (June)
case 7:
// (July)
case 8:
// (August)
case 10:
// (October)
case 12:
// (December)
DaysOfTheMonth = 31;
break;
case 2:
// (February) 28 or 29
DaysOfTheMonth = 28;
break;
case 4:
// (April)
case 9:
// (September)
case 11:
// (November)
DaysOfTheMonth = 30;
break;
}
return ( 525600l ) + ( ( DaysOfTheMonth ) * 1440l )
+ ( calendar.get(java.util.Calendar.DAY_OF_MONTH) * 1440l );
/* Also to calculate the number of minutes of a particular date (year plus month and day):
java.util.Calendar calendar = java.util.Calendar.getInstance();
//** Give the desired date to the Calendar object (this class replaces the Date class, which became obsolete in Java version 1.1): **
calendar.set( year, month, day );
System.out.println( calendar );
Output:
year (525600l) + month (month * 43800l) + day (date * 1440l)
*/
}
Good luck!
I've been trying to think of a purely Java way to represent a schedule for a whole calendar year. Each schedule is for a 30 minute slot (half hour granularity).
This would be abstracted through a repository methods like findByDateTime()
Essentially I need to model time slots at 30 min granularites for each day.
The way I have hacked it together is like so
public Map<Integer, Map<Integer, Programme>> enumerateMapOfMapSchedules() {
int numberOfSlots = 48; //48 half hours in a day
Map<Integer, Map<Integer, Programme>> dayToTimeScheduleMap = new HashMap<>();
//create a key value map for each day of the year
for (int i = 1; i < 366; i++) {
Map<Integer, Programme> dayProgrammeScheduleMap = new HashMap<>();
for (int y = 1; y < numberOfSlots; y++) {
dayProgrammeScheduleMap.put(y, null);
}
dayToTimeScheduleMap.put(i, dayProgrammeScheduleMap);
}
//creates a map with 365 days and each day having a map of 48 schedule slots
return dayToTimeScheduleMap;
}
I appreciate this solution doesn't handle or have a concept of year, however since these are for mocks/tests then I am ok with this.
Also it doesn't handle a schedule that overlaps, if programme spans two half hour slots.
My query method is quite simple for finding what is in a particular schedule slot.
public Programme findByDateTime(LocalDateTime dateTime) {
int scheduleSlot = dateTime.getHour() * 2;
//if its the after 30 minute schedule slot
if (dateTime.getMinute() > 30) {
scheduleSlot++;
}
return scheduleMap.get(dateTime.getDayOfYear()).get(scheduleSlot);
}
However for iterating through all the data structure to see how many occurences of a particular programme exist.
My question, is there an easier way of doing this?
I tried doing it with a relational DB but it was hard to represent time periods easily without a lot of SQL.
Any suggestions or implementation advice welcomed!
Days vary in length
A calendar schedule only makes sense in the context of a time zone and a year. Politicians frequently change the offset used by the time zone(s) of their jurisdiction. This means days are not always 24-hours long. Anomalies such as Daylight Saving Time (DST) mean a day might be 23 hours long, 25 hours long, or something else such as 23.5 hours long.
Start at the beginning, count by 30-minute increments
So if what you want is to chop up the entire year in 30-minute segments, you must start at the first moment of the first day of a specific year in a specific time zone, and add 30 minutes at a time until reaching the new year.
ZoneId z = ZoneId.of( "America/Montreal" );
Year year = Year.of( 2021 );
LocalDate firstOfYear = year.atDay( 1 );
ZonedDateTime start = firstOfYear.atStartOfDay( z );
List < ZonedDateTime > zdts = new ArrayList <>();
Duration duration = Duration.ofMinutes( 30 );
ZonedDateTime zdt = start;
while ( zdt.getYear() == year.getValue() )
{
zdts.add( zdt );
// Setup the next loop.
zdt = zdt.plus( duration );
}
Return a non-modifiable copy of that list.
List < ZonedDateTime > slots = List.copyOf( zdts );
When run. Notice what happens at 1 or 2 AM on Mar 14, 2021 and Nov 7, 2021.
slots = [2021-01-01T00:00-05:00[America/Montreal], 2021-01-01T00:30-05:00[America/Montreal], 2021-01-01T01:00-05:00[America/Montreal], 2021-01-01T01:30-05:00[America/Montreal], 2021-01-01T02:00-05:00[America/Montreal],
…
2021-03-14T01:00-05:00[America/Montreal], 2021-03-14T01:30-05:00[America/Montreal], 2021-03-14T03:00-04:00[America/Montreal],
…
2021-11-07T00:30-04:00[America/Montreal], 2021-11-07T01:00-04:00[America/Montreal], 2021-11-07T01:30-04:00[America/Montreal], 2021-11-07T01:00-05:00[America/Montreal], 2021-11-07T01:30-05:00[America/Montreal], 2021-11-07T02:00-05:00[America/Montreal],
…
2021-12-31T22:00-05:00[America/Montreal], 2021-12-31T22:30-05:00[America/Montreal], 2021-12-31T23:00-05:00[America/Montreal], 2021-12-31T23:30-05:00[America/Montreal]]
Future projections unreliable!
But beware: politicians frequently change the offset used in a zone! This happens much more often than you likely realize. Politicians have even gotten worse at this reducing their forewarning from years to a few months, or even several weeks as seen recently in Turkey and Morocco, and even no forewarning at all as seen in North Korea.
So you cannot reliably project into the future using the approach seen above.
Slot math
I suppose you could approach the slots-of-year problem in another way. Calculate the number of whole slots during the year this way.
ZoneId z = ZoneId.of( "America/Montreal" );
Year year = Year.of( 2021 );
LocalDate firstOfYear = year.atDay( 1 );
ZonedDateTime start = firstOfYear.atStartOfDay( z );
ZonedDateTime end = start.plusYears( 1 );
Duration slotLength = Duration.ofMinutes( 30 );
long wholeSlotsInYear = Duration.between( start , end ).dividedBy( slotLength );
Then you could jump to a point in the year by multiplying duration, and adding the result to the start of year.
int slotNumber = 22;
Duration jump = slotLength.multipliedBy( slotNumber - 1 ); // Subtract one to change an ordinal number into a zero-based index.
ZonedDateTime slot22 = start.plus( jump );
Appointment book tracking
If you are doing appointments such as at a hair salon or dental clinic, the usual approach is to track a year-month-day with a particular time of day. But track the time zone separately. So use a LocalDateTime with a separate ZoneId in your Java model. In your database table, use a pair of columns, one of type akin to the SQL-standard type TIMESTAMP WITHOUT TIME ZONE and another column of a text type holding the name of the time zone such as America/Montreal or Africa/Tunis.
When building a schedule, apply the zone to determine a moment. In Java, that means applying a ZoneId to a LocalDateTime to get a ZonedDateTime.
You need to be clear on the fundamental idea that a LocalDateTime object does not represent a moment. In our example here, 3 PM on the 23rd of next year could mean 3 PM in Tokyo Japan or 3 PM in Toledo Ohio US, two very different moments several hours apart. A LocalDateTime is inherently ambiguous. Thus the need to store a time zone as well, but kept separate.
LocalDateTime ldt = LocalDateTime.of( 2021 , 1 , 23 , 15 , 0 , 0 , 0 ) ;
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ; // Determine a moment.
See that same moment in UTC by extracting a Instant.
Instant instant = zdt.toInstant() ;
//[Sat 2018-12-29 13:30:00 UTC]
final long startTs = 1546090200000L
//[Wed 2019-01-02 09:12:00 UTC]
final long endTs = 1546420320000L
Is there a way using LocalDateTime I can print all the days between these two times?
Ideal output would be:
2018-12-29
2018-12-30
2018-12-31
2019-01-01
2019-01-02
You can use LocalDateTime, for example:
LocalDateTime startLDT = LocalDateTime.ofInstant(Instant.ofEpochMilli(startTs), ZoneId.systemDefault());
LocalDateTime endLDT = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTs), ZoneId.systemDefault());
while (startLDT.isBefore(endLDT)) {
System.out.println(startLDT.format(DateTimeFormatter.ISO_LOCAL_DATE));
startLDT = startLDT.plusDays(1);
}
This loop takes milliseconds and creates instances of LocalDateTime. Then at each iteration if earlier date is before later - it's printed in format yyyy-MM-dd and incremented by day.
tl;dr
First, consider the built-in solution shown in Answer by Ole V.V.
Add the ThreeTen-Extra library to your project, for the LocalDateRange class.
LocalDateRange
.of(
Instant
.ofEpochMilli( 1_546_090_200_000L )
.atZone(
ZoneId.of( "America/Toronto" )
)
.toLocalDate() ,
Instant
.ofEpochMilli( 1_546_420_320_000L )
.atZone(
ZoneId.of( "America/Toronto" )
)
.toLocalDate()
)
.stream()
.forEach(
System.out::println
)
;
2018-12-29
2018-12-30
2018-12-31
2019-01-01
org.threeten.extra.LocalDateRange
The excellent Answer by Ole V.V. is correct, and is likely to meet your needs.
But if you find yourself working often with these date ranges, then you might want to learn about the LocalDateRange class found in the ThreeTen-Extra library. This library adds functionality to the java.time classes built into Java.
As discussed in that other Answer, start by parsing your count of milliseconds since first moment of 1970 in UTC into moments represented as Instant objects.
//[Sat 2018-12-29 13:30:00 UTC]
final long startInput = 1_546_090_200_000L ;
//[Wed 2019-01-02 09:12:00 UTC]
final long stopInput = 1_546_420_320_000L ;
Instant startInstant = Instant.ofEpochMilli( startInput ) ;
Instant stopInstant = Instant.ofEpochMilli( stopInput ) ;
startInstant.toString() = 2018-12-29T13:30:00Z
stopInstant.toString() = 2019-01-02T09:12:00Z
Adjust those into the time zone by which you want to perceive the calendar. Remember, for any given moment, the date varies around the globe by time zone. A moment may be “tomorrow” in Tokyo Japan while simultaneously being “yesterday” in Toronto Canada.
ZoneId z = ZoneId.of( "Asia/Tokyo" ) ;
ZonedDateTime zdtStart = startInstant.atZone( z ) ; // Produce a `ZonedDateTime` object from the `Instant` by applying a `ZoneId`.
zdtStart.toString() = 2018-12-29T22:30+09:00[Asia/Tokyo]
zdtStop.toString() = 2019-01-02T18:12+09:00[Asia/Tokyo]
Extract the date-only portion, without the time-of-day and without the time zone, as LocalDate object.
LocalDate start = zdtStart.toLocalDate() ;
start.toString() = 2018-12-29
stop.toString() = 2019-01-02
Pass both the start and stop LocalDate objects to make a org.threeten.extra.LocalDateRange.
LocalDateRange dateRange = LocalDateRange.of( start , stop ) ;
dateRange.toString() = 2018-12-29/2019-01-02
This LocalDateRange class has many methods for comparisons including contains, encloses, abuts, and overlaps. But for our purpose here, we want to see all the dates in-between. This class can make a stream of LocalDate objects.
Stream < LocalDate > stream = dateRange.stream() ;
From here, use the same .forEach method call to loop as seen in that other Answer.
2018-12-29
2018-12-30
2018-12-31
2019-01-01
Half-open span-of-time
Handling a span-of-time is usually best done using the Half-Open approach where the beginning is inclusive while the ending is exclusive. If you want to use the code above but also want to include the ending date, just add a day: stop = stop.plusDays( 1 ) ;.
LocalDate::datesUntil ➙ stream
Since Java 9 you can use LocalDate.datesUntil() for iterating over a date interval.
//[Sat 2018-12-29 13:30:00 UTC]
final long startTs = 1_546_090_200_000L;
//[Wed 2019-01-02 09:12:00 UTC]
final long endTs = 1_546_420_320_000L;
LocalDate startDate = millisToLocalDate(startTs);
LocalDate endDate = millisToLocalDate(endTs);
startDate.datesUntil( endDate.plusDays(1) ) // Returns a stream.
.forEach( System.out::println ); // Iterates objects in the stream, passing each to `println` method.
Output from this snippet is:
2018-12-29
2018-12-30
2018-12-31
2019-01-01
2019-01-02
I am using the following auxiliary method for converting your counts of milliseconds to LocalDate. I seemed to understand that you wanted to use dates in UTC, so this is what the method does.
private static LocalDate millisToLocalDate(long millisSinceEpoch) {
return Instant.ofEpochMilli(millisSinceEpoch)
.atOffset(ZoneOffset.UTC)
.toLocalDate();
}
datesUntil returns a stream of the dates from start date inclusive to end date exclusive. Since you wanted the end date to be included, we needed to add one day to it before passing it to datesUntil.
Link: Documentation of LocalDate.datesUntil
I'm in an Android project that requires saving a file with TDateTime type (Delphi). I have my date in milliseconds, but I don't know how to convert milliseconds to TDateTime.
I have something like this:
Date dateInMillis = new Date(System.currentTimeMillis());
double dateInDouble = ???;
I'll be glad for any tips that can help me to resolve this.
Delphi's TDateTime measures time in days. Java follows the Unix standard and measures in milliseconds. To convert between the two you need to scale by the number of milliseconds in a day, 86400000.
The other difference is that the two systems use a different epoch. The Unix epoch, as used by Java, is 00:00, 1 Jan 1970. The Delphi epoch is 00:00, 30 December 1899. The Unix epoch, represented as a Delphi TDateTime is 25569.
So, to convert from milliseconds from the Unix epoch, to days from the Delphi epoch you perform the following calculation:
double delphiDateTime = unixMillis/86400000.0 + 25569.0;
More recently post Java 8 with the new date time classes, the following should work:
LocalDateTime localDateTime = LocalDateTime.of(1899, 12, 30, 0, 0);//Delphi date EPOCH time start
double fraction = val % 1;
long intPart = (long) (val - fraction);
localDateTime = localDateTime.plus(intPart, ChronoUnit.DAYS);
localDateTime = localDateTime.plus((long) (24*60*60*1000 * fraction), ChronoUnit.MILLIS); //fraction is a fraction of the time of day
tl;dr
Use modern java.time classes that years ago supplanted Date class.
Duration duration =
Duration.between(
LocalDate.of( 1899 , Month.DECEMBER , 30 ).atStartOfDay( ZoneOffset.UTC ).toInstant() , // Epoch reference moment used by Delphi.
Instant.now() // Current moment as seen in UTC.
);
double temp =
// Get a whole number of days (integer ) + a decimal fraction of partial day = a `double` number = the `TDateTime` type in Delphi.
duration.toDays() +
(
( double ) duration.minusDays( duration.toDays() ).toMillis() // Milliseconds in our partial day.
/
( double ) Duration.ofDays( 1 ).toMillis() // Milliseconds in a full day, a generic 24-hour day.
);
double r = Math.floor( temp * 1000 ) / 1000; // Truncate to third decimal place to represent a resolution of milliseconds, the limit of `TDateTime` type in Delphi.
java.time
Use only the modern java.time classes in Java, never the legacy classes such as java.util.Date or java.sql.Date.
Capture the current moment as seen in UTC.
Instant now = Instant.now() ;
Because of some twisted history, as its epoch reference for its TDateTime class Delphi uses the first moment of 1899-12-30 presumably in UTC. For the date portion, use LocalDate.
LocalDate delphiEpochDate = LocalDate.of( 1899 , Month.DECEMBER , 30 ); // No, not the 31st, the 30th.
epochDate.toString(): 1899-12-30
As a habit, let java.time determine the first moment of the day, as it is not always 00:00 on all dates in all zones.
Instant delphiEpochMoment = delphiEpochDate.atStartOfDay( ZoneOffset.UTC ).toInstant();
odt.toString(): 1899-12-30T00:00Z
Delphi uses a terrible method of tracking time as inherited from spreadsheets: Using a double floating-point fractional number.
The integer portion represents full days, generic 24-hour long days that ignore the anomalies of political time. For such elapsed time, we use Duration.
Duration d = Duration.between( delphiEpochMoment , now ) ;
long days = d.toDays() ; // Generic 24-hour long days.
Next, get the fractional part that represents a portion of a 24-hour day. First we subtract the amount of the full days, to leave us with a partial day.
Duration partialDay = d.minusDays( days ) ;
Then divide the partial amount by the length of a full day. We will use a resolution of milliseconds rather than the nanoseconds capability of Duration, as it seems Delphi is limited to milliseconds.
double millisOfPartialDay = partialDay.toMillis() ;
double millisOfFullDay = Duration.ofDays( 1 ).toMillis() ;
double tempResult = ( millisOfPartialDay / millisOfFullDay ) ;
We should truncate results to milliseconds. For truncating a double, see this Answer. And we should add our whole number of days.
double tempResult = days + ( millisOfPartialDay / millisOfFullDay ) ;
double result = Math.floor( tempResult * 1000 ) / 1000 ;
Putting that all together.
Instant now = Instant.now();
LocalDate delphiEpochDate = LocalDate.of( 1899 , Month.DECEMBER , 30 ); // No, not the 31st, the 30th.
Instant delphiEpochMoment = delphiEpochDate.atStartOfDay( ZoneOffset.UTC ).toInstant();
Duration d = Duration.between( delphiEpochMoment , now );
long days = d.toDays(); // Generic 24-hour long days.
Duration partialDay = d.minusDays( days );
double millisOfPartialDay = partialDay.toMillis();
double millisOfFullDay = Duration.ofDays( 1 ).toMillis();
double tempResult = days + ( millisOfPartialDay / millisOfFullDay );
double result = Math.floor( tempResult * 1000 ) / 1000; // Truncate to third decimal place to represent a resolution of milliseconds.
System.out.println( "delphiEpochMoment = " + delphiEpochMoment );
System.out.println( "d = " + d );
System.out.println( "tempResult = " + tempResult );
System.out.println( "result = " + result );
delphiEpochMoment = 1899-12-30T00:00:00Z
d = PT1056815H56M10.613011S
tempResult = 44033.99734505787
result = 44033.997
Caveat: I have not tested this code. And I do not use Delphi. So buyer beware: this code is worth every penny you paid for it.
Avoid LocalDateTime
Beware: Do not use LocalDateTime to represent a moment. This class represents a date with time-of-day, but lacks the context of a time zone or offset-from-UTC. For example, take the value of Noon on January 23rd of 2021. Would that be noon in Tokyo Japan? Or noon in Toulouse France? Or noon in Toledo Ohio US? Those would be three very different moments, several hours apart. We do not know which is intended if the time zone is lacking.
You could get away with LocalDateTime in this specific problem of Delphi time-tracking because Delphi (presumably) uses UTC, and UTC uses generic 24-hour-long days as does LocalDateTime. But using LocalDateTime is conceptually not fit for this problem, as we need moments for here.