I have a family tree app which allows you to build nodes. I am stuck on a problem which requires editing a members date of birth. The date of birth is just a string in the following format dd-mm-yyyy. My problem arises when checking if the date of birth is valid (i.e. any parent cannot be younger than a child). So if the node has both parents and children and user selects to edit it's date of birth, the function must continuously check to see whether an age between the two dates has been added. The problem I am having is getting this continual check to occur using the methods I have defined. I'm hoping someone understands the isue and can help. Note checkDOb also sets the dob too. its bad naming on my part.
here is the code:
private void dateCheck(FamilyMember node) {
String dob = enterDateOfBirth();
if (node.hasChildren()) {
node.setDob(dob);
checkDob(node, node.getOldestChild(), 0);
}
FamilyMember parent = null;
if (node.hasMother() && node.hasFather()) {
if (node.getMother().getAge() > node.getFather().getAge()) {
parent = node.getFather();
} else {
parent = node.getMother();
}
checkDob(parent, node, 1);
} else {
//single parent
if (node.hasMother()) {
parent = node.getMother();
checkDob(parent, node, 1);
}
if (node.hasFather()) {
parent = node.getFather();
checkDob(parent, node, 1);
}
}
}
private void checkDob(FamilyMember parent, FamilyMember child, int member) {
out.println(parent.getYear());
out.println(child.getYear());
while (parent.getYear() > child.getYear()) {
out.println("Invalid Date - The Oldest Child of " + parent.getFullName()
+ "(" + child.getFullName() + ")\n cannot older than his/her parents. Try Again.");
out.println();
if (member == 0) {
parent.setDob(enterDateOfBirth());
}
if (member == 1) {
child.setDob(enterDateOfBirth());
}
}
}
private String enterDateOfBirth() {
out.print("Enter Year Of Birth (0 - 2011): ");
String y = in.nextLine();
out.print("Enter Month Of Birth (1-12): ");
String m = in.nextLine();
if (m.trim().equals("")) {
m = "0";
}
if (m.length() == 1) {
m = "0" + m;
}
m += "-";
out.print("Enter Date of Birth (1-31): ");
String d = in.nextLine();
if (d.trim().equals("")) {
d = "0";
}
if (d.length() == 1) {
d = "0" + d;
}
d += "-";
String dob = d + m + y;
while (!DateValidator.isValid(dob)) {
out.println("Invalid date. Try again.");
dob = enterDateOfBirth();
}
return (dob);
}
Thanks in advance.
Well the standard way to parse Dates is using SimpleDateFormat. But I don't think that will help in your case you so I won't go there.
But you should use Date objects (or, as others will say: use JodaTime's DateTime object) when you are talking about Dates, it makes things easier.
make Date the type of dob
and exchange this code:
String dob = d + m + y;
for this:
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, Integer.parseInt(y));
cal.set(Calendar.MONTH, Integer.parseInt(m)-1);
cal.set(Calendar.DATE, Integer.parseInt(d));
Date dob = cal.getTime();
(you should lose all the if (m.length() == 1) stuff, because Strings with leading zeroes will be parsed as octal values)
Now you can just validate that parentBirthDate.compareTo(childBirthDate)>0
Also, to be more precise you should truncate all the fields that are smaller than day:
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Also: don't use System.out.println(), use a logging framework like Log4J or SLF4J.
Oh, BTW, validating that the child is younger than the parent won't be enough. You will probably have to validate that the difference is 12 years or more :-)
It can be smt like
//return true if one of arguments is true!
private boolean bornBefore(FamilyMember first, FamilyMember second){
if(first == null || second == null){
return true;
}
return /*your magic date comaparator*/;
}
private boolean validDate(FamilyMember node, MagicDate date) {
return bornBefore(node, node.getOldestChild())
&& bornBefore(node.getFather(), node)
&& bornBefore(node.getMother(), node);
}
}
You should decouple your validation and data entry. To start of you have your tree model with the current values and a new string that you want to enter in to change a value.
bool checkDOB(String date, FamilyMember node) {
//return true if date is more recent than any parents of node and
//older than any children of node
}
Now that you have a stand alone validation method, you should call it anytime you are about to add a new node or edit one. Do not change any values in the actual data model until it has been validated This means your tree will always be in a valid state and an edit is just a local operation.
Related
I have a scenario where i want to select the nearest date or the past date from a bunch of data which i am getting from a ajax call.
For example: If I am getting three rows with three different date like 12/12/2018, 12/3/2018 and 1/1/2018 then I want to return 1/1/2018.
And also if the array doesn't contains past date then it should return the nearest date to the current date.
Please suggest me something to achieve this.
Maybe a function like this would be useful:
function getNearestDate(dates, date) {
if (!dates || dates.length == 0) {
return null;
}
if (!date) {
date = new Date();
}
var result = dates[0];
var dt = Math.abs(new Date(dates[0])-date);
var minimum = dt;
for (var i=1;i<dates.length;i++) {
dt = Math.abs(new Date(dates[i])-date);
if (dt < minimum) {
result = dates[i];
minimum = dt;
}
}
return result;
}
In my website, I can select a date range and list all the transactions within the date range. My test case is to verify whether listed transactions dates are within the selected date range .
This is my code. I get all the transaction dates into a LinkedList. Comp_Dates method will compare the actual date is within the ‘From’ and ‘To’ dates.
The problem is this code will always return True. I have changed the FromDate and ToDate to test the false scenario, But still code will return True.
Can you please help? What’s the problem in this code?
//Set From Date
driver.findElement(By.id("ctl00_ContentPlaceHolderMain_container_container_Block_172_tabPanelMyAccounts_dtDateFrom_txtDate")).sendKeys(Keys.chord(Keys.CONTROL, "a"),"01/03/2016");
//Set To date
driver.findElement(By.id("ctl00_ContentPlaceHolderMain_container_container_Block_172_tabPanelMyAccounts_dtDateTo_txtDate")).sendKeys(Keys.chord(Keys.CONTROL, "a"),"30/04/2016");
driver.findElement(By.id("ctl00_ContentPlaceHolderMain_container_container_Block_172_tabPanelMyAccounts_btnList")).click();
List<WebElement> Date =
driver.findElements(By.xpath(".//* [#id='ctl00_ContentPlaceHolderMain_container_container_Block_172_tabPanelMyAccounts_stxOutstandingTransactions_gvOSTransactions']/tbody/tr[*]/td[1]"));
List<String> Dates = new LinkedList<String>();
for(int i=0;i<Date.size();i++)
{
Dates.add(Date.get(i).getText());
System.out.println(Dates);
}
boolean result = comp_Dates(Dates);
if (result=true)
{
System.out.println(result + ", Address are within the range");
}
else
{
System.out.println(result + ", Addresses are not within the range. Test Case Failed");
}
}
private static boolean comp_Dates(List<String> Dates) {
try
{
SimpleDateFormat fmt = new SimpleDateFormat("dd/MM/yyyy");
//Date date = fmt.parse("2013-05-06");
String FromDate= "01/05/2016";
String ToDate= "30/06/2016";
java.util.Date Fdate =fmt.parse(FromDate);
java.util.Date Tdate =fmt.parse(ToDate);
for(String e : Dates)
{
java.util.Date ActualDate = fmt.parse(e);
if (ActualDate.compareTo(Fdate)>=0 & ActualDate.compareTo(Tdate)<=0 );
{
return true;
}
}
}
catch (Exception ex ){
System.out.println(ex);
}
return false;
}
}
Transactions dates in Linked list is [18/04/2016, 14/04/2016, 13/04/2016]
I have specified dates as below in the code.
String FromDate= "01/05/2016";
String ToDate= "30/06/2016";
When compare these dates, code should return false as dates doesn’t fall on within From and To dates. But it returns True. What am I doing wrong here?
Thanks
When you are returning true, it will exit the function whenever it founds a date in the range. Thus it would not check for all dates in the list.
If you want to check for all dates, proper comp_Dates method could be:
//Set From Date
driver.findElement(By.id("ctl00_ContentPlaceHolderMain_container_container_Block_172_tabPanelMyAccounts_dtDateFrom_txtDate")).sendKeys(Keys.chord(Keys.CONTROL, "a"), "01/03/2016");
//Set To date
driver.findElement(By.id("ctl00_ContentPlaceHolderMain_container_container_Block_172_tabPanelMyAccounts_dtDateTo_txtDate")).sendKeys(Keys.chord(Keys.CONTROL, "a"), "30/04/2016");
driver.findElement(By.id("ctl00_ContentPlaceHolderMain_container_container_Block_172_tabPanelMyAccounts_btnList")).click();
List<WebElement> Date =
driver.findElements(By.xpath(".//* [#id='ctl00_ContentPlaceHolderMain_container_container_Block_172_tabPanelMyAccounts_stxOutstandingTransactions_gvOSTransactions']/tbody/tr[*]/td[1]"));
for (int i = 0; i < Date.size(); i++) {
String date = Date.get(i).getText();
boolean result = comp_Dates(date);
if (result) {
System.out.println(result + ", Address are within the range");
} else {
System.out.println(result + ", Addresses are not within the range. Test Case Failed");
}
}
private static boolean comp_Dates(String date) {
try {
SimpleDateFormat fmt = new SimpleDateFormat("dd/MM/yyyy");
String FromDate = "01/05/2016";
String ToDate = "30/06/2016";
java.util.Date Fdate = fmt.parse(FromDate);
java.util.Date Tdate = fmt.parse(ToDate);
java.util.Date ActualDate = fmt.parse(date);
if (ActualDate.compareTo(Fdate) >= 0 && ActualDate.compareTo(Tdate) <= 0) {
return true;
}
} catch (Exception ex) {
System.out.println(ex);
}
return false;
}
N.B: There are many typos in your code. You should fix these.
I have trouble finding elements, here is my code:
public static void main(String[] args) {
BufferedReader br = getFileReader("reader.csv");
ArrayList<Monitoring> col = getCollection(br);
//sort the collection on 'beginTime'
for (Monitoring x : col)
System.out.println(x.toString());
BeginTimeComparator beginTime = new BeginTimeComparator();
Collections.sort(col,beginTime);
System.out.println("Begin time:");
for (Monitoring x : col)
System.out.println(x.toString());
This is the part I have trouble with, I don't know how to search en get back the object with endTime 2015-03-10.
BTW this is one line of cvs data:
UnitId;BeginTime;EndTime;Type;Min;Max;Sum
14100072;2015-03-10 07:12:20;2015-03-10 7:13:20;Gps/GpsAccuracyGyroBias;0;0;0
//find the amount of elements that were sent on 'endTime' = 2015-03-10 (just the date)
EndTimeComparator endTime = new EndTimeComparator();
String findThis = "2015-03-10";
Collections.sort(col, endTime);
for(Monitoring x : col){
if(x.getEndTime().equals(findThis)){
System.out.println("Here is 'endTime= 2015-03-10' :");
System.out.println(x.toString());
}
}
I have tried this but both didn't work:
int index = Collections.binarySearch(col, findThis.toString(), null);
System.out.println("Here is 'endTime= 2015-03-10' :");
System.out.println(index);
Guessing that getEndTime() returns a LocalDateTime you can't compare a string with a type of LocalDateTime. You could try to parse the LocalDateTime to LocalDate and fill the 'findThis' variabel with a type of LocalDate.
Because code says more than a 1000 words:
EndTimeComparator endTime = new EndTimeComparator();
Collections.sort(col, endTime);
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate findThis = LocalDate.parse("2015-03-10", dtf);
System.out.println("Here is 'endTime= 2015-03-10' :");
for (Monitoring x : col) {
if (x.getEndTime().toLocalDate().equals(findThis)) {
System.out.println(x.toString());
}
}
You need to provide Comparator for that null or Monitoring should implement comparable (both of them should compare items by time field that you need).
Collections.binarySearch(col, findThis.toString(), null);
According to the example data you provided
UnitId;BeginTime;EndTime;Type;Min;Max;Sum
14100072;2015-03-10 07:12:20;2015-03-10 7:13:20;Gps/GpsAccuracyGyroBias;0;0;0
endTime is "2015-03-10 7:13:20", not "2015-03-10", so using equals will not work. Instead, you could try using startsWith:
String findThis = "2015-03-10";
for (Monitoring x : col) {
if (x.getEndTime().startsWith(findThis)) {
System.out.println("Here is 'endTime= 2015-03-10': ");
System.out.println(x.toString());
}
}
Or even better: Instead of storing the begin and end times as strings, convert them to Date objects or similar when you read the objects from CSV.
I have a window that contains a HH:mm time TextField in it, in 24 hours format
I need to validate if the user entered any non valid hour, like 28:00, 99:00, 24:01.
What's the best and simpler way to do that ?
some code below of what is currently doing that job wrong and giving errors in date parsed.
Today I get an random hour and an user hit 99:99 in that text field.
This code is not mine, but I gotta fix it.
I am stuck with it, tried to validate as a String is useless, and I cannot find a nice way to make it a Date without having to put year, month, etc... too.
Please forget about the return -1 instead of throwing an exception this is old code and this cannot be changed.
to help understand :
Statics.hF2 = SimpleDateFormat (HH:mm)
this.cmpHora.getText() = Is the field with the value
Statics.df_ddmmyy = Another date format
Statics.m2ms = converts minutes to milliseconds
//CODE
public long getDataEmLong ()
{
try
{
Calendar hour= Calendar.getInstance();
new GregorianCalendar().
hour.setTime( Statics.hF2.parse( this.cmpHora.getText() ) );
return Statics.df_ddmmyy.parse( this.cmpData.getText() ).getTime() + Statics.m2ms( hour.get( Calendar.HOUR_OF_DAY ) * 60 ) + Statics.m2ms( hour.get( Calendar.MINUTE ) );
} catch ( Exception e )
{
e.printStackTrace();
return -1;
}
}
Cheers !
Regular expressions to the rescue:
public boolean validTime24(String time) {
return time.matches("^([01]\d|2[0-3]):[0-5]\d$")
}
This will validate the format of the string. Then you can parse out the time from there.
Insert this in your class, and perform the validateTime method from inside your junk code.
public boolean validateTime(String timeString) {
if (timeString.length() != 5) return false;
if (!timeString.substring(2, 3).equals(":")) return false;
int hour = validateNumber(timeString.substring(0, 2));
int minute = validateNumber(timeString.substring(3));
if (hour < 0 || hour >= 24) return false;
if (minute < 0 || minute >= 60) return false;
return true;
}
public int validateNumber(String numberString) {
try {
int number = Integer.valueOf(numberString);
return number;
} catch (NumberFormatException e) {
return -1;
}
}
You can use JFormattedTextField with proper Date or Time Format set. The field will return you proper values.
Since Java 8 you can use DateTimeFormatter:
public boolean validate(String time) {
try {
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
timeFormatter.parse(time);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
First, sorry this is so long. I probably don't need all the code, but wanted to be sure.
Second, my actual question is, am I doing something wrong, or is this a bug in the joda-time library?
I'm trying to use joda-time (1.6.1) to calculate, then format time durations.
I'm currently using Period, which may be the wrong choice. Please let me know if it is.
However, even if it is the wrong choice, I'm pretty sure this shouldn't happening.
I'm initialising a Period using milliseconds (by multiplying a duration in seconds by 1000). I'm using the Period so I can then format it and print it:
long durationLong = durationSec * 1000;
Period duration = new Period(durationLong);
PeriodFormatter daysHoursMinutes = new PeriodFormatterBuilder()
.appendHours()
.appendSeparator(":")
.appendMinutes()
.appendSeparator(":")
.appendSeconds()
.toFormatter();
String formattedString = daysHoursMinutes.print(callDuration.normalizedStandard());
I get the Exception below, and have looked through the source to confirm the loop.
Caused by: java.lang.StackOverflowError
at java.util.Hashtable.get(Hashtable.java:274)
at java.util.Properties.getProperty(Properties.java:177)
at java.lang.System.getProperty(System.java:440)
at java.lang.System.getProperty(System.java:412)
at org.joda.time.DateTimeZone.getDefault(DateTimeZone.java:132)
at org.joda.time.DateTimeZone.forID(DateTimeZone.java:190)
at org.joda.time.DateTimeZone.getDefault(DateTimeZone.java:132)
at org.joda.time.DateTimeZone.forID(DateTimeZone.java:190)
...snip (all the same)...
at org.joda.time.DateTimeZone.getDefault(DateTimeZone.java:132)
at org.joda.time.DateTimeZone.forID(DateTimeZone.java:190)
at org.joda.time.DateTimeZone.getDefault(DateTimeZone.java:132)
at org.joda.time.DateTimeZone.forID(Dat
Period(long):
public Period(long duration) {
super(duration, null, null);
}
super(long, PeriodType, Chronology):
protected BasePeriod(long duration, PeriodType type, Chronology chrono) {
super();
type = checkPeriodType(type);
chrono = DateTimeUtils.getChronology(chrono);
iType = type;
iValues = chrono.get(this, duration);
}
DateTimeUtils.getChronology(chrono):
public static final Chronology getChronology(Chronology chrono) {
if (chrono == null) {
return ISOChronology.getInstance();
}
return chrono;
}
ISOChronology.getInstance():
public static ISOChronology getInstance() {
return getInstance(DateTimeZone.getDefault());
}
DateTimeZone.getDefault():
public static DateTimeZone getDefault() {
DateTimeZone zone = cDefault;
if (zone == null) {
synchronized(DateTimeZone.class) {
zone = cDefault;
if (zone == null) {
DateTimeZone temp = null;
try {
try {
temp = forID(System.getProperty("user.timezone"));
} catch (RuntimeException ex) {
// ignored
}
if (temp == null) {
temp = forTimeZone(TimeZone.getDefault());
}
} catch (IllegalArgumentException ex) {
// ignored
}
if (temp == null) {
temp = UTC;
}
cDefault = zone = temp;
}
}
}
return zone;
}
forID(String) calls getDefault(), which creates the loop:
public static DateTimeZone forID(String id) {
if (id == null) {
return getDefault();
}
if (id.equals("UTC")) {
return DateTimeZone.UTC;
}
DateTimeZone zone = cProvider.getZone(id);
if (zone != null) {
return zone;
}
if (id.startsWith("+") || id.startsWith("-")) {
int offset = parseOffset(id);
if (offset == 0L) {
return DateTimeZone.UTC;
} else {
id = printOffset(offset);
return fixedOffsetZone(id, offset);
}
}
throw new IllegalArgumentException("The datetime zone id is not recognised: " + id);
}
As the looping part is only in the joda code, I would say that's a bug.
It has been corrected on the trunk and will be available in V2.0.
Resources :
The reported bug
Looks like it's a bug in that it assumes the user.timezone property will have been set.
That's the way to get round it in this case - just make sure that user.timezone is set appropriately. It's a shame that you have to though.
Joda Time uses "null means default" in a lot of places - unfortunately, in my view. I prefer "null is invalid" usually. In Noda Time (a port of Joda Time to .NET) we're trying to get rid of a lot of this kind of thing - as well as preventing the default time zone from being as prevalent in the first place.