For example, I have input parameter this format: "04:00-06:00" or "23:00-24:00". Type of parameter - String.
And in my method I must check, that time range in input parameter NOT before current time. How I can do it?
More details:
input time range: "12:00-15:00"
current time: 16:00.
In this case, method must return false.
Another example:
input time range: "10:30-12:10"
current time: 09:51.
method must return true.
Can you please give me some idea or algorithm? How I can implement this method?
First off, you should probably just learn to use Joda time.
That said, since the times are all zero padded, you can just compare strings lexically.
public static boolean inRange(String time, String range) {
return time.compareTo(range.substring(0, 5)) >= 0
&& time.compareTo(range.substring(6)) <= 0;
}
It's good practice to fail fast on malformed inputs.
private static final Pattern VALID_TIME = Pattern.compile("[012][0-9]:[0-5][0-9]");
private static final Pattern VALID_RANGE = Pattern.compile("[012][0-9]:[0-5][0-9]-[012][0-9]:[0-5][0-9]");
and then put an assert at the top of inRange:
assert VALID_TIME.matcher(time).matches() : time
assert VALID_RANGE.matcher(range).matches() : range
EDIT:
If you really need to represent the current time as a Date, then you should compare it this way:
public final class Range {
/** Inclusive as minutes since midnight */
public final int start, end;
public Range(int start, int end) {
assert end >= start;
}
/** #param time in minutes since midnight */
public boolean contains(int time) {
return start <= time && time <= end;
}
public static Range valueOf(String s) {
assert VALID_RANGE.matcher(s).matches() : s;
return new Range(minutesInDay(s.substring(0, 5)),
minutesInDay(s.substring(6));
}
private static int minutesInDay(String time) {
return Integer.valueOf(time.substring(0, 2)) * 60
+ Integer.valueOf(time.substring(3));
}
}
Use Range.valueOf to convert from a String, convert your Date to a number of minutes since midnight in whatever timezone you like using whatever calendar implementation you like, and then use Range.contains.
Date currentDate = new Date();
Date maxDate;
Date minDate;
//Parse range to two substrings
//parse two substrings to [HH, MM]
//for HH && MM parseInt()
//
minDate= new Date.SetHour(HH); minDate.SetMinute(MM);
//repeat for max date
if(currentDate.Before(maxDate) && currentDate.After(minDate))
{
return true;
}
else
return false;
Related
I have a String date format (e.g. dd/MM/yyyy) and want to convert it to a US-style with the month first (e.g. MM/dd/yyyy), programatically.
The use-case of this is to read some data and determine which format fits best.
This sounds trivially easy, but having actually tried implementing it, my solution seems sub-optimal.
Below is my attempt, including a test.
public class DateSwapperExample
{
private static final char dateFormatDayLetter = 'd', dateFormatMonthLetter = 'M';
/**
* Swaps the Day & Month component in a Date Format, if both are present <br>
* When swapping, ensures the frequency is retained - e.g. dd/MMM -> MMM/dd <br>
* TODO Only handles one instance of each tag <br>
* TODO This doesn't handle quoted elements in the Date Format (e.g. "dd/mm 'since dave made the best cakes' yyyy")
*/
private static String swapDayAndMonthInDateFormat(final String dateFormat)
{
// Get the position of the groups
final int[] dayIndex = new int[] {dateFormat.indexOf(dateFormatDayLetter), dateFormat.lastIndexOf(dateFormatDayLetter)};
final int[] monthIndex = new int[] {dateFormat.indexOf(dateFormatMonthLetter), dateFormat.lastIndexOf(dateFormatMonthLetter)};
if ((dayIndex[0] == -1) || (monthIndex[0] == -1))
{
// Cannot swap as dateFormat does not contain both dateFormatDayLetter & dateFormatMonthLetter
return dateFormat;
}
else
{
final int[] firstGroup, secondGroup;
// Work out which group comes first
if (dayIndex[0] < monthIndex[0])
{
firstGroup = dayIndex;
secondGroup = monthIndex;
}
else
{
firstGroup = monthIndex;
secondGroup = dayIndex;
}
// Split the string up into segments, re-organise and combine
// The other parts of the format at the start
return substringConstrained(dateFormat, 0, firstGroup[0])
// The second group
+ substringConstrained(dateFormat, secondGroup[0], secondGroup[1] + 1)
// The other parts of the format in the middle
+ substringConstrained(dateFormat, firstGroup[1] + 1, secondGroup[0])
// The first group
+ substringConstrained(dateFormat, firstGroup[0], firstGroup[1] + 1)
// The other parts of the format at the end
+ substringConstrained(dateFormat, secondGroup[1] + 1, dateFormat.length());
}
}
/** Extension of {#link String#substring(int, int)} that constrains the index parameters to be within the allowed range */
private static String substringConstrained(final String str, final int beginIndex, final int endIndex)
{
return str.substring(constrainToRange(beginIndex, 0, str.length()), constrainToRange(endIndex, 0, str.length()));
}
/** Copy of {#link com.google.common.primitives.Ints#constrainToRange(int, int, int)} to avoid the need of Guava in this example */
private static int constrainToRange(int value, int min, int max)
{
return Math.min(Math.max(value, min), max);
}
#org.junit.Test
public void testSwapDayAndMonthInDateFormat()
{
org.junit.Assert.assertEquals("Md", swapDayAndMonthInDateFormat("dM"));
org.junit.Assert.assertEquals("MMd", swapDayAndMonthInDateFormat("dMM"));
org.junit.Assert.assertEquals("Mdy", swapDayAndMonthInDateFormat("dMy"));
org.junit.Assert.assertEquals("Myd", swapDayAndMonthInDateFormat("dyM"));
org.junit.Assert.assertEquals("yMd", swapDayAndMonthInDateFormat("ydM"));
org.junit.Assert.assertEquals("aMbdc", swapDayAndMonthInDateFormat("adbMc"));
org.junit.Assert.assertEquals("MM/dd/yyyy", swapDayAndMonthInDateFormat("dd/MM/yyyy"));
org.junit.Assert.assertEquals("MMM/dd/yyyy", swapDayAndMonthInDateFormat("dd/MMM/yyyy"));
for (final String str : new String[] {"ydy", "yMy", "yDy", "ymy", "Dm", "Dmm", "DD/mm/yyyy", "DD/mmm/yyyy"})
{
org.junit.Assert.assertEquals(str, swapDayAndMonthInDateFormat(str));
}
}
}
private static String swapDayAndMonthInDateFormat(final String dateFormat)
{
return dateFormat.replaceFirst("(d+)(.*?)(M+)", "$3$2$1");
}
I am far from convinced that this is the good solution to your real problem. But it makes you test pass.
Also you should not want to use SimpleDateFormat. That class is notoriously troublesome and along with Date and friends long outdated. Instead use DateTimeFormatter and other classes from java.time, the modern Java date and time API. Format pattern strings still look similar, though, so it could be that this answer is still relevant.
Please suggest if there is an API support to determine if my time is between 2 LocalTime instances, or suggest a different approach.
I have this entity:
class Place {
LocalTime startDay;
LocalTime endDay;
}
which stores the working day start and end time, i.e. from '9:00' till '17:00', or a nightclub from '22:00' till "5:00".
I need to implement a Place.isOpen() method that determines if the place is open at a given time.
A simple isBefore/isAfter does not work here, because we also need to determine if the end time is on the next day.
Of course, we can compare the start and end times and make a decision, but I want something without additional logic, just a simple between() call. If LocalTime is not sufficient for this purpose, please suggest other.
If I understand correctly, you need to make two cases depending on whether the closing time is on the same day as the opening time (9-17) or on the next day (22-5).
It could simply be:
public static boolean isOpen(LocalTime start, LocalTime end, LocalTime time) {
if (start.isAfter(end)) {
return !time.isBefore(start) || !time.isAfter(end);
} else {
return !time.isBefore(start) && !time.isAfter(end);
}
}
This looks cleaner for me:
if (start.isBefore(end)) {
return start.isBefore(date.toLocalTime()) && end.isAfter(date.toLocalTime());
} else {
return date.toLocalTime().isAfter(start) || date.toLocalTime().isBefore(end);
}
I have refactored #assylias answer so i use int instead of local time as i get open and close hour from api int integer format
public static boolean isOpen(int start, int end, int time) {
if (start>end) {
return time>(start) || time<(end);
} else {
return time>(start) && time<(end);
}
}
public static boolean isOpen(int start, int end) {
SimpleDateFormat sdf = new SimpleDateFormat("HH");
Date resultdate = new Date();
String hour = sdf.format(resultdate);
int time = Integer.valueOf(hour);
if (start>end) {
return time>(start) || time<(end);
} else {
return time>(start) && time<(end);
}
}
I'm create a java webapp and I have to create a consecutive number that should starts every month. The idea is to have something like this:
01-0414 / 02-0414 /03-0414 / 04-0414
where the first two digits should be the consecutive number, and the last four digits are the month and year.
I'm using spring 3.2.2 and hibernate 4.2.6. I really appreciate any help about this.
thanks
Well, your question is not clear. But as far as I understand you need help to get the date. You can use Calendar() or Date(), use something like this Calendar.get(Calendar.MONTH)to get the month and year (or simply parse to string and substring where you want).
Regarding the number at the beginning, I will assume (again, since you are not clear) that it is passed as an input. So basically you concatenate that with the "-" and the output of the previous step; the date thingy.
I hope I helped!
If you want to encode your web app string using a sequence number that gets reset to 1 at the beginning of each month, you could use a singleton class instance to hold the month and sequence number state. The code string generator method checks whether the month has changed, and if so, it resets the internal current month to the new month, and resets the effective internal sequence number to 1.
Here is the generator class (see below for an example of how to use it):
public class MySequenceCodeStringGenerator {
private static final int generatorMonth;
private static final int generatorSequenceNumber;
// Create a singleton instance to hold month and sequence number state.
private static final MySequenceCodeStringGenerator INSTANCE = new MySequenceCodeStringGenerator();
private MySequenceCodeStringGenerator() {
generatorMonth = getCurrentMonth();
generatorSequenceNumber = 0;
}
/////////////////////////
// PUBLIC functions:
/////////////////////////
// Get the singleton instance:
public static MySequenceCodeStringGenerator getInstance() {
return INSTANCE;
}
// Get the formatted sequence code string:
public static int getSequenceCodeString {
int sequenceNumber = getSequenceNumber();
Calendar now = Calendar.getInstance();
int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH);
String yearString = String.valueOf(year);
return String.format( "%02d-%02d%s", sequenceNumber, month+1, yearString.substring(2) );
}
// Get the current month:
private int getCurrentMonth() {
Calendar now = Calendar.getInstance();
return now.get(Calendar.MONTH);
}
// Get the singleton sequence number. Update if this is a new month.
private int getSequenceNumber() {
currentMonth = getCurrentMonth();
if ( currentMonth != generatorMonth ) {
generatorMonth = currentMonth;
generatorSequenceNumber = 0;
}
return ++generatorSequenceNumber;
}
}
Here's an example of how you use the generator class:
String myWebAppString = MySequenceCodeStringGenerator.getInstance().getSequenceCodeString();
I'm trying to calculate the number of complete contiguous Periods in an Interval in Joda Time (where the Period is arbitrary but constant).
The simple solution I've come up with is a linear search using a while loop:
public static long periodsInAnInterval(Interval interval, Period period) {
int periods = -1;
DateTime marker = interval.getStart();
while (marker.isBefore(interval.getEnd()) || marker.isEqual(interval.getEnd())) {
marker = marker.plus(period);
periods++;
}
return periods;
}
An O(n) solution is obviously pretty horrible, so can anyone think of a better way? I'm wondering whether some kind of binary search could be used...
Here's a test case: https://gist.github.com/Mahoney/9899832
Edit - remember a Period does not have a known number of seconds; Period.toStandardDuration() is just an approximation assuming years have 365 days, months have 30 days and days have 24 hours. (Actually a quick test reveals Period.toStandardDuration bombs out with an exception if you have years or months in the period.)
Edit 2 - I'm happy to assume that the first period begins at the start of the interval - otherwise I suspect the answer might vary depending on whether the remainder time were at the beginning, the end or both.
Here's my preferred solution: use average length of a period to form a best guess and then refine it. This seems the most efficient and elegant way to do it.
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import org.joda.time.*;
import static com.google.common.collect.FluentIterable.from;
import static java.util.Arrays.asList;
import static org.joda.time.DurationFieldType.*;
public class PeriodArithmetic {
public static long periodsInAnInterval(Interval interval, Period period) {
int bestGuess = (int) (interval.toDurationMillis() / toAverageMillis(period));
if (bestGuess < 0) return 0;
if (startPlusScaledPeriodIsAfterEnd(interval, period, bestGuess + 1)) {
return searchDownwards(interval, period, bestGuess);
} else {
return searchUpwards(interval, period, bestGuess);
}
}
private static long searchDownwards(Interval interval, Period period, int currentGuess) {
if (startPlusScaledPeriodIsAfterEnd(interval, period, currentGuess)) {
return searchDownwards(interval, period, currentGuess - 1);
} else {
return currentGuess;
}
}
private static long searchUpwards(Interval interval, Period period, int currentGuess) {
if (!startPlusScaledPeriodIsAfterEnd(interval, period, currentGuess + 1)) {
return searchUpwards(interval, period, currentGuess + 1);
} else {
return currentGuess;
}
}
private static boolean startPlusScaledPeriodIsAfterEnd(Interval interval, Period period, int scalar) {
return interval.getStart().plus(period.multipliedBy(scalar)).isAfter(interval.getEnd());
}
private static final long MILLIS_IN_DAY = Days.ONE.toStandardSeconds().getSeconds() * 1000L;
private static final long MILLIS_IN_YEAR = Days.ONE.toStandardSeconds().getSeconds() * 365250L;
private static final ImmutableMap<DurationFieldType, Long> averageLengthMillis
= ImmutableMap.<DurationFieldType, Long>builder()
.put(millis(), 1L)
.put(seconds(), 1000L)
.put(minutes(), Minutes.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(hours(), Hours.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(halfdays(), MILLIS_IN_DAY / 2)
.put(days(), MILLIS_IN_DAY)
.put(weeks(), Weeks.ONE.toStandardSeconds().getSeconds() * 1000L)
.put(months(), MILLIS_IN_YEAR / 12)
.put(years(), MILLIS_IN_YEAR)
.put(weekyears(), MILLIS_IN_YEAR)
.put(centuries(), MILLIS_IN_YEAR * 100)
.put(eras(), Long.MAX_VALUE)
.build();
private static long toAverageMillis(Period period) {
final Iterable<Long> milliValues = from(asList(period.getFieldTypes())).transform(toAverageMillisForFieldType(period));
return total(milliValues);
}
private static Function<DurationFieldType, Long> toAverageMillisForFieldType(final Period period) {
return new Function<DurationFieldType, Long>() {
#Override
public Long apply(DurationFieldType durationFieldType) {
final Long averageDuration = averageLengthMillis.get(durationFieldType);
return period.get(durationFieldType) * averageDuration;
}
};
}
private static long total(Iterable<Long> milliValues) {
long acc = 0;
for (Long milliValue : milliValues) {
acc += milliValue;
}
return acc;
}
}
I've put the beginnings of a binary search solution here: https://gist.github.com/Mahoney/9899936
It's much more complicated than the linear search; on the other hand it's roughly 100x faster at finding the number of months in 1,000 years, for instance.
It's also unfinished - more of an experiment than anything, so I'm sure there are untested edge cases (negative periods?).
Still keen to know if anyone has a more elegant solution (or just one I don't have to write, test & maintain!).
I am trying to get a date range using Guava's new Range functionality, via
Range<Date> dateRange = Ranges.range(start, BoundType.CLOSED, end, BoundType.CLOSED);
My goal is to get the hours in this date range. So I have created a DiscreteDomain like such:
private static final DiscreteDomain<Date> HOURS = new DiscreteDomain<Date>() {
public Date next(Date value) {
return addHours(value, 1);
}
private Date addHours(Date value, int i) {
Calendar cal = Calendar.getInstance();
cal.setTime(value);
cal.add(Calendar.HOUR_OF_DAY, i);
return cal.getTime();
}
public Date previous(Date value) {
return addHours(value, -1);
}
public long distance(Date start, Date end) {
Calendar cal1 = Calendar.getInstance();
cal1.setTime(start);
Calendar cal2 = Calendar.getInstance();
cal2.setTime(end);
return cal2.getTimeInMillis() - cal1.getTimeInMillis();
}
public Date minValue() {
return new Date(Long.MIN_VALUE);
}
public Date maxValue() {
return new Date(Long.MAX_VALUE);
}
};
If I merely sysout the output, I get the closed set
[Thu Feb 24 00:00:00 EST 2011..Thu Feb 24 00:02:00 EST 2011]
I really want to see each hour in the range, however, so I try a for loop:
for (Date hour : hours) {
System.out.println(hour);
}
When running this block, I seem to get an infinite set, beginning at the left side of the range, but not stopping at the right side, making me kill the IDE. What am I doing wrong?
I think this might be due to the behavior of the Iterator returned by the ContiguousSet (returned by Range.asSet()):
#Override public UnmodifiableIterator<C> iterator() {
return new AbstractLinkedIterator<C>(first()) {
final C last = last();
#Override
protected C computeNext(C previous) {
return equalsOrThrow(previous, last) ? null : domain.next(previous);
}
};
}
private static boolean equalsOrThrow(Comparable<?> left,
#Nullable Comparable<?> right) {
return right != null && compareOrThrow(left, right) == 0;
}
private static int compareOrThrow(Comparable left, Comparable right) {
return left.compareTo(right);
}
It only stops when the next computed value is equal to the right bound of the range.
In your case, have you tried calling it using Thu Feb 24 02:00:00 instead of Thu Feb 24 00:02:00 for the right bound of your range?
I think this behavior is problematic, and it might be worth asking if equalsOrThrow() could be changed to check for left <= right instead of left == right
Also, your distance() method is incorrect. It should return the distance in hours, not in milliseconds, according to the method contract.
EDIT
All this being said, I believe the real problem is that, according to the DiscreteDomain's javadoc:
A discrete domain always represents
the entire set of values of its type;
it cannot represent partial domains
such as "prime integers" or "strings
of length 5."
In your case, you are attempting to create a discrete domain over hourly dates, which is a partial domain of all dates. This is, I think, the root cause of the problem. When you have a partial domain, the equalsOrThrow method becomes unreliable, and it can "miss" the right bound of your range.
I just tried this and it worked fine for me. #eneveu already pointed out the issue with your distance method as well. I'm also guessing that there's some minor difference at the millisecond level between start and end which means that you'll never actually get a Date equal to end by adding hours to start.
However, that's all just symptoms of using the classes in a way they aren't designed to work. The Javadoc for DiscreteDomain states:
A discrete domain always represents the entire set of values of its type; it cannot represent partial domains such as "prime integers" or "strings of length 5."
A DiscreteDomain of "hours" does not represent the domain of all possible Date objects and as such breaks its contract.