JSpinner time constraint - java

I am trying to create a spinner that has hours and minutes. The minutes part needs to increment by 10 mins only and the time must range from the current time to an end time. I also need the minimum value (previously current time) to update to current time.
I tried playing around with it, but I just couldn't get it to work.
JSpinner spinner1 = new javax.swing.JSpinner();
SpinnerDateModel spinnermodel = new SpinnerDateModel();
spinnermodel.setCalendarField(Calendar.MINUTE);
spinner1.setModel(spinnermodel);
spinner1.setEditor(new JSpinner.DateEditor(spinner1, "hh:mm"));
SpinnerModel model = new SpinnerDateModel(currentDate, currentDate, latestDate, Calendar.MINUTE * 10 ?);

The SpinnerDateModel just uses 1 to increment the field you want to change.
I extended the SpinnerDateModel to add an addition property to the model to control the increment value instead of hard coding to 1:
import java.util.*;
import javax.swing.*;
public class MySpinnerDateModel extends SpinnerDateModel
{
private int increment = 1;
public MySpinnerDateModel(Date value, Comparable start, Comparable end, int calendarField)
{
super(value, start, end, calendarField);
}
public MySpinnerDateModel()
{
this(new Date(), null, null, Calendar.DAY_OF_MONTH);
}
public void setIncrement(int increment)
{
this.increment = increment;
}
public int getIncrement()
{
return increment;
}
#Override
public Object getNextValue()
{
Calendar cal = Calendar.getInstance();
Date value = (Date)getValue();
cal.setTime(value);
cal.add(getCalendarField(), increment);
Date next = cal.getTime();
Comparable end = getEnd();
return ((end == null) || (end.compareTo(next) >= 0)) ? next : null;
}
#Override
public Object getPreviousValue()
{
Calendar cal = Calendar.getInstance();
Date value = (Date)getValue();
cal.setTime(value);
cal.add(getCalendarField(), -increment);
Date prev = cal.getTime();
Comparable start = getStart();
return ((start == null) || (start.compareTo(prev) <= 0)) ? prev : null;
}
}
You should be able to use the model the way you did before but with one additional statement:
MySpinnerDateModel model = new MySpinnerDateModel(currentDate, currentDate, latestDate, Calendar.MINUTE);
model.setIncrement( 10 );

You can extend the SpinnerDateModel to specify the behavior. Below is an example in which the getNextValue and getPreviousValue are overridden to return values +/- 10 minutes:
Date now = new Date();
Date start = now;
final long tenMinutesInMillis = 1000 * 60 * 10;
Date end = new Date(now.getTime() + tenMinutesInMillis * 60);
SpinnerModel model = new SpinnerDateModel(now, start, end, Calendar.MINUTE){
#Override
public Object getNextValue(){
Date newDate = new Date(getDate().getTime() + tenMinutesInMillis);
Date endDate = (Date)getEnd();
return newDate.getTime() > endDate.getTime() ? endDate : newDate;
}
#Override
public Object getPreviousValue(){
Date newDate = new Date(getDate().getTime() - tenMinutesInMillis);
Date startDate = (Date)getStart();
return newDate.getTime() < startDate.getTime() ? startDate : newDate;
}
};

Related

A more elegant way to iterate through a list to compare 2 elements next to one another

I have a method which works in this way:
Take as an argument 3 params - a list with dates (sorted in ascending order) , interval unit and interval value
Check whether the next element doesn't exceed the previous date (interval). In other words, given the interval of 30 min, prev - 10:00, next 10:29 - iterate further. if next is 10:31 - break it and return the counter of dates in a row.
The code for it is below:
public static void main(String[] args)
{
Date d1 = new Date();
Date d2 = addOrSubtractTimeUnitFromDate(d1, Calendar.MINUTE, 10, true);
Date d3 = addOrSubtractTimeUnitFromDate(d2, Calendar.MINUTE, 10, true);
Date d4 = addOrSubtractTimeUnitFromDate(d3, Calendar.MINUTE, 10, true);
Date d5 = addOrSubtractTimeUnitFromDate(d4, Calendar.MINUTE, 10, true);
Date d6 = addOrSubtractTimeUnitFromDate(d5, Calendar.MINUTE, 10, true);
List<Date> threeDates = new ArrayList<>();
threeDates.add(d1);
threeDates.add(d2);
threeDates.add(d3);
threeDates.add(d4);
threeDates.add(d5);
threeDates.add(d6);
System.out.println(returnDatesInARowCounter(threeDates, Calendar.MINUTE, 30));
}
private static int returnDatesInARowCounter(List<Date> allDates, int intervalBetween2DatesTimeUnit, int intervalValue)
{
int datesInARowCounter = allDates.size() > 0 ? 1 : 0; // esp. this line (in case allDates is empty)
Date lastDate = null;
Date nextDate;
Iterator<Date> iter = allDates.iterator();
while (iter.hasNext())
{
nextDate = iter.next();
if (lastDate != null) // both lastDate и nextDate are initialized now
{
if(isNextIncidentInIntervalWithLastOneOrNot(lastDate, nextDate, intervalBetween2DatesTimeUnit, intervalValue, true))
{
datesInARowCounter += 1;
}
else break;
}
lastDate = nextDate;
}
return datesInARowCounter;
}
public static Date addOrSubtractTimeUnitFromDate(Date dateToAddToOrSubtractFrom, int calendarTimeUnit, int value, boolean isAdd)
{
if(!isAdd)
{
value = -value;
}
Calendar cal = Calendar.getInstance();
cal.setTime(dateToAddToOrSubtractFrom);
cal.add(calendarTimeUnit, value);
return cal.getTime();
}
private static boolean isNextIncidentInIntervalWithLastOneOrNot(Date lastIncidentRegDate, Date nextIncidentRegDate, int intervalTimeUnit, int intervalValue, boolean isBetween)
{
Date currentIncidentPlusInterval = addOrSubtractTimeUnitFromDate(lastIncidentRegDate, intervalTimeUnit, intervalValue, true);
boolean betweenBool = isDateBetween(nextIncidentRegDate, lastIncidentRegDate, currentIncidentPlusInterval);
return isBetween == betweenBool;
}
private static boolean isDateBetween(Date targetDate, Date startDate, Date endDate)
{
return targetDate.compareTo(startDate) >= 0 && targetDate.compareTo(endDate) <= 0;
}
However, the code looks peculiar to me. Is the any way to make it look more readable?
If you are using Java 8 or newer, you can use the java.time-API instead. It's built-in support for "periods of time" makes the actual implementation much simpler.
static int daysInARow(List<Instant> allInstants, Duration maxDifference) {
int counter = allInstants.size() > 0 ? 1 : 0;
Instant previous = allInstants.get(0);
for (int i = 1; i < allInstants.size(); i++) {
Instant current = allInstants.get(i);
if (Duration.between(previous, current).compareTo(maxDifference) > 0)
break;
counter++;
previous = current;
}
return counter;
}
If you're using java.util.Date in other parts of your project, you can easily convert between Instants by using
Date#from(Instant)
and
Date#toInstant()

Return value based on current month

I'm using this Java code to get price based on month start and end date.
public int getPrice()
{
java.util.Date today = Calendar.getInstance().getTime();
Calendar aumgc = new GregorianCalendar();
aumgc.set(Calendar.AUGUST, 8);
aumgc.set(Calendar.DAY_OF_MONTH, 1);
java.util.Date augustStart = aumgc.getTime();
Calendar emgc = new GregorianCalendar();
emgc.set(Calendar.AUGUST, 8);
emgc.set(Calendar.DAY_OF_MONTH, 31);
java.util.Date augustEnd = emgc.getTime();
Calendar sepmgc = new GregorianCalendar();
sepmgc.set(Calendar.SEPTEMBER, 9);
sepmgc.set(Calendar.DAY_OF_MONTH, 1);
java.util.Date septemberStart = sepmgc.getTime();
Calendar eomgc = new GregorianCalendar();
eomgc.set(Calendar.SEPTEMBER, 9);
eomgc.set(Calendar.DAY_OF_MONTH, 31);
java.util.Date septemberEnd = eomgc.getTime();
Calendar ocmgc = new GregorianCalendar();
ocmgc.set(Calendar.OCTOBER, 10);
ocmgc.set(Calendar.DAY_OF_MONTH, 1);
java.util.Date octoberStart = ocmgc.getTime();
Calendar eocmgc = new GregorianCalendar();
eocmgc.set(Calendar.OCTOBER, 10);
eocmgc.set(Calendar.DAY_OF_MONTH, 31);
java.util.Date octoberEnd = eocmgc.getTime();
if (!(today.before(augustStart) || today.after(augustEnd)))
{
return 30;
}
if (!(today.before(septemberStart) || today.after(septemberEnd)))
{
return 40;
}
if (!(today.before(octoberStart) || today.after(octoberEnd)))
{
return 50;
}
return 0;
}
As you can see I'm using a lot of code to get the price based on current month. How I can simplify the code and use SQL date?
Is there any already made solution implemented in JVM?
I would suggest to use latest API LocalDateTime from Java 8 to do this king of thing, easily :
import java.time.Month; // Enum use in `switch` statement.
public int getPrice() {
LocalDateTime now = LocalDateTime.now(); // line 2
switch (now.getMonth()) { // line 3
case AUGUST:
return 30;
case SEPTEMBER:
return 40;
case OCTOBER:
return 50;
default:
return 0;
}
}
// line 2&3 can be reduce in : // switch (LocalDateTime.now().getMonth()){
This would return the same :
public int getPrice() {
return (LocalDateTime.now().getMonthValue() - 8) * 10 + 30;
}
//or return (LocalDateTime.now().getMonthValue() - 5) * 10;

Android-Week-View loading events per week asynchronously

First off, I'm using the library https://github.com/alamkanak/Android-Week-View
I have an API returning data for one week at a time.
For example GET /stuff/{current_week_number}
Then a scroll listener set up that checks if the week has changed, and will then load
GET /stuff/{new_week_number}
The problem is that all events will be in the current week position as duplicates. I know the library wants events on a per month basis, is it the problem?
Been debugging this for a day now, help would be greatly appreciated.
Function for creating the event:
private WeekViewEvent createNew(JSONObject json, int week) {
String eventTitle = "";
String colorString = "#999";
String startTimeString = "";
String endTimeString = "";
int dayOfWeek = 0;
try {
eventTitle = json.getString("text").replaceAll("\n", " ");
colorString = json.getString("color");
startTimeString = json.getString("startTime");
endTimeString = json.getString("endTime");
dayOfWeek = json.getInt("day");
} catch (JSONException e) {
e.printStackTrace();
}
Calendar startTime = Calendar.getInstance();
startTime.set(Calendar.HOUR_OF_DAY, hoursFromString(startTimeString));
startTime.set(Calendar.MINUTE, minutesFromString(startTimeString));
startTime.set(Calendar.WEEK_OF_YEAR, week);
startTime.set(Calendar.DAY_OF_WEEK, dayOfWeek);
Calendar endTime = (Calendar) startTime.clone();
endTime.set(Calendar.HOUR_OF_DAY, hoursFromString(endTimeString));
endTime.set(Calendar.MINUTE, minutesFromString(endTimeString));
WeekViewEvent event = new WeekViewEvent(mGlobalCounter, eventTitle, startTimeString + " - " + endTimeString, startTime, endTime);
event.setColor(Color.parseColor(colorString));
if (event.getColor() == Color.WHITE) {
event.setColor(R.color.event_color_01);
}
return event;
}
Month change listener + helper method (from example)
private boolean eventMatches(WeekViewEvent event, int year, int month) {
return (event.getStartTime().get(Calendar.YEAR) == year && event.getStartTime().get(Calendar.MONTH) == month - 1) || (event.getEndTime().get(Calendar.YEAR) == year && event.getEndTime().get(Calendar.MONTH) == month - 1);
}
MonthLoader.MonthChangeListener mMonthChangeListener = new MonthLoader.MonthChangeListener() {
#Override
public List<WeekViewEvent> onMonthChange(int newYear, int newMonth) {
// Populate the week view with some events.
List<WeekViewEvent> events = new ArrayList<>(); //getEvents(newYear, newMonth);
for (WeekViewEvent event : mNewEvents) {
if (eventMatches(event, newYear, newMonth)) {
events.add(event);
}
}
mFetchedWeeks.add(Integer.valueOf(mWeek));
return events;
}
};
And here's the JSON-response from the API
https://gist.github.com/jonathanort/668de267966e3b673fffe23dfbdfb90b
Also, my modified version of WeekView.java
https://gist.github.com/jonathanort/472d86355dcdbc338f13373a838f548a
The solution was to add the line
startTime.get(Calendar.DAY_OF_WEEK);
before
startTime.set(Calendar.DAY_OF_WEEK, dayOfWeek);
Apparently it will not set the day otherwise

working with datetime to check which one is bigger

I have two datetime values and i dont knw how to compare them. I know if i had only date values then before() and after() methods would have worked but i have no idea about Datetime values. All i have done is below plz tell me if its correct ?? and plz do guide me if its not a good way and a better alternative is available.
Date now = new Date();
DateTime currenttime = new DateTime(now, TimeZone.getTimeZone("IST"));
DateTime edate = e.getEnd().getDateTime();
if(currenttime.getValue()>edate.getValue())
{
//here I want to do the logic to delete this event.
}
e refers to the event object that is of google calendar. All i want to do here is check if Event e is past todays date and time. and if it is then i wanna delete the event.
You can use jdk Calendar to get and check days:
public boolean isDatePass(Date date) {
Calendar calendar = Calendar.getInstance();
// Getting day of year and year of checked date:
calendar.setTime(date);
int checkedYear = calendar.get(Calendar.YEAR);
int checkedDay = calendar.get(Calendar.DAY_OF_YEAR);
// Getting day of year and year of current date:
calendar.setTime(new Date());
int currentYear = calendar.get(Calendar.YEAR);
int currentDay = calendar.get(Calendar.DAY_OF_YEAR);
if(checkedYear != currentYear) {
return checkedYear < currentYear;
}
return checkedDay < currentDay;
}
For yoda DateTime:
public boolean isDatePass(DateTime date) {
// Getting day of year and year of checked date:
int checkedYear = date.getYear();
int checkedDay = date.getDayOfYear();
// Getting day of year and year of current date:
DateTime currentTime = new DateTime(now, TimeZone.getTimeZone("IST"));
int currentYear = currentTime.getYear();
int currentDay = currentTime.getDayOfYear();
if(checkedYear != currentYear) {
return checkedYear < currentYear;
}
return checkedDay < currentDay;
}
Not days only but time:
public boolean isDatePass(DateTime date) {
DateTime currentTime = new DateTime(now, TimeZone.getTimeZone("IST"));
return date.isAfter(currentTime);
}
More simple solution (according to javadoc when pass null to isAfter/isBefore this mean current or now):
public boolean isDatePass(DateTime date) {
return date.isAfter(null); // but it does not take in account time zone
}
public String deleteEvents() throws ParseException {
try {
boolean evtDelMsg = false;
int iEvtCnt = 0;
int totalEvents = lstEvents.size();
System.out.println("events are :"+lstEvents.getItems().toString());
if(lstEvents.size()>0)
{
for(Event e : lstEvents.getItems())
{
System.out.println("startdate is "+e.getStart().toString());
Date now = new Date();
try
{
if((new Date()).getTime() < e.getEnd().getDateTime().getValue())
{
evtDelMsg = EventManager.deleteEvent(getGoogleCalObj(), selectedCalId, e.getId());
iEvtCnt++;
}
}
catch(NullPointerException npe)
{
System.out.println("edate is null so creating");
processImportedEventsData();
}
}
}
else
{
System.out.println("no events in this calendar");
}
setInfoMsg("Successfully deleted " + iEvtCnt + " Events out of total " + totalEvents);
createEventFlag = true;
processImportedEventsData();
}
catch (IOException ex)
{
Logger.getLogger(ManageCalendar.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
This one worked for me I simply used the long value of the event's i.e "e" date and time and compared with the todays date time.The getValue() method returns in long which is milliseconds. This made it a bit simple.
And then in the loop i deleted all the events calling deleteEvent() of EventManager.

Set step for a DateEditor in a JSpinner

How can a step be specified for JSpinner? (for example of 10 minutes instead of 1)
There is a model that allow select step size for numbers
SpinnerNumberModel(value, min, max, step);
but how to set it for Dates?
This code
JSpinner timeSpinner = new JSpinner( new SpinnerDateModel() );
DateEditor timeEditor = new DateEditor(timeSpinner, "HH:mm:ss");
timeSpinner.setEditor(timeEditor);
timeSpinner.setValue(new Date()); // will only show the current time
(From this answer Is there any good and free Date AND Time Picker available for Java Swing?)
Allows edit time only, but the steps are always 1 hour, 1 minute, or 1 second depending on if "HH", "HH:mm", or "HH:mm:ss" is specified.
Is there any simple way to have min, max, and step for minutes?
Thanks
Use DateEditor.getModel() to get the SpinnerDateModel which provides setStart,setEnd and setCalendarField.
As you can see in the code of SpinnerDateModel:
public Object getNextValue() {
Calendar cal = Calendar.getInstance(); //Current date.
cal.setTime(value.getTime()); //Set the date to the current value of the model.
cal.add(calendarField, 1); //Increment the date by 1 unit of the selected calendar field (e.g. 1 month).
Date next = cal.getTime(); //Convert back to Date Object.
return ((end == null) || (end.compareTo(next) >= 0)) ? next : null;
}
public Object getPreviousValue() {
Calendar cal = Calendar.getInstance(); //Current date.
cal.setTime(value.getTime()); //Set the date to the current value of the model.
cal.add(calendarField, -1); //Decrement the date by 1 unit of the selected calendar field (e.g. 1 month).
Date prev = cal.getTime(); //Convert back to Date Object.
return ((start == null) || (start.compareTo(prev) <= 0)) ? prev : null;
}
It works with adding or substracting 1 unit of the specified calendar field.
You want to customize this number 1 to something else.
So I see two options here:
Reimplement AbstractSpinnerModel. Just copy-paste SpinnerDateModel (it's NOT big), then introduce your integer field "step" for example, and instead of the number 1, just put "step" in getNext and getPrevious.
Implement a SpinnerDateModel, which also works internally with a SpinnerDateModel. It's going to be a lot smaller, but is a bit hackish I guess.
Follows the code of such an SpinnerDateModel (case 2):
import java.awt.GridLayout;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerDateModel;
public class MultiStepDateSpinner extends JPanel {
private static class StepperSpinnerDateModel extends SpinnerDateModel {
private final SpinnerDateModel internal; //We let the internal SpinnerDateModel do the work for us.
private final int step; //The number of steps to increment and decrement per click.
private Object currentValue; //Needed to get restored each time getPreviousValue and getNextValue is called.
private StepperSpinnerDateModel(final Date value, final Comparable start, final Comparable end, final int calendarField, final int step) {
internal = new SpinnerDateModel(value, start, end, calendarField);
if (step <= 0)
throw new IllegalArgumentException("Non positive step.");
this.step = step;
currentValue = internal.getValue();
}
private StepperSpinnerDateModel(final int step) {
this(new Date(), null, null, Calendar.DAY_OF_MONTH, step);
}
#Override
public Object getValue() {
return currentValue;
}
#Override
public void setValue(final Object value) {
internal.setValue(value);
currentValue = value;
fireStateChanged(); //Important step for the spinner to get updated each time the model's value changes.
}
#Override
public Object getNextValue() {
Object next = null;
for (int i=0; i<step; ++i) { //Calculate step next values:
next = internal.getNextValue();
internal.setValue(next); //We have to set the next value internally, in order to recalculate the next-next value in the next loop.
}
internal.setValue(currentValue); //Restore current value.
return next;
}
#Override
public Object getPreviousValue() {
Object prev = null;
for (int i=0; i<step; ++i) { //Calculate step previous values:
prev = internal.getPreviousValue();
internal.setValue(prev); //We have to set the previous value internally, in order to recalculate the previous-previous value in the next loop.
}
internal.setValue(currentValue); //Restore current value.
return prev;
}
}
private MultiStepDateSpinner() {
super(new GridLayout(0, 1));
//Increment and decrement by 4 minutes each step.
//The null values indicate there shall be no minimum nor maximum date.
//The current value is set to the current date.
final JSpinner spinner = new JSpinner(new StepperSpinnerDateModel(new Date(), null, null, Calendar.MINUTE, 4));
final JButton getValueButton = new JButton("Get value");
getValueButton.addActionListener(e -> {
JOptionPane.showMessageDialog(null, spinner.getValue(), "Got value", JOptionPane.PLAIN_MESSAGE);
});
add(spinner);
add(getValueButton);
}
public static void main(final String[] args) {
final JFrame frame = new JFrame("Frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new MultiStepDateSpinner());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
The example code above, is runnable, so you can see it in practice.
Why not just let it be an AbstractSpinnerModel instead of SpinnerDateModel? Because we need it to be identified as an instance of SpinnerDateModel so the JSpinner internally allocates by default a DateEditor for the editor.
Even if you extend AbstractSpinnerModel and supply the spinner with a DateEditor, you will get an exception complaining about the model not being a SpinnerDateModel.
The cleanest way though as I see it, is to reimplement AbstractSpinnerModel and copy paste the code of SpinnerDateModel if needed (which is not big) and introduce any fields you feel.
Incrementa JSpinnerDateEditor_15_+_15_minutos
Calendar Horacalendario = Calendar.getInstance();
Horacalendario.setTime(cita_hora.getDate());
Date HoraParaCita = Horacalendario.getTime();//para asignar luego del incremento de 15 formato date()'
int minutos = Horacalendario.get(Calendar.MINUTE);
Horacalendario.set(Horacalendario.get(Calendar.YEAR), Horacalendario.get(Calendar.MONTH),
Horacalendario.get(Calendar.DATE), Horacalendario.get(Calendar.HOUR), 0);//hago cero los minutos
if (minutos >= 1 & minutos <= 14) {
Horacalendario.add(Calendar.MINUTE, 15); //minutos A Sumar
HoraParaCita = Horacalendario.getTime();
cita_hora.setDate(HoraParaCita);
}
if (minutos >= 16 & minutos <= 29) {
Horacalendario.add(Calendar.MINUTE, 30);
HoraParaCita = Horacalendario.getTime();
cita_hora.setDate(HoraParaCita);
}
if (minutos >= 31 & minutos <= 44) {
Horacalendario.add(Calendar.MINUTE, 45);
HoraParaCita = Horacalendario.getTime();
cita_hora.setDate(HoraParaCita);
}
if (minutos >= 46 & minutos <= 59) {
Horacalendario.set(Horacalendario.get(Calendar.YEAR), Horacalendario.get(Calendar.MONTH),
Horacalendario.get(Calendar.DATE), Horacalendario.get(Calendar.HOUR), 0);//hago cero los minutos
HoraParaCita = Horacalendario.getTime();
cita_hora.setDate(HoraParaCita);
}

Categories