Getting a nicely formatted timestamp without lots of overhead? - java

In my app I have a textView which contains real-time messages from my app, as things happen, messages get printed to this text box. Each message is time-stamped with HH:MM:SS.
Up to now, I had also been chasing what seemed to be a memory leak, but as it turns out, it's just my time-stamp formatting method (see below), It apparently produces thousands of objects that later get gc'd. For 1-10 messages per second, I was seeing 500k-2MB of garbage collected every second by the GC while this method was in place. After removing it, no more garbage problem (its back to a nice interval of about 30 seconds, and only a few k of junk typically)
So I'm looking for a new, more lightweight method for producing a HH:MM:SS timestamp string :)
Old code:
/**
* Returns a string containing the current time stamp.
* #return - a string.
*/
public static String currentTimeStamp() {
String ret = "";
Date d = new Date();
SimpleDateFormat timeStampFormatter = new SimpleDateFormat("hh:mm:ss");
ret = timeStampFormatter.format(d);
return ret;
}

Make your SimpleDateFormat be static final, rather than creating one each time.

Related

Formatting hour in java 20h 10m 5000s to 20h 10m 10s

I am trying to create a small program that we give a wrong time for example: 20h 10m 5000s and that transforms it giving me back 20h 10m 50s. But I am unable to show you the code to see if you can help me, thank you very much :)
import java.util.Date;
import java.text.SimpleDateFormat;
import javax.swing.JOptionPane;
public class EejercicioBasico3 {
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat dateForm = new SimpleDateFormat("HH:mm:ss");
String UserDate = dateForm.format(JOptionPane.showInputDialog("Escriba una hora en formato hh-mm-ss"));
System.out.println(date);
System.out.println(UserDate);
}
}
Removing excess digits
I tend to understand from your question and comments that you are assuming that the user may type too many digits by mistake. I am further assuming that each number may be in the interval from 0 or 00 to 59, and any digits that make the number greater than 59 or wider than two digits are to be removed. It’s probably not perfect, but may get you started.
String inputTimeString = "20h 10m 5000s";
String outputTimeString
= inputTimeString.replaceAll("([6-9]|[0-5]\\d)\\d+", "$1");
System.out.println(outputTimeString);
Output is:
20h 10m 50s
The regular expression first matches either a digit in the range 6 – 9 or two digits starting with 0 through 5 to ensure that we got at most 59. This or these digits are captured as a group using round brackets around the group in the regexp. After the group any number of excess digits is matched. In the replacement string I use $1 to denote that the digits should be replaced with just what was matched in capturing group no. 1 (the only capturing group in this case).
Try another example:
String inputTimeString = "60h 010m 777s";
6h 01m 7s
Reservation: If this is a basic exercise from school, your teacher may have another solution in mind, but you can judge that better. If you haven’t learnt regular expressions, you probably should not hand in a solution that uses them. Maybe you were expected to iterate through the input string and add characters that are OK to a string buffer where you collect your output.
Converting excess seconds to minutes and hours
If instead you want excess seconds — over 59 seconds — converted to minutes and hours, use the Duration class:
String isoTimeString = "PT" + inputTimeString.replaceAll(" ", "");
Duration dur = Duration.parse(isoTimeString);
String outputTimeString = String.format("%dh %dm %ds",
dur.toHours(), dur.toMinutesPart(), dur.toSecondsPart());
System.out.println(outputTimeString);
21h 33m 20s
Duration.parse() requires ISO 8601 format. This is obtained from your format by prefixing PT (think period of time) and removing the spaces. The String.format() call reproduces your format.
Always avoid Date and SimpleDateFormat
The classes you were trying to use, SimpleDateFormat and Date, are poorly designed and long outdated and were never meant for a job like this. I recommmend that you never use them and always use java.time, the modern Java date and time API, for your time work. The Duration class is part of java.time.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
In my opinion the bottom line would be...just don't accept an entry of: 20h 10m 5000s. Although the data is supplied through an Input Dialog window it can still be validated to contain the desired format (which is clearly shown as an example within the dialog) and if it isn't, inform the User to enter it again. Your code really shouldn't need to accommodate every typo and automatically correct it. It should however determine that there is a typo and inform the User to correct it or discard the input data altogether. Your application sets the rules, not the User (unfortunately however this may not be the case for everything). This may seem blunt but, you can't make everything idiot proof because tomorrow, there will just be a better idiot. Make the idiot do it right.
Determine your application's Time Entry rules:
Time is to be in three specific units: Hours, Minutes, and
Seconds.
Time is in 24 hour format meaning there is no such thing as AM or PM.
Each time unit (Hour, Minute, or Second) is to be comprised of two integer digits (ex: 15-32-05).
A separator character must be applied to separate each time
unit. The allowable character in this case is the Hyphen (-) or Minus character.
String userTime = "";
while (userTime.isEmpty()) {
userTime = JOptionPane.showInputDialog(null, "<html>Enter a time in "
+ "<font color=red><b>hh-mm-ss</b></font> format:<br><br></html>");
if (userTime == null) {
JOptionPane.showMessageDialog(null, "Time entry Canceled!", "Entry Canceled",
JOptionPane.WARNING_MESSAGE);
return;
}
if (!userTime.matches(
"^([0-1][0-9][-]|[2][0-3][-])([0-5][0-9][-])([0-5][0-9])$")) {
JOptionPane.showMessageDialog(null, "Invalid Time format supplied!",
"Invalid Entry", JOptionPane.WARNING_MESSAGE);
userTime = "";
}
}
String[] timeUnits = userTime.split("-");
String time = new StringBuilder("").append(timeUnits[0]).append("h ")
.append(timeUnits[1]).append("m ").append(timeUnits[2])
.append("s").toString();
JOptionPane.showMessageDialog(null, "<html>User supplied the time of:<br><br>"
+ "<center><font color=blue><b>" + time + "</b></font></center></html>",
"Invalid Entry", JOptionPane.INFORMATION_MESSAGE);
Obviously you don't need to do this sort of thing in a loop but you get the idea I'm sure.

How to make a string in java understand current local time

Here's my code, I'm trying to my 'calculateButton' variable to understand the current local time so that it can use it in the math equation. Thanks for taking a look. By the current time, I mean the time currently being shown on my computer or the time zone I'm in.
private void bednowButtonActionPerformed(java.awt.event.ActionEvent evt) {
//variable declaration
String calculateButton, outputTime;
double outputtimeConstant;
calculateButton= "";
//math
outputtimeConstant = +104;
outputTime = calculateButton + outputtimeConstant;
//output
bednowOutput.setText(outputTime) ;
}
To get the current date and time just use static method now() of LocalDateTime or ZonedDateTime classes (you mentioned time zone here, so I'm specifying this implementation as well). For some math with these objects afterwards just use their instance methods, like plusDays, plusHours... and so on. I'd recommend reading documentation of these classes, they are quite useful.
If you want to have a String representation simply call toString() method.

While statement with Calendar.after doesn't start

So I've spend a fair amount of Googling and looking through things, but I just can't get it to work. It won't consider the statement as true, even though the request.getEnd() is a couple days after the current startCal.getTime. Any ideas?
while(startCal.after(request.getEnd()))
{
/* Calculate new beginning */
startCal.add(Calendar.DATE, 1);
newBegin = startCal.getTime();
System.out.println(newBegin);
/* Calculate new ending */
endCal.add(Calendar.DATE, 1);
newEnd = endCal.getTime();
/* Setting new dates and series ID */
localRequest.setBegin(newBegin);
localRequest.setEnd(newEnd);
localRequest.setSeriesID(seriesID);
/* Sending new reservation to database */
//reserve(localRequest);
System.out.println("RESERVATION DONE");
}
even though the request.getEnd() is a couple days after the current
startCal.getTime.
Then it makes sense that it wouldn't start! This line of code:
while(startCal.after(request.getEnd()))
Says "While start cal is after request end." You are describing your data as the opposite!
Calendar#after returns whether this Calendar represents a time after the time represented by the specified Object.
So, your code is currently checking whether startCal is after request.getEnd(), which from your description is the opposite of what you're trying to do.
It would make more sense to use Calendar#before here to get the desired result.

SimpleDateFormat timezone bug on Android

I've been trying to isolate a bug in my application. I succeeded in producing the following "riddle":
SimpleDateFormat f1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
SimpleDateFormat f2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date d = f1.parse("2012-01-01T00:00:00+0700");
String s1 = f1.format(d); // 2011-12-31T18:00:00+0700
String s2 = f2.format(d); // 2011-12-31T18:00:00+0100
I get the values in comments when I run this code on Android API 7 (yes, really). This behavior depends on particular Java implementation.
My questions are:
Why s1 does not equal s2?
And more importantly, why s1 is incorrect? While s2 points to a proper point in time, s1 does not. There seems to be a bug in Android's SimpleDateFormat implementation.
ANSWER TO QUESTION 1: See the answer by BalusC:
[After using SimpleDateFormat#parse] any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.
ANSWER TO QUESTION 2: See the answer by wrygiel (myself).
This is due to a bug in Android 2.1 (API 7).
This is mentioned in javadoc of DateFormat#parse():
Parse a date/time string according to the given parse position. For example, a time text "07/10/96 4:5 PM, PDT" will be parsed into a Date that is equivalent to Date(837039900000L).
By default, parsing is lenient: If the input is not in the form used by this object's format method but can still be parsed as a date, then the parse succeeds. Clients may insist on strict adherence to the format by calling setLenient(false).
This parsing operation uses the calendar to produce a Date. As a result, the calendar's date-time fields and the TimeZone value may have been overwritten, depending on subclass implementations. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.
Note the last paragraph. It unfortunately doesn't explain when exactly this will occur. To fix your particular problem you need to explicitly set the desired timezone before the formatting operation.
As to the mutability of SimpleDateFormat itself, this is known for years. You should never create and assign an instance of it as a static or class variable, but always as a method (threadlocal) variable.
This is due to a bug in Android 2.1 (API 7). It seems that Android programmers missed some undocumented Java behavior (which is classified as an unfixable bug itself!) in their implementation of Android 2.1.
Your question intrigued me so I went ahead and compiled your code. The result? As expected...
2011-12-31T18:00:00+0100
2011-12-31T18:00:00+0100
The two values are the same, are you using some concurrency? Maybe the variable gets changed on another thread right before the f2.format(d).
I tried to compare s1 and s2 by running the same program. they come equal to me.

SimpleDateFormat localized month names

I have searched throughout the site but I think I have a slightly different issue and could really do with some help before I either have heart failure or burn the computer.
I dynamically generate a list of month names (in the form June 2011, July 2011) and obviously I want this to be locale sensitive: hence I use the simple date format object as follows:
//the actual locale name is dependent on UI selection
Locale localeObject=new Locale("pl");
// intended to return full month name - in local language.
DateFormat dtFormat = new SimpleDateFormat("MMMM yyyy",localeObject);
//this bit just sets up a calendar (used for other bits but here to illustrate the issue
String systemTimeZoneName = "GMT";
TimeZone systemTimeZone=TimeZone.getTimeZone(systemTimeZoneName);
Calendar mCal = new GregorianCalendar(systemTimeZone); //"gmt" time
mCal.getTime(); //current date and time
but if I do this:
String value=dtFormat.format(mCal.getTime());
this "should" return the localized version of the month name. In polish the word "September" is "Wrzesień" -- note the accent on the n. However all I get back is "Wrzesie?"
What am I doing wrong?
Thanks to all - I accept now that it's a presentation issue - but how can I "read" the result from dtFormat safely - I added some comments below ref using getBytes etc. - this worked in other situations, I just can't seem to get access to the string result without messing it up
-- FINAL Edit; for anyone that comes accross this issue
The answer was on BalusC's blog : http://balusc.blogspot.com/2009/05/unicode-how-to-get-characters-right.html#DevelopmentEnvironment
Basically the DTformat object was returning UTF-8 and was being automatically transformed back to the system default character set when I read it into a string
so this code worked for me
new String(dtFormat.format(mCal.getTime()).getBytes("UTF-8"),"ISO-8859-1");
thank you very much for the assistance
Your problem has nothing to do with SimpleDateFormat - you're just doing the wrong thing with the result.
You haven't told us what you're doing with the string afterwards - how you're displaying it in the UI - but that's the problem. You can see that it's fetching a localized string; it's only the display of the accented character which is causing a problem. You would see exactly the same thing if you had a string constant in there containing the same accented character.
I suggest you check all the encodings used throughout your app if it's a web app, or check the font you're displaying the string in if it's a console or Swing app.
If you examine the string in the debugger I'm sure you'll see it's got exactly the right characters - it's just how they're getting to the user which is the problem.
In my tests, dtFormat.format(mCal.getTime()) returns
październik 2011
new SimpleDateFormat(0,0,localeObject).format(mCal.getTime()) returns:
poniedziałek, 3 październik 2011 14:26:53 EDT

Categories