I'm trying to clean up my timer for my application but im having difficulties getting it to do what I want.
Would anyone know how to format H:M:S and remove the 00's?
Example the time may start like this: 12:34:56 (hh:mm:ss)
But once the time reaches lets say 00:34:56, remove the remaining 00's,
I'm a bit worried about performance which is why I'm here to find the more efficient way to format the time as this will be called a lot.
Would String.format("%02d:%02d", m, s).replaceAll("00:",""); be a wise choice?
Thanks.
Using a regular expression, you can delete only the first section '00:' using (It's only for deleting the hour part in a HH:MM::SS time format)
String.format("%02d:%02d", m, s).replaceAll("^00:","");
^ is a character to mark the beginning of a line
I assume you could be negligibly faster by just checking the first 2 characters:
if (timeString.charAt(0) == '0' && timeString.charAt(1) == '0')
timeString = timeString.substring(3);
I don't necessarily think that's great code, but 2 character checks would probably be faster than a larger string search. Not by much, though, so I doubt it's worth it.
(Made a fix based on the comment, changing == '1' to == '0' )
You have practically answered your own question. Just turn your thoughts into code.
What you need:
If the time is less than one hour, show only minutes and seconds; else, show hours, minutes and seconds.
Your code should read like the spoken version:
if (hours < 1) {
text = String.format("%02d:%02d", minutes, seconds);
} else {
text = String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
Simple, clean, fast, and easy for future programmers (including yourself, a year from now) to understand.
Related
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.
I have a Duration, like P3M (3 months). How can I get number of days it is from now?
All I have now is this:
Duration.parseWeekBasedPeriod("P3M")
I know the period parameter will never be shorter than 1 week, so parseWeekBasedPeriod() should be ok. But I'm reading JavaDoc, and I can't figure out how to get those days.
I understand, the problem is that months can has 31, 30, 29 and 28 days.
Using parseWeekBasedPeriod(...) is certainly wrong if you want to apply durations measured in months. This very special method handles week based years which can last either 364 or 371 days (52 or 53 weeks). So I suggest just to use the standard parsing method for calendar-based durations. The following code also strongly simplifies the evaluation of days between two dates (see last line).
Duration<CalendarUnit> duration = Duration.parseCalendarPeriod("P3M");
PlainDate today = PlainDate.nowInSystemTime();
PlainDate later = today.plus(duration);
long days = CalendarUnit.DAYS.between(today, later);
By the way, I have tested the method for weekbased durations once again. It will usually throw an exception if it tries to parse months. You didn't seem to have seen any exception so I assume that the fact that you use untyped constructs like "val" has shadowed the necessary type information in processing the duration string (and Time4J is a strongly typed library). Hence - if technically possible for you -, I strongly recommend to use type-safe code as shown in my solution.
Finaly figured it out:
val period = Duration.parseWeekBasedPeriod("P3M")
val start = PlainDate.nowInSystemTime()
val end = start.plus(period)
val days: Long = Duration.`in`(CalendarUnit.DAYS).between(start, end).getPartialAmount(CalendarUnit.DAYS)
How can I obtain the current TAI time in milliseconds in Linux using either Java or C++?
The reason I need this is to be able to accurately take timestamps over a long period of time (on the order of years) and still be able to compare them, without worrying about leap seconds. It is possible for multiple measurements to take place during a leap second and all measurements need to be unambiguous, monotonically increasing, and linearly increasing. This will be a dedicated Linux server. This is for a scientific project which needs precision of about .5 seconds.
I do not currently wish to invest in a GPS timekeeper and hope to use NTP to pool.ntp.org in order to keep the system clock on track.
I have looked into the following solutions:
Java 8 or the ThreeTen Project
The only way to obtain a TAIInstant is to use an Instant and then convert it which, according to the specs, "Conversion from an Instant will not be completely accurate near a leap second in accordance with UTC-SLS." That in and of itself is not a big deal (in fact, using UTC-SLS would also be acceptable). However, using now() in the Instant class also seems to just be a wrapper for System.currentTimeMillis(), which makes me think that during the leap second, the time will still be ambiguous and the project will not actually give me TAI time. The Java 8 specifications also state:
Implementations of the Java time-scale using the JSR-310 API are not
required to provide any clock that is sub-second accurate, or that
progresses monotonically or smoothly. Implementations are therefore
not required to actually perform the UTC-SLS slew or to otherwise be
aware of leap seconds.
Using a right/? timezone
This seems like it would work, however I am not sure if the implementation is smart enough to continue working during a leap second or if System.currentTimeMillis() would even give TAI time. In other words, would the underlying implementation still use UTC, thus giving an ambiguous time during the leap second which is then converted to TAI, or does using a right/ timezone actually work with TAI using System.currentTimeMillis() always (ie even during leap second)?
Using CLOCK_TAI
I tried using CLOCK_TAI in the Linux kernel but found it to be completely identical to CLOCK_REALTIME in my test:
Code:
#include <iostream>
#include <time.h>
long sec(int clock)
{
struct timespec gettime_now;
clock_gettime(clock, &gettime_now);
return gettime_now.tv_sec;
}
int main()
{
std::cout << sec(0) << std::endl; // CLOCK_REALTIME
std::cout << sec(1) << std::endl; // CLOCK_MONOTONIC
std::cout << sec(11) << std::endl; // CLOCK_TAI
return 0;
}
The output was simply:
1427744797
6896
1427744797
Using CLOCK_MONOTONIC
The problem with this is that the timestamps need to remain valid and comparable even if the computer restarts.
CLOCK_REALTIME and CLOCK_TAI return the same because the kernel parameter tai_offset is zero.
Check by using adjtimex(timex tmx) and read the value. I think that ntpd will set it if it is new enough (>4.2.6) and has a leap second file. It may also be able to get it from upstream servers but I haven't been able to verify. The call adjtimex() can set tai_offset manually when run as root. You will need a new-ish man page for adjtimex to see the parameters to set. My debian man page was too old but the command worked.
In addition to the correct accepted answer I would also mention the free Java library Time4J (min version v4.1) as possible solution because
I have written it to fill a gap in Java world (java.time cannot do all),
other answers given so far only talk about C++ (but you also asked for Java),
it works according to the same principles described by #user3427419.
It uses a monotonic clock based on System.nanoTime() but even allows custom implementations via the interface TickProvider. For the purpose of calibration, you can either use net.time4j.SystemClock.MONOTONIC, or you use an SNTP-clock named SntpConnector which just needs some simple configuration to connect to any NTP-time-server you want. And thanks to the built-in leap-second-table Time4J can even show you the announced leap second at the end of this month - in ISO-8601-notation or even as formatted local timestamp string in any timezone (using i18n-module).
A recalibration (in case of NTP - reconnect) of the clocks is possible meaning the clocks can be adapted to intermediate time adjustments (although I strongly recommend not to do it during your measurements or during a leap second). Although such a reconnect of an SNTP clock would normally cause the time stepping back in some cases Time4J tries to apply a smoothing algorithm (if activated in clock configuration) to ensure monotone behaviour. Detailed documentation is available online.
Example:
// Step 0: configure your clock
String ntpServer = "ptbtime1.ptb.de";
SntpConnector clock = new SntpConnector(ntpServer);
// Step 1: Timestamp start of the program and associate it with a counter
clock.connect();
// Step 2: Use the counter for sequential measurements at fixed intervals
Moment m = clock.currentTime();
System.out.println(m); // possible output = 2015-06-30T23:59:60,123456789Z
// Step 3: Timestamp new counter value(s) as necessary to keep your data adequately synced
clock.connect();
I doubt if any C++-based solution is more simple. More code demonstrations can also be studied on DZone.
Update (answer to question in comment):
A slightly simplified solution how to automatically download the given IETF-resource for new leap seconds and to translate it into a Time4J-specific format might look like this:
URL url = new URL("https://www.ietf.org/timezones/data/leap-seconds.list");
BufferedReader br =
new BufferedReader(
new InputStreamReader(url.openStream(), "US-ASCII"));
String line;
PlainDate expires = null;
Moment ntpEpoch = PlainTimestamp.of(1900, 1, 1, 0, 0).atUTC();
List<PlainDate> events = new ArrayList<PlainDate>();
try {
while ((line = br.readLine()) != null) {
if (line.startsWith("##")) {
long expraw = Long.parseLong(line.substring(2).trim());
expires = ntpEpoch.plus(
expraw, TimeUnit.SECONDS)
.toZonalTimestamp(ZonalOffset.UTC).toDate();
continue;
} else if (line.startsWith("#")) {
continue; // comment line
}
// this works for some foreseeable future
long epoch = Long.parseLong(line.substring(0, 10));
// this is no leap second
// but just the official introduction of modern UTC scale
if (epoch == 2272060800L) {
continue;
}
// -1 because we don't want to associate
// the leap second with the following day
PlainDate event =
ntpEpoch.plus(epoch - 1, TimeUnit.SECONDS)
.toZonalTimestamp(ZonalOffset.UTC).toDate();
events.add(event); // we don't assume any negative leap seconds here for simplicity
}
} finally {
br.close();
}
// now let's write the result into time4j-format
// use a location relative to class path of main program (see below)
String path = "C:/work/leapseconds.txt";
Writer writer = new FileWriter(new File(path));
String sep = System.getProperty("line.separator");
try {
for (PlainDate event : events) {
writer.write(event + ", +" + sep);
}
writer.write("#expires=" + expires + sep);
} finally {
writer.close();
}
System.out.println(
"Leap second file was successfully written from IETF-resource.");
// And finally, we can start the main program in a separate process
// with the system property "net.time4j.scale.leapseconds.path"
// set to our leapsecond file path (must be relative to class path)
Some notes:
I recommend to write this code as subprogram called by a simple batch program in order to avoid the main program being dependent on internet connectivity. This batch file would finally call the main program with the mentioned system property. If you set this property then the leap seconds will be read from the file specified there, and any eventually available tzdata-module would then stop to yield any concurrent leap second informations.
The reason I need this is to be able to accurately take timestamps
over a long period of time (on the order of years) and still be able
to compare them, without worrying about leap seconds. It is possible
for multiple measurements to take place during a leap second and all
measurements need to be unambiguous, monotonically increasing, and
linearly increasing.
Then your design is suboptimal. You cannot use time and then somehow meddle through leap seconds. This actually comes up often enough and people fall into the same trap of timestamping measurements using wall clock.
Timestamp start of the program and associate it with a counter
Use the counter for sequential measurements at fixed intervals
Timestamp new counter value(s) as necessary to keep your data adequately synced
If you avoid timestamping for the 1 second that leapsecond can occur (midnight!), you are home free because those can be adjusted later.
Now if you insist on using TAI without counter, all you need is a table with leap seconds that need to be accounted for. Then just use monotonic time. There is also libraries that can do this for you, but they may be out of date so you'll have to maintain them yourself,
http://skarnet.org/software/skalibs/libstddjb/tai.html
You have to implement a TAI clock based on C++ std::steady_clock or similar. To synchronize your TAI clock you could rely on GPS or NTP.
Option TAI from NTP: Your TAI implementation would need knowledge about leap seconds. Probably NTP protocol or referenced resources are the most reliable sources of current and future leap seconds.
Option TAI from GPS: GPS clock has a fixed offset to TAI, you do not have to mess with leap seconds
while(true){
try
{
if(Calendar.DATE == X){
startTask();
}
long delay = timeUntilNextCheck();
Thread.sleep(delay);
}
catch (Throwable t)
{
}
}
I have a program that requires a specific task to run on a specific day of the month.
After the task is run (Or if its not that day) the thread sleeps until tomorrow where it will check again.
However, I am getting a Dead code warning because part of the code is only ran on a specific day of the month.
I had a read up on what this warning is and I found that in some cases the compiler doesn't compile dead code. So my question is, will this always be compiled?
Can you tell us what X is?
if(Calendar.DATE == X)
If X is some constant representing day of month, this will not work because you are comparing Calendar.DATE constant with another constant X. Let me guess, your code is something like:
if(Calendar.DATE == 17) //run on 17th of every month
which translates into:
if(5 == 17) //I see dead code
Compiler gives you a hint that this condition will never be satisfied (and might not bother compiling the if statement body).
Instead you should test:
if(new GregorianCalendar().get(Calendar.DATE) == 17)
Or even better use Quartz. You would be surprised how many mistakes can you make with such a simple code (think: server timezone, daylight saving time...)
However, I am getting a Dead code warning because part of the code is
only ran on a specific day of the month.
No, you're getting a dead code warning becase startTask(); will never run. Calendar.DATE is an internal index constant of the Calendar class with the value 5. To get the current day of the month, use this code: Calendar.getInstance().get(Calendar.DAY_OF_MONTH)
I presume the dead code is the line
startTask();
If the compiler can detect that this is unreachable, it is probably because X (whatever it is) can never take the same value as Calendar.DATE, which is always 5. This is "field number for get and set indicating the day of the month." according to the Javadoc, not the current day of the month, which you could get for example using
Calendar.getInstance().get(Calendar.DATE)
You might want to look at something like java.util.Timer by the way.
Go for quartz and configure CronTrigger that would be far better
table structure:
sysdurationtimeday , sysdurationtimehour, sysdurationtimeminute
1, 12,10
3, 23,10
0, 0,10
i have these 3 fields from database, after getting these 3 values, what is the technique that i can use do cast to which Java Object? (maybe Calendar.class, TimeStamp.class) ?
and use it to compared with record is spent less than 1 day, more than 1 day + less than 3 days. etc?
As long as you're talking durations and not absolute times, this is pretty easy. Just express the time in a convenient unit, say seconds:
time_in_seconds = 86400*sysdurationtimeday +
3600*sysdurationtimehour +
60*sysdurationtimeminute
In Java the standard way to represent this is actually as a long value in milliseconds, ala System.currentTimeMillis().
All the standard Java classes are intended to handle absolute times and need to deal with daylight savings, leap years, and all that crap. At least with the data you gave us, you don't have the required info anyway: there's no way to tell if the day was a daylight savings day and therefore took 23 or 25 hours instead of 24.
I would prefer my own class, overriding the "essential" methods.
public class SysDuration implements Comparable {
int day;
int hour;
int min;
public SysDuration(int day,int hour,int min) {
}
public boolean equals(Object obj) {
}
public int hashCode() {
}
public int compareTo(Object obj) {
}
public boolean spendLess(SysDuration dur) {
}
}
Lots of good answers already.
A sugegstion, perhaps out of scope, if you use durations in java I would prefer to
just calculate and store this in one variable, typically a long in milliseconds
if this resolution is good enough. The splitting in 3 variables usually
make most of the code more complicated.
Calculations are easier and intergartion with libs such as jodatime and similar will be
even more simple.
If you literally want "more than 1 day", that is there's no rounding so d=1, h=23, m=59 gives you 1 day, not 2 days then you can just use sysdurationtimeday and completely ignore hours and minutes. (That assumes you don't have more than 24 in sysdurationtimehour).
Classes such as Calendar don't help, they are for manipulating actual dates, you already are working in durations.
Jodatime supports durations and then you get the operations needed for free.
If you are hesitant to add another dependency and learning curve, I would create a little custom class with a field storing the duration as a number in the desired precision. Then add some methods to do your comparisons or return a Calendar object or a Date aded and subtracted with your duration.
My guess is this will end up being cleaner than using the standard Java API's which always end up in complicated, clunky code when you start manipulating time.