I'm trying to convert a time string to milliseconds using the code below. Because I will use the time as countdown timer.
The problem is that the time is from database and it is on varchar type. I tried this code and it's not giving me the correct output.
String timeDuration = "10:00"; //for example only
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
Date time = sdf.parse(timeDuration);
long millis = time.getTime(); //The output must be 600000
I'm getting the wrong "millis" using this code.
I'm using Android Studio.
Duration is not the same as Date-Time
Duration is not the same as Date-Time and therefore you should not parse your string into a Date-Time type. The Date-Time type (e.g. java.util.Date) represents a point in time whereas a duration represents a length of time. You can understand it from the following examples:
I have been reading this book for 10 minutes.
I have been reading this book since 4:00 pm.
The first example has a duration whereas the second example has a Date-Time (implicitly today).
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Solution using java.time, the modern Date-Time API:
You can convert your string into ISO 8601 format for a Duration and then parse the resulting string into Duration which you can be converted into milliseconds.
Demo:
import java.time.Duration;
public class Main {
public static void main(String[] args) {
System.out.println(durationToMillis("10:00"));
}
static long durationToMillis(String strDuration) {
String[] arr = strDuration.split(":");
Duration duration = Duration.ZERO;
if (arr.length == 2) {
strDuration = "PT" + arr[0] + "M" + arr[1] + "S";
duration = Duration.parse(strDuration);
}
return duration.toMillis();
}
}
Output:
600000
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
Using classes SimpleDateFormat and Date is VERY STRONGLY discouraged. These classes are deprecated and very problematic. Use package java.time instead. for parser use DateTimeFormatter class. Also, in your case you may use class Duration. Alternatively, you can use Open Source MgntUtils library That has class TimeInterval and method TextUtils.parseStringToTimeInterval(java.lang.String valueStr). In this case your code could be very simple:
long milliseconds = TextUtils.parseStringToTimeInterval("10m").toMillis();
And this will give you 600000 as a result. The library is available as Maven artifact here, and on the github (including source code and Javadoc) here. Here is a link to Javadoc. The article about the library is here (See paragraph "Parsing String to Time Interval")
Disclaimer: The library is written by me
I think you can cut substring first. Then convert the substring to integer. Then multiple with 60000.
Integer.parseInt(timeDuration.substring(0, timeDuration.indexOf(":"))) * 60 * 1000
HTH
Cheers,
Hook
Related
How can parse LocalTime from String e.g. "10:38.0" in mm:ss.S format? I struggle to change the format.
public static LocalTime parseTime(String time) {
return localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern("mm:ss.S"));
}
Getting error
ISO of type java.time.format.Parsed
java.time.format.DateTimeParseException: Text '10:38.2' could not be parsed: Unable to obtain LocalTime from TemporalAccessor: {MinuteOfHour=10, MicroOfSecond=200000, MilliOfSecond=200, NanoOfSecond=200000000, SecondOfMinute=38},
java.time.Duration.parse()
As several others have correctly and wisely stated, your example string of 10:38.0 looks more like an amount of time, a duration. Not like a time of day a little more than 10 minutes after midnight. So LocalTime is not the correct class to use here. Use Duration. And parse the string into a Duration object.
The Duration class only supports parsing of ISO 8601 format, though. ISO 8601 format goes like PT10M38.0S for a period of time of 10 minutes 38.0 seconds (or for example PT10M38S or PT10M38.00000S, they work too). There are more ways to overcome this limitation. Arvind Kumar Avinash already shows one in his answer. My way would be to convert the string before parsing it:
public static Duration parseTime(String time) {
String iso = time.replaceFirst("^(\\d+):(\\d+(?:\\.\\d*)?)$", "PT$1M$2S");
return Duration.parse(iso);
}
Let’s try it out:
Duration dur = parseTime("10:38.0");
System.out.println(dur);
Output is:
PT10M38S
You see that the Duration prints back in ISO 8601 format too.
Depending on what further processing you want your duration for you are likely to find many useful methods in the documentation of that class; link below.
How time.replaceFirst("^(\\d+):(\\d+(?:\\.\\d*)?)$", "PT$1M$2S") works: I am using a regular expression to match your string:
^: Match the beginning of your string.
(\\d+): A capturing group matching one or more digits. Round brackets denote capturing groups. I will need this feature in the replacement below.
:: A colon (indeed).
(\\d+(?:\\.\\d*)?): A capturing group of digits optionally followed by a dot and zero or more further digits. (?: denotes the beginning of a non-capturing group that I use since I don’t need it separately in the replacement. ? after the non-capturing group denotes that it is optional (so 38 with no fraction would work for the seconds too).
$: match the end of your string
In my replacement string, PT$1M$2S, $1 and $2 denotes whatever was marched by the first and second capturing groups, which is what inserts 10 and 38.0 into the resulting string to obtain PT10M38.0S.
Nicer solution with an external library: Time4J
Using the non-trivial regular expression above to make your string and Duration.parse() meet isn’t the perfectly beautiful solution. Pattern-based parsing of a duration is supported by the Time4J library. So if you can tolerate an external dependency, consider using it. See the details in the answer by Meno Hochshield, the author of Time4J.
Links
Wikipedia article: ISO 8601
Documentation of Java regular expressions
Documentation of Duration
Answer by Meno Hochschild to this question
DateTimeFormatterBuilder#parseDefaulting
You can use DateTimeFormatterBuilder#parseDefaulting to default the hour of the day to zero.
However, in common sense, 10:38.0 represents a duration. You can obtain a Duration object by finding the duration between the parsed LocalTime and LocalTime.MIN.
Demo:
import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String str = "10:38.0";
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.appendPattern("mm:ss.S")
.toFormatter(Locale.ENGLISH);
LocalTime time = LocalTime.parse(str, dtf);
System.out.println(time);
Duration duration = Duration.between(LocalTime.MIN, time);
System.out.println(duration);
}
}
Output:
00:10:38
PT10M38S
ONLINE DEMO
Learn more about the modern Date-Time API* from Trail: Date Time.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.
The problem is, mm:ss isn't really a local time. It's more like a sprint race time. The error occurs because the translation demands a value for # of hours passed, and none are available ('no hours' is interpreted here as: They weren't in the pattern, not: "They were missing, therefore lets assume there are 0 hours").
One hacky way to fix that is to change your pattern to [HH:]mm:ss - the [] indicates optional input. Now the meaning changes: just 10:20 is interpreted as (via the optional input aspect) a shorthand for 00:10:20 and that is parsable into a LocalTime.
But, perhaps LocalTime isn't quite what you're looking for here; if indeed this describes the time passed to measure an event, you're looking for Duration, not LocalTime. Unfortunately, parsing 10:20 into a duration of 10 minutes and 20 seconds is non-trivial, the API just doesn't support it (only way to get there from a DTFormatter object is via LocalTime, crazily enough).
a) Parsing an expression like mm:ss.S without hours is not possible with the class DateTimeFormatter because such a parser tries to interprete it as point in time, not as duration. The missing hour is a fixed requirement for resolving the result to an instance of LocalTime.
b) You probably want a duration, not a LocalTime. Well, java.time has indeed a class named java.time.Duration but it can only format and parse a subset of ISO-8601-like expressions, for example: PT10M38.2S The pattern you want is not supported. Sorry.
c) Some people suggest a compromise by saying: Interprete LocalTime as kind of duration (not really true!) then parse the expression with a default hour value and finally evaluate the minute-of-hour and second-of-minute and so on. However, such a hacky workaround will only work if you never get time component values greater than 59 minutes or 59 seconds.
d) My external library Time4J supports pattern-based printing and parsing of durations. Example using the class net.time4j.Duration.Formatter:
#Test
public void example() throws ParseException {
TemporalAmount ta =
Duration.formatter(ClockUnit.class, "mm:ss.f")
.parse("10:38.2")
.toTemporalAmount();
System.out.println(LocalTime.of(5, 0).plus(ta)); // 05:10:38.200
}
The example also demonstrates a bridge to Java-8-classes like LocalTime via the conversion method toTemporalAmount(). If you use net.time4j.PlainTime instead then the bridge is of course not necessary.
Furthermore, one of many features of the time4j-duration-class is controlled normalizing when an expression contains a time component which does not fit into a standard clock scheme like 10 minutes and 68 seconds (= 11min + 8 sec).
#Test
public void example2() throws ParseException {
net.time4j.Duration dur =
Duration.formatter(ClockUnit.class, "mm:ss.f")
.parse("10:68.2")
.with(Duration.STD_CLOCK_PERIOD); // normalizing
System.out.println(PlainTime.of(5, 0).plus(dur)); // 05:11:08.200
}
I believe you want: .ofPattern("H:mm.s")
public static LocalTime parseTime(String time) {
return LocalTime.parse(time, DateTimeFormatter.ofPattern("H:mm.s"));
}
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
I need a date pattern to generate date string like this :
2019-12-18T17:11:24.2646051+03:30
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.?XXX").format(new Date())
What should I write instead of '?' ?
First point, get rid of SimpleDateFormat. That class is notoriously troublesome and long outdated. Also there is no way that SimpleDateFormat can produce 7 decimals on the seconds.
Second point, you probably don’t need to print 7 decimals on the seconds. Your format exemplified through 2019-12-18T17:11:24.2646051+03:30 is ISO 8601. In ISO 8601 the number of decimals on the seconds is free, so everyone should accept if you give them a string with 3 decimals, 9 decimals or no decimals at all (in the last case leave out the decimal point too).
java.time
So the easy solution is:
String desiredString
= OffsetDateTime.now(ZoneId.systemDefault()).toString();
System.out.println(desiredString);
Output when I ran the code just now:
2019-12-28T11:46:07.308+01:00
I am using java.time, the modern Java date and time API. And I am exploiting the fact that the toString methods of the date and time classes of java.time print ISO 8601 format. So we need no explicit formatter so far.
If you do want or need 7 decimals, do use a DateTimeFormatter:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T')
.appendPattern("HH:mm:ss.SSSSSSSXXX")
.toFormatter();
String desiredString = OffsetDateTime.now(ZoneId.systemDefault())
.format(formatter);
2019-12-28T11:48:52.3840000+01:00
It is possible to specify the formatter using a format pattern string only, but I prefer to reuse the built-in ISO_LOCAL_DATE formatter for the date part also when the code gets a few lines longer.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Try this:
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZ").format(new Date())
If you want more accuracy than milliseconds please look at this link
You can use S for the milliseconds. See this documentation page. You cannot use more than 3 though; there seems to be no support for microseconds or more fine-grained options.
import java.text.SimpleDateFormat;
import java.util.Date;
class Main {
public static void main(String[] args) {
String s = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").format(new Date());
System.out.println(s);
}
}
Example output:
2019-12-28T10:28:02.933Z
If you do need more granularity, using the Instant.now() function might be an option, although the following code will only produce 6 digits (microseconds, no nanoseconds):
import java.time.Instant;
class Main {
public static void main(String[] args) {
System.out.println(Instant.now().toString());
}
}
Example output:
2019-12-28T10:31:56.477551Z
See this answer for more info.
I am not interested in what the current UTC time is in milliseconds, nor do I need to mess with timezones. My original date is already stored as a UTC timestamp.
I have a date stored in a database in UTC time, "2012-06-14 05:01:25".
I am not interested in the datetime, but just the date portion of the it. So, after retrieving the date in Java, and excluding the hours, minutes, and seconds - I am left with "2012-06-14".
How can I convert this into UTC milliseconds?
EDIT: I'd missed the "ignoring the time of day" part. It's now present, but near the end...
The simplest approach is probably to use SimpleDateFormat, having set the time zone appropriately:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = format.parse(text);
long millis = date.getTime();
(Setting the time zone is the important bit here, as otherwise it will interpret the value to be in the local time zone.)
Alternatively, if you're doing anything less trivial than this, use Joda Time which is a much better date/time API. In particular, SimpleDateFormat isn't thread-safe whereas DateTimeFormatter is:
// This can be reused freely across threads after construction.
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
.withLocale(Locale.US)
.withZoneUTC();
// Option 1
DateTime datetime = formatter.parseDateTime(text);
long millis = dateTime.getMillis();
// Option 2, more direct, but harder to diagnose errors
long millis = formatter.parseMillis(text);
Now so far, we've parsed the whole whole caboodle. The easiest way of ignoring the date part is just to round it off - after all, Java doesn't observe leap seconds, so we can just truncate it:
long millisPerDay = 24L * 60L * 60L * 1000L; // Or use TimeUnit
long dayMillis = (millis / millisPerDay) * millisPerDay;
That will "round towards 1970" so if you have a date before 1970 it will round to the end of the day - but I suspect that's unlikely to be a problem.
With the Joda Time version you could just use this instead:
DateTime dateTime = formatter.parseDateTime(text);
long millis = dateTime.toLocalDate().getLocalMillis();
I would personally not go with the idea of just taking a substring. Even though you're not actually interested in preserving the hour/minute/second, I think it's appropriate to parse what you've been given and then throw away information. Aside from anything else, it makes your code fail appropriately with bad data, e.g.
"2012-06-100"
or
"2012-06-14 25:01:25"
indicate problems in whatever's supplying you data, and it's good to spot that rather than to continue blindly just because the first 10 characters are okay.
UPDATE: See the modern solution using java.time classes in the correct Answer by Ole V.V..
Simpler
The answer by Jon Skeet is correct. And he makes a good point about including, rather than truncating, the time-of-day info while parsing.
However, his code could be simplified. Especially so because Joda-Time gained an important new method in the latest versions: withTimeAtStartOfDay. This method supplants all the "midnight"-related classes and methods which are now deprecated.
Specifying a Locale is a good habit, as shown in his code. But in this particular case a Locale is not necessary.
His answer correctly suggests the Joda-Time library, far superior to using java.util.Date, .Calendar, and java.text.SimpleTextFormat. Those classes are notoriously troublesome, and should be avoided. Instead use either Joda-Time or the new java.time package built into Java 8 (inspired by Joda-Time, defined by JSR 310).
First Moment Of The Day
You cannot ignore time-of-day if what you want is a count of milliseconds-since-epoch. I suspect what you want is to change the time to first moment of the day. In UTC, this always means the time 00:00:00.000. But note that in local time zones, the first moment may be a different time because of Daylight Saving Time and possibly other anomalies.
ISO 8601
Your string is nearly in standard ISO 8601 format, but we need to swap a T for the SPACE in the middle. Then we can feed the resulting string directly to Joda-Time as Joda-Time has built-in formatters used by default for standard strings.
Example Code
The following example code assumes the intent of your question is to parse a string as a date-time value in UTC time zone, adjust the time to the first moment of the day, and then convert to number of milliseconds since Unix epoch (beginning of 1970 in UTC).
String inputRaw = "2012-06-14 05:01:25";
String input = inputRaw.replace( " ", "T" ); // Replace SPACE with a 'T'.
DateTime dateTime = new DateTime( input, DateTimeZone.UTC ); // Parse, assuming UTC.
DateTime dateTimeTopOfTheDay = dateTime.withTimeAtStartOfDay(); // Adjust to first moment of the day.
long millisecondsSinceUnixEpoch = dateTimeTopOfTheDay.getMillis(); // Convert to millis. Use a 'long', not an 'int'.
java.time and JDBC 4.2
I am providing the modern answer. These days (and for the last several years) you should use java.time, the modern Java date and time API, for your date and time work. And since JDBC 4.2 you can directly retrieve java.time objects from your database (and also store them into it). A modern JPA implementation (Hibernate at least since Hibernate 5) will be happy to do the same. So forget about SimpleDateFormat, Date and other old classes used in most of the old answers. The mentioned ones are poorly designed, and java.time is so much nicer to work with.
Retrieve proper date-time objects from your database
I also recommend that you don’t retrieve your UTC time as a string from the database. If the datatype in SQL is timestamp with time zone (recommended for UTC times), retrieve an OffsetDateTime. For example:
PreparedStatement pStmt = yourDatabaseConnection
.prepareStatement("select utc_time from your_table where id = 7;");
ResultSet rs = pStmt.executeQuery();
if (rs.next()) {
OffsetDateTime utcDateTime = rs.getObject("utc_time", OffsetDateTime.class);
long millisecondsSinceEpoch = utcDateTime.truncatedTo(ChronoUnit.DAYS)
.toInstant()
.toEpochMilli();
System.out.println("Milliseconds since the epoch: " + millisecondsSinceEpoch);
}
If the type in SQL is dateTime or timestamp without time zone, we probably need to retrieve a LocalDateTime instead (details depending on your JDBC driver and the time zone of your database session). It goes in the same manner. For converting your LocalDateTime to OffsetDateTime, see the conversion below.
If you need to convert from a string
If you cannot avoid getting your UTC time as a string as in the question, parse it into a LocalDateTime and convert from there. For example:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
String utcTimeString = "2012-06-14 05:01:25";
long millisecondsSinceEpoch = LocalDateTime.parse(utcTimeString, formatter)
.atOffset(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
System.out.println("Milliseconds since the epoch: " + millisecondsSinceEpoch);
Output:
Milliseconds since the epoch: 1339650085000
Link
Oracle tutorial: Date Time explaining how to use java.time.
Use the Date object in combination with SimpleDateFormat.
There is a method named getTime() in Date which will return the milliseconds for you.
Example that solves your problem :
Date truc = new SimpleDateFormat( "y-m-d").parse( "2010-06-14");
System.out.println(truc.getTime());
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd"); //or whatever format you have
Date t = ft.parse('2014-03-20');
String result = String.format("%tQ", t);
System.out.printf("%tQ", t);
There are two methods here:
you put the result milliseconds into a variable result
printing it straight off.
I use a simple and straight forward approach:
Date date = new Date(utcDateInString);
long utcDateInMilliSeconds = date.getTime();
Ok, so I've pretty much tried everything. I bet it's something really simple but I can't seem to get a hold of it.
The server sends me the time, which is epoch. However when I put this into a date object it seems to automatically pick up the time zone and it adds +3 to the server time. So if the gmt time is 00.00, it says its 03.00.
I also need to add a timezone of my own. Let's say the epoch time is 00.00 again, it should read 10.00 after I add the timezone.
any help would be much appreciated. Thank you
"It seems to add" - I suspect you're using Date.toString() which does indeed use the local time zone. The Date object itself is effectively in UTC though. Use DateFormat to perform the conversion to a string instead, and you can specify which time zone to use. You may also need to use Calendar - it depends what you're trying to do.
(Alternatively, use Joda Time in the first place, which is a better API. It may be a little bulky for your Android project though. I wouldn't be surprised if there were a "Joda Time lite" project around somewhere for precisely this sort of thing...)
EDIT: Quick sample, although it's not entirely clear what you need...
long millis = getMillisFromServer();
Date date = new Date(millis);
DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
format.setTimeZone(customTimeZone);
String formatted = format.format(date);
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API:
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Main {
public static void main(String[] args) {
long millis = 1316391494L;
Instant instant = Instant.ofEpochMilli(millis);
System.out.println(instant);
// The same instant at a specific timezone
ZonedDateTime zdt = instant.atZone(ZoneId.of("Australia/Brisbane"));
System.out.println(zdt);
}
}
Output:
1970-01-16T05:39:51.494Z
1970-01-16T15:39:51.494+10:00[Australia/Brisbane]
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
What went wrong with your code?
A java.util.Date object simply represents an instant on the timeline — a wrapper around the number of milliseconds since the UNIX epoch (January 1, 1970, 00:00:00 GMT). Since it does not hold any timezone information, its toString function applies the JVM's timezone to return a String in the format, EEE MMM dd HH:mm:ss zzz yyyy, derived from this milliseconds value. To get the String representation of the java.util.Date object in a different format and timezone, you need to use SimpleDateFormat with the desired format and the applicable timezone e.g.
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
long millis = 1316391494L;
Date date = new Date(millis);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX[zzzz]", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
String strDateUtc = sdf.format(date);
System.out.println(strDateUtc);
sdf.setTimeZone(TimeZone.getTimeZone("Australia/Brisbane"));
String strDateBrisbane = sdf.format(date);
System.out.println(strDateBrisbane);
}
}
Output:
1970-01-16T05:39:51.494Z[Coordinated Universal Time]
1970-01-16T15:39:51.494+10:00[Australian Eastern Standard Time]
ONLINE DEMO
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
There are two parts to this: first compute the start date. Then compute the elapsed time. For the second part, I found some good advice here: Calculate elapsed time in Java / Groovy
However, the first part is a problem for me. Here's how it works:
The user indicates that the time span extends from an hour value (00 to 23). I do not have a starting date/time object of any sort - just an integer hour. From that I need to figure the start date (and then the elapsed time).
If the start hour is greater than the now hour, it was the prior day. In order to get an actual start date from that however, I need to potentially consider month and year boundaries as well as things like leap years and daylight savings time changes. Surely someone has solved a problem like this already. (I believe it can be quite complex.) Is there a proven solution that will let me compute how much time (in seconds) has actually elapsed from the given hour of the day (00 to 24) to now? (The start time will always be assumed to be on the hour.)
Firstly, I'd suggest using the Joda Time API. It's the best date/time API available for Java, in my opinion.
Next you need to work out exactly what to do in various corner cases. In particular, suppose the user enters "1" and you're near a daylight saving transition. It's possible that 1am happened twice (if the the time went 1:58, 1:59, 1:00, 1:01 because of a transition back away from DST) or that it didn't happen at all (if the time went 12:58, 12:59, 2:00 because of a transition forward into DST). You need to work out what to do in each of those situations - and bear in mind that this means knowing the time zone too.
Once you've worked that out, it may not be too hard. With Joda Time you can use withHourOfDay method to get from one time to another having set one component of the time - and likewise there are simple APIs for adding or subtracting a day, if you need to. You can then work out the time between two DateTime values very easily - again, Joda Time provides everything you need.
Here's an example which doesn't try to do anything with DST transitions, but it's a good starting point:
import org.joda.time.*;
public class Test
{
public static void main(String[] args)
{
// Defaults to current time and time zone
DateTime now = new DateTime();
int hour = Integer.parseInt(args[0]);
DateTime then = now
.withHourOfDay(hour)
.withMinuteOfHour(0)
.withSecondOfMinute(0);
if (then.isAfter(now))
{
then = then.minusDays(1);
}
Period period = new Period(then, now, PeriodType.seconds());
System.out.println("Difference in seconds: " + period.getSeconds());
}
}
java.time
The accepted answer has provided a solution using Joda-Time API which was probably the best 3rd party date-time API in Java at that time. In Mar 2014, the modern date-time API was released as part of the Java 8 standard library. Notice the following message on the Home Page of Joda-Time:
Joda-Time is the de facto standard date and time library for Java
prior to Java SE 8. Users are now asked to migrate to java.time
(JSR-310).
Solution using java.time, the modern date-time API:
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
class Main {
public static void main(String[] args) {
// A sample hour indicated by the user
int hour = 16;
// Replace the ZoneId with the applicable one
ZoneId zone = ZoneId.of("America/New_York");
ZonedDateTime then = LocalDate.now(zone)
.atStartOfDay(zone)
.withHour(hour);
ZonedDateTime now = ZonedDateTime.now(zone);
System.out.println(now);
if (then.isAfter(now))
then = then.minusDays(1).withHour(hour);
long seconds = ChronoUnit.SECONDS.between(then, now);
System.out.println(seconds);
}
}
Output from a sample run:
2023-01-08T09:31:28.040819-05:00[America/New_York]
63088
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
Let's say startHour is given by the user (assume, that's in the 0-23 range). Now you may start with this:
import java.util.Calendar;
import static java.util.Calendar.*;
...
Calendar start = Calendar.getInstance();
if (start.get(HOUR_OF_DAY) < startHour) {
start.add(DAY_OF_MONTH, -1);
Use Joda-Time classes. You can do something like this:
DateTime now = new DateTime();
DateTime start;
int startHour = getStartHour();// user's choice
int nowHour = now.getHourOfDay();
if (nowHour < startHour) {
start = now.minusHours(24 - startHour + nowHour);
} else {
start = now.minusHours(nowHour - startHour);
}