What is an efficient and easy-to-read expression of testing if LocalDate dayX falls in a duration that is described with a starting day LocalDate day0 and a length Period length?
Currently I am doing like this:
boolean match = !day0.isAfter(dayX) && day0.plus(length).isAfter(dayX);
I just feel this looks a bit dumb and every time I read this it takes several seconds for my brain to tell if the boundaries are correct. So I am looking for a smarter way that maybe involves the Duration or Interval classes.
What you are doing is correct (except that you need to use before method for the first condition). Maybe you can wrap it into a method and call it to make it reusable and look more elegant, e.g.:
public boolean isInRange(Localdate start, Period period, Localdate target){
return !target.before(start) && !target.after(start.plus(period));
}
Somewhat similar approach: day0 < dayx < (day0 + period)
boolean match = (dayx.isAfter(day0) && dayx.isBefore(day0.plus(length));
Related
Problem situation: I have an incredibly high number of records all marked with a timestamp. I'm looping through all of them to do this and that but I need to detect when the day has changed.
Right now for each loop I'm doing:
cal.setTimeInMillis(record.time);
int currentDay = cal.get(Calendar.DAY_OF_WEEK);
Is this as slow as I imagine it is when it's running hundreds of thousands of times?
I imagine I'm missing a really simple modulo answer or something.
Edit: Time zone does not matter, the information I'm collecting more resolves around a consumable report for someone. 24 hours per report is more accurate, so realistically I don't have to worry about whether or not that's 5am - 5am or 3pm - 3pm, just that I was able to gather 24H worth of info.
Thanks all
After Andy Turner’s time test I am not necessarily convinved that you need any optimized solution. In any case, timsmelik’s suggestion is pretty straightforward: convert the time when the day changes to a count of milliseconds since the epoch so you only need to compare long values. I don’t find that it hurts readability very badly. So here it is in code. I am using and warmly recommending java.time, the modern Java date and time API, if only for the conversion from hours to milliseconds and for printing the results. Even when such a conversion seems trivial, it’s always best to leave to the standard library to do it. It’s more self-explanatory and less error-prone, and it’s easier for the reader to convince oneself that it’s correct.
final long twentyfourHoursAsMillis = Duration.ofHours(24).toMillis();
// Times are already sorted descending (from newest to oldest)
long[] times = { 1_611_718_370_000L, 1_611_632_000_000L,
1_611_631_970_000L, 1_611_459_150_000L };
List<List<Long>> chunks = new ArrayList<>();
List<Long> currentChunk = new ArrayList<>();
// Process first time separately to get started
currentChunk.add(times[0]);
long timeOfNextChunk = times[0] - twentyfourHoursAsMillis;
// Process remaining times
for (int i = 1; i < times.length; i++) {
long currentTime = times[i];
if (currentTime <= timeOfNextChunk) {
chunks.add(currentChunk);
currentChunk = new ArrayList<>();
do {
timeOfNextChunk -= twentyfourHoursAsMillis;
} while (currentTime <= timeOfNextChunk);
}
currentChunk.add(currentTime);
}
// Save last chunk, why not?
chunks.add(currentChunk);
// Print result
for (List<Long> chunk : chunks) {
String chunkAsString = chunk.stream()
.map(Instant::ofEpochMilli)
.map(Instant::toString)
.collect(Collectors.joining(", "));
System.out.println(chunkAsString);
}
Output is:
2021-01-27T03:32:50Z, 2021-01-26T03:33:20Z
2021-01-26T03:32:50Z
2021-01-24T03:32:30Z
I am printing Instant objects. They always print in UTC. For your situation you may want to do otherwise if you need to print the times at all.
You should add a check of your assumption that the times come in sorted order.
I have taken your word for it and broken into chunks at 24 hours. 24 hours may not even mean 5am - 5am but could mean for instance from 5 AM EST on March 13 to 6 AM EDT on March 14 because summer time (DST) has begun in the meantime. If you prefer to split at the same clock hour, the code can be modified to do that.
I looked at this question and my problem is similar but not exactly identical. I have many timestamps in problem and they are all in the form "yyyyMMddHHmmssSSS", so I am parsing the string as follow:
DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").parse("20180301050630663")
which will output 2018-03-01T05:06:30.663 (I do not know what the "T" in the middle stands for and do not know how to get rid of it)
Problem is I only care about the timestamps that lies within the range of [09:15:00.000am -12:00:00.000pm] and [15:15:00.000pm -18:00:00.000pm] across all different dates (inclusive too, meaning that if the timestamp is exactly at 09:15:00.000, then it should return true too).
However, how should I tackle this problem because sometimes the dates could be different, i.e. it could be across different dates 2018-03-01T05:06:30.663, 2018-03-02T10:36:30.596,2018-03-11T05:06:30.663? Since the date and times always come together, shall I extract the time from the timestamp ? What is the best way to deal with this in this situation ? I am not too familiar with the datetime libraries in Java.
You were on the right track with DateTimeFormatter. Rather than use DateTimeFormatter.parse, you can pass this formatter to LocalTime.parse which will effectively discard the date portion of the timestamp. You can then use Comparable.compareTo to see whether it's in the ranges you've given.
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
LocalTime time = LocalTime.parse("20180301050630663", formatter);
if (time.compareTo(LocalTime.of(9, 15)) >= 0
&& time.compareTo(LocalTime.of(12, 0)) <= 0)
{
System.out.println("First period");
}
else if (time.compareTo(LocalTime.of(15, 15)) >= 0
&& time.compareTo(LocalTime.of(18, 0 )) <= 0)
{
System.out.println("Second period");
}
If your ranges were exclusive rather than inclusive, you could have used LocalTime.isAfter and LocalTime.isBefore which would have resulted in slightly nicer looking code.
I am looking to calculate the number of minutes given the time of the day.
Eg.: when input is 11:34, the output should be 11*60+34. The date doesn't matter.
I only need it down to the minutes scale. Seconds, milliseconds... don't matter.
Is there a method somewhere in Java doing this the neat way without me calculating it?
Right now, i'm using theTime.split(":"), theTime is a String holding "11:34" here, parsing the integers on each side and doing the calculation.
I saw Time but what I'm doing right now seemed more direct.
Nothing in Systems either.
There is no build in method for it. However here is a one-liner for it:
int timeInMins = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) * 60 + Calendar.getInstance().get(Calendar.MINUTE);
Your approach looks good and sound, however to answer your question it would be simple to say that there is no such build in method which does that. You have to calculate it the way you are doing it right now.
Hi maybe you could use JodaTime? Below example how to get number of minutes from parsed string and from current time. In java 8 there is similar api but I haven't found exactly method like minutesOfDay()
#Test
public void learnHowManyMinutesPassedToday() {
DateTime time = DateTimeFormat.forPattern("HH:mm").parseDateTime("11:34");
System.out.println(time.getMinuteOfDay());
System.out.println(DateTime.now().getMinuteOfDay());
}
If you are looking to have input not from a String, take a look at
Java.util.Calendar.
It has Calendar.HOUR_OF_DAY and Calendar.HOUR and Calendar.MINUTE which could be your input. I'm not sure what the "neat" way of doing this would be. It is a simple calculation.
Calendar rightNow = Calendar.getInstance();
int hour = rightNow.get(Calendar.HOUR);
int min = rightNow.get(Calendar.MINUTE);
System.out.println("TimeMinutes:" + hour * 60 + min);
EDIT:
Except using split use the above.
Regex'ers:
How can I construct a Java Regex to match Strings lexigraphically <= to a given date string?
For example, suppose the input is in YYYY-DD-MM format:
2014-01-20 MLK day
2007-04-14 'twas a very good day
2014-05-19 is today
1998-11-30 someone's birthday
I'd like the filter to return all lines before, say, Groundhog's day of this year, 2014-02-20;
so in the above list the regex would return all lines except today. (I don't want to convert the
dates to Epoch time; I'd like to just pass a Regex to a class that runs a map/reduce job so that
my input record reader can use the Regex as it constructs bundles to deliver to the mappers.)
TIA,
It's near impossible to do <= type logic with regular expressions. You technically could, but you'd have to map out every possible scenario...and then if you want to change the date you are comparing to, the whole expression would change. Instead, I'd just match all the dates/values and then use a date parser to see if it less then the date. Here's an expression to get you started:
(\d{4}-\d{2}-\d{2})\s+(.*)
Then the date will be in capture group one. If it is <= Groundhog's day, then you have the value in capture group two.
To show how complicated it is to do <= logic with regular expression, I whipped together a quick expression to match numbers > 0 and <= 27.
^([1-9]|1[0-9]|2[0-7])$
As you can see, we pretty much need to map out each scenario. You can imagine how much more of a headache this would be with a date..and you wouldn't just be able to say "2014-02-02", you'd need to redo the majority of the expression.
Is there a better way of doing this?
boolean oneCalendarWeek = interval.getStart().plusWeeks(1).equals( interval.getEnd() );
I guess the following won't work because of the way equals is implemented...
boolean oneCalendarWeek = interval.toPeriod().equals( Weeks.ONE );
From the comments:
i really want to know if the api supports something like my second example which i think is clearer than the first
While the example using Weeks.ONE does not work (since Period.equals() first checks if the two Period instances support the same number of fields, and Weeks.ONE only supports one field), this should work instead:
boolean oneCalendarWeek = interval.toPeriod().equals( Period.weeks(1) );
Here is a code sample that tests this for an interval that starts before the start of DST and ends while in DST. However, I'm not 100% sure how this would behave if the start or end time of the Interval fell exactly on the DST boundary.