StackOverflowError using joda-time new Period(long) - java

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.

Related

How to return sum of LocalTime in Java

It wont get the minutes. i need to return minutes.
How to return sum of minutes while iterating over Localtime in Java?
public String userLunchHoursSum(String username) {
List<WorkHour> workHours = workHourRepository.findWorkHoursByUsername(username);
System.out.println(Arrays.toString(workHours.toArray()));
long diff = 0;
LocalTime lunchTime;
long minutes = 0;
LocalTime plusMinutes = null;
for (WorkHour workHour : workHours) {
lunchTime = workHour.getLunch_time().toLocalTime(); //00:30:00
plusMinutes = lunchTime.plusMinutes(lunchTime.getMinute());
}
if(workHours.size()!= 0) {
return Long.toString(plusMinutes.getMinute());
}
return "00:00";
}
getLunch_time returns java.sql.Time.
As mentioned, you should be storing duration instead of localtime. If this is something you have no control over, consider migrating the database or creating a intermediate parsing function. Example code that I have not run, because I don't know what is in WorkHour.
// leave the string formatting to other functions
public long userLunchHoursSum(String username) {
List<WorkHour> workHours = workHourRepository.findWorkHoursByUsername(username);
Duration totalDuration = Duration.ZERO;
for (WorkHour workHour : workHours) {
// save your time in the appropriate format beforehand
// do not use local time to store duration.
Duration lunchTime = Duration.between(LocalTime.MIDNIGHT, workHour.getLunch_time().toLocalTime()); //00:30:00
totalDuration = totalDuration.plus(lunchTime);
}
return totalDuration.toMinutes();
}

Apache Flink: Wierd FlatMap behaviour

I'm ingesting a stream of data into Flink. For each 'instance' of this data, I have a timestamp. I can detect if the machine I'm getting the data from is 'producing' or 'not producing', this is done via a custom flat map function that's located in it's own static class.
I want to calculate how long the machine has been producing / not producing.
My current approach is collecting the production and non production timestamps in two plain lists. For each 'instance' of the data, I calculate the current production/non-production duration by subtracting the latest timestamp from the earliest timestamp. This is giving me incorrect results, though. When the production state changes from producing to non producing, I clear the timestamp list for producing and vice versa, so that if the production starts again, the duration starts from zero.
I've looked into the two lists I collect the respective timestamps in and I see things I don't understand. My assumption is that, as long as the machine 'produces', the first timestamp in the production timestamp list stays the same, while new timestamps are added to the list per new instance of data.
Apparantly, this assumption is wrong since I get seemingly random timestamps in the lists. They are still correctly ordered, though.
Here's my code for the flatmap function:
public static class ImaginePaperDataConverterRich extends RichFlatMapFunction<ImaginePaperData, String> {
private static final long serialVersionUID = 4736981447434827392L;
private transient ValueState<ProductionState> stateOfProduction;
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SS");
DateFormat timeDiffFormat = new SimpleDateFormat("dd HH:mm:ss.SS");
String timeDiffString = "00 00:00:00.000";
List<String> productionTimestamps = new ArrayList<>();
List<String> nonProductionTimestamps = new ArrayList<>();
public String calcProductionTime(List<String> timestamps) {
if (!timestamps.isEmpty()) {
try {
Date firstDate = dateFormat.parse(timestamps.get(0));
Date lastDate = dateFormat.parse(timestamps.get(timestamps.size()-1));
long timeDiff = lastDate.getTime() - firstDate.getTime();
if (timeDiff < 0) {
System.out.println("Something weird happened. Maybe EOF.");
return timeDiffString;
}
timeDiffString = String.format("%02d %02d:%02d:%02d.%02d",
TimeUnit.MILLISECONDS.toDays(timeDiff),
TimeUnit.MILLISECONDS.toHours(timeDiff) % TimeUnit.HOURS.toHours(1),
TimeUnit.MILLISECONDS.toMinutes(timeDiff) % TimeUnit.HOURS.toMinutes(1),
TimeUnit.MILLISECONDS.toSeconds(timeDiff) % TimeUnit.MINUTES.toSeconds(1),
TimeUnit.MILLISECONDS.toMillis(timeDiff) % TimeUnit.SECONDS.toMillis(1));
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("State duration: " + timeDiffString);
}
return timeDiffString;
}
#Override
public void open(Configuration config) {
ValueStateDescriptor<ProductionState> descriptor = new ValueStateDescriptor<>(
"stateOfProduction",
TypeInformation.of(new TypeHint<ProductionState>() {}),
ProductionState.NOT_PRODUCING);
stateOfProduction = getRuntimeContext().getState(descriptor);
}
#Override
public void flatMap(ImaginePaperData ImaginePaperData, Collector<String> output) throws Exception {
List<String> warnings = new ArrayList<>();
JSONObject jObject = new JSONObject();
String productionTime = "0";
String nonProductionTime = "0";
// Data analysis
if (stateOfProduction == null || stateOfProduction.value() == ProductionState.NOT_PRODUCING && ImaginePaperData.actSpeedCl > 60.0) {
stateOfProduction.update(ProductionState.PRODUCING);
} else if (stateOfProduction.value() == ProductionState.PRODUCING && ImaginePaperData.actSpeedCl < 60.0) {
stateOfProduction.update(ProductionState.NOT_PRODUCING);
}
if(stateOfProduction.value() == ProductionState.PRODUCING) {
if (!nonProductionTimestamps.isEmpty()) {
System.out.println("Production has started again, non production timestamps cleared");
nonProductionTimestamps.clear();
}
productionTimestamps.add(ImaginePaperData.timestamp);
System.out.println(productionTimestamps);
productionTime = calcProductionTime(productionTimestamps);
} else {
if(!productionTimestamps.isEmpty()) {
System.out.println("Production has stopped, production timestamps cleared");
productionTimestamps.clear();
}
nonProductionTimestamps.add(ImaginePaperData.timestamp);
warnings.add("Production has stopped.");
System.out.println(nonProductionTimestamps);
//System.out.println("Production stopped");
nonProductionTime = calcProductionTime(nonProductionTimestamps);
}
// The rest is just JSON stuff
Do I maybe have to hold these two timestamp lists in a ListState?
EDIT: Because another user asked, here is the data I'm getting.
{'szenario': 'machine01', 'timestamp': '31.10.2018 09:18:39.432069', 'data': {1: 100.0, 2: 100.0, 101: 94.0, 102: 120.0, 103: 65.0}}
The behaviour I expect is that my flink program collects the timestamps in the two lists productionTimestamps and nonProductionTimestamps. Then I want my calcProductionTime method to subtract the last timestamp in the list from the first timestamp, to get the duration between when I first detected the machine is "producing" / "not-producing" and the time it stopped "producing" / "not-producing".
I found out that the reason for the 'seemingly random' timestamps is Apache Flink's parallel execution. When the parallelism is set to > 1, the order of events isn't guaranteed anymore.
My quick fix was to set the parallelism of my program to 1, this guarantees the order of events, as far as I know.

Java: How to check whether given time lies between two times?

I want to check whether target time lies between two given times without considering date using Java8 time. Let say if starting time is "21:30" , ending time is "06:30" and target time is "03:00", so program should return true.
#Test
public void posteNuit()
{
DateTimeFormatter format = DateTimeFormatter.ofPattern("HH:mm");
String s = "21:30";
String e = "06:30";
String t = "03:00";
LocalTime startTime = LocalTime.parse(s, format);
LocalTime endTime = LocalTime.parse(e, format);
LocalTime targetTime = LocalTime.parse(t, format);
if ( targetTime.isBefore(endTime) && targetTime.isAfter(startTime) ) {
System.out.println("Yes! night shift.");
} else {
System.out.println("Not! night shift.");
}
}
You've used LocalTime which doesn't store date information, only time.
Then you are trying to check if target time is after start time (03:00 after 21:30). This statement is false.
Your start time should be before end time.
If you need to handle night shift try following:
if (startTime.isAfter(endTime)) {
if (targetTime.isBefore(endTime) || targetTime.isAfter(startTime)) {
System.out.println("Yes! night shift.");
} else {
System.out.println("Not! night shift.");
}
} else {
if (targetTime.isBefore(endTime) && targetTime.isAfter(startTime)) {
System.out.println("Yes! without night shift.");
} else {
System.out.println("Not! without night shift.");
}
}
in your scenario it seems that if startTime > endTime no matter what's targetTime you'll return true.
So update the if statement:
if (( startTime.isAfter(endTime) && targetTime.isBefore(startTime)
&& targetTime.isAfter(endTime) )
|| ( startTime.isBefore(endTime) && targetTime.isBefore(endTime)
&& targetTime.isAfter(startTime) )) {

Java time manipulation - subtracting 2 strings

I am pulling 2 time values (as strings) from an XML file using xpath, these values (for example) are as follows:
00:07
08:00
00:07 is equal to 7 minutes
08:00 means 8am, with no date associated or needed (that is handled elsewhere)
Each of these values is subject to change in each XML file that i read. What i am attempting to do is as follows:
I need to subtract or add (depending on the situation) the 7mins from the 8am and give me a hh:mm time (eg: 07:53 or 08:07) in a string that i can eventually output to CSV
Next i need to produce 2 additional strings, 1 min before and 1 min after (eg: 07:52 and 07:54 OR 08:06 and 08:08) which also need to be output to CSV
I have tried everything and i can think of in relation to the time interpretation and manipulation to get the minutes subtracted/added to the time and then +/- 1 min from there, but being a complete novice i am totally stuck despite reading and testing as much as i could find. Spent the last 2 days working with Joda Time for the first time but i must be missing something fundamental as i cannot get the desired result with this either.
The question is - how can i achieve this?
Some sample code that gets me reading from the XML and printing the time
FileInputStream file = null;
try {
file = new FileInputStream(new File("Output/XmlConfig.xml"));
} catch (FileNotFoundException ex) {
Logger.getLogger(KATT.class.getName()).log(Level.SEVERE, null, ex);
}
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = builderFactory.newDocumentBuilder();
} catch (ParserConfigurationException ex) {
Logger.getLogger(KATT.class.getName()).log(Level.SEVERE, null, ex);
}
Document xmlDocument = null;
try {
xmlDocument = builder.parse(file);
} catch (SAXException ex) {
Logger.getLogger(KATT.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(KATT.class.getName()).log(Level.SEVERE, null, ex);
}
XPath xPath = XPathFactory.newInstance().newXPath();
//get In Early rule from XML
String exceptionInEarlyXML = "Root/Response/WSAExceptionRule/#InEarly";
NodeList nodeListInEarly = null;
try {
nodeListInEarly = (NodeList) xPath.compile(exceptionInEarlyXML).evaluate(xmlDocument, XPathConstants.NODESET);
} catch (XPathExpressionException ex) {
Logger.getLogger(KATT.class.getName()).log(Level.SEVERE, null, ex);
}
String exceptionInEarly = (nodeListInEarly.item(1).getFirstChild().getNodeValue());
String InEarly = exceptionInEarly;
SimpleDateFormat format = new SimpleDateFormat("hh:mm");
Date d2 = null;
try {
d2 = format.parse(InEarly);
} catch (ParseException ex) {
Logger.getLogger(KATT.class.getName()).log(Level.SEVERE, null, ex);
}
DateTime dt2 = new DateTime(d2);
System.out.println(dt2);
This give me an output of 1970-01-01T00:07:00.000+10:00
I have tried so many permutations of code that i am at the point of deleting and starting again from scratch as it is un-compilable, and i am not experienced enough yet to be able to solve this issue.
Once you have the Date object for the parsed time, use getTime() to get the time in milliseconds and save it into a long variables. Then parse the offset time format and use a NumberFormat to get the number of minutes to offset. Add or subtract as needed. Take the result and create a new Date(millis) then apply your format to it.
Here is a working example:
String sTime = "08:00";
String sOffset ="00:07";
SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm");
Date dtTime = null;
try {
dtTime = dateFormat.parse(sTime);
} catch (ParseException e) {
// handle exception
return;
}
String[] offsetHrsMins = null;
NumberFormat numberFormat = NumberFormat.getNumberInstance();
long offsetMillis = 0;
try {
offsetHrsMins = sOffset.split(":");
long offsetHrs = (Long) numberFormat.parse(offsetHrsMins[0]);
long offsetMins = (Long) numberFormat.parse(offsetHrsMins[1]);
offsetMillis = 1000 * 60 * ((offsetHrs * 60) + offsetMins);
} catch (ParseException e) {
// handle exception
return;
}
long lTime = dtTime.getTime();
System.out.println("Adding minutes: " + dateFormat.format(new Date(lTime + offsetMillis)));
System.out.println("Subtracting minutes: " + dateFormat.format(new Date(lTime - offsetMillis)));
output:
Adding minutes: 08:07
Subtracting minutes: 07:53
First, you need to use SimpleDateFormat to parse the Date String to a Java.util.Date Object.
Second, After getting the Date Object, you can easily add/substract some time, and get another Date Object.
Last, you can use another SimpleDateFormat object to format the Date Object you got in second step to String.
SimpleDateFormat is very useful in Processing Date Strings. You can refer to the Javadoc in JDK or search some examples by Google.
Try passing the strings into a method aswel as what you are subrtacting by
Then converting them to ints
Then have an if statment that if the subtraction amount is greater that the minets int
then it subtracts 1 from the hours int and sets the new minets int to 60 subtract the subtraction int
Then convert them back to Strings
Here is the code exept for turing it back into a string
public class Main {
static String hours="8";
static String minets="7";
static String minus="17";
public static void main(String[] args) {
Main m = new Main();
m.timechange(hours,minets,minus);
}
void timechange(String hour, String minuet, String subtract){
int h = Integer.parseInt(hour);
int m = Integer.parseInt(minuet);
int s = Integer.parseInt(subtract);
if(s>m){
h-=1;
m=60-s;
}
else{
m-=s;
}
if ((m>9)&&(h>9)) {
System.out.println(h+":"+m);
} else {if ((m<10)&&(h<10)) {
System.out.println("0"+h+":0"+m);
}else {if ((m<10)&&(h>9)) {
System.out.println(h+":0"+m);
}else {if ((m>9)&&(h<10)) {
System.out.println("0"+h+":"+m);
}
}
}
}
}}
I wasnt sure if you wanted the back to String.
Hopeful that answers your question
The same can be done for when the minets reach over 60 if that ever happens.
Here a genuine Joda-Time answer because OP wants Joda-Time (and I also consider that library as superior to java.util.Date, java.text.SimpleDateFormat etc.):
Joda-Time has the big advantage of having several different temporal types. The right type for handling plain wall times is LocalTime. It also defines a method to add minutes.
Your task:
I need to subtract or add (depending on the situation) the 7mins from the 8am and give me a hh:mm time (eg: 07:53 or 08:07) in a string that i can eventually output to CSV
Next i need to produce 2 additional strings, 1 min before and 1 min after (eg: 07:52 and 07:54 OR 08:06 and 08:08) which also need to be output to CSV
The solution (only for part one, the other part is very similar):
LocalTime time = new LocalTime(8, 0); // corresponds to 08:00
LocalTime laterBy8Minutes = time.plusMinutes(7);
LocalTime earlierBy8Minutes = time.minusMinutes(7);
String sLaterBy8Minutes = laterBy8Minutes.toString("HH:mm"); // 08:07
String sEarlierBy8Minutes = earlierBy8Minutes.toString("HH:mm"); // 07:53
One additional note: If you start with another type like java.util.Date and wish to convert it to LocalTime then you can use the constructor
new LocalTime(jdkDate, DateTimeZone.forID("Europe/Moscow")) // example
or for default timezone:
new LocalTime(jdkDate)

How to validate Hour and Minutes that came from a Swing textBox?

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;
}
}

Categories

Resources