Suppose I have a System.currentTimeMillis() value as a long number.
How do I modify it to match the instant when last minute started? I.e., zero out seconds and milliseconds.
I would prefer to not use magic constants. Using java.time is fine.
I agree with the answers recommending java.time, but it can be done yet simpler as in those answers:
long lastWholeMinute = Instant.now().truncatedTo(ChronoUnit.MINUTES).toEpochMilli();
This just gave 1517940060000. Of course, if it makes sense for you to keep the Instant object, by all means do that rather than converting to a naked primitive long.
If your long value was one you had stored rather than the time now, it’s quite similar:
long someEpochMilliValue = 1_517_941_234_567L;
long lastWholeMinute = Instant.ofEpochMilli(someEpochMilliValue)
.truncatedTo(ChronoUnit.MINUTES)
.toEpochMilli();
Using java.time is probably the easiest way. You could use withNano and withSecond, like
java.time.ZonedDateTime zdt = java.time.ZonedDateTime.now().withNano(0).withSecond(0);
long millis = zdt.toInstant().toEpochMilli();
Since the value is in milliseconds, if we assume an idealized day (no leap seconds, etc.), then given l you could do it by simply removing the value of l % 60000L from it. I realize that's a magic constant, but it's truly a constant, there are always going to be 60,000 milliseconds in a minute. I'd give it symbolic name:
private static long SIXTY_SECONDS_IN_MS = 60000L;
and not worry about it. Then it's:
long l = /*...your number...*/;
l = l - (l % SIXTY_SECONDS_IN_MS);
Why this works: The Epoch value is from midnight Jan 1st 1970, and so at 0L, 60000L, 120000L, etc., the seconds and milliseconds of an idealized day based on that value are 0. So we use the remainder operator (%) to isolate the part of the value that would remain if we divided by 60000L and remove it. Thus the resulting value, again assuming idealized days, has 0 for seconds and milliseconds. It also works across timezones if we assume all timezones are going to be at whole-minute offsets to UTC. I've only ever heard of timezones that were multiples of hours or half-hours offset from UTC ("GMT plus five hours", "GMT plus 5.5 hours"), never (say) "GMT plus five hours seven minutes and 20 seconds". (And indeed, the standard notation for timezome offsets, +0600 or similar, only includes hours and minutes, not fractional minutes.)
Live Example:
import java.time.*;
public class Example
{
private static long SIXTY_SECONDS_IN_MS = 60000L;
public static void main (String[] args) throws java.lang.Exception
{
long l = System.currentTimeMillis();
l = l - (l % SIXTY_SECONDS_IN_MS);
System.out.println("l = " + l);
// Checking the result
LocalDateTime dt = Instant.ofEpochMilli(l).atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println(dt);
System.out.println(dt.getSecond()); // 0
System.out.println(dt.getNano()); // 0
}
}
Still, though, if that constant violates the terms of the question such that you think I shouldn't have answered, let me know and I'll delete the answer. :-)
Related
How to convert epoch like 1413225446.92000 to ZonedDateTime in java?
The code given expects long value hence this will throw NumberFormatException for the value given above.
ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(dateInMillis)), ZoneId.of(TIME_ZONE_PST));
java.time can directly parse your string
Edit: If your millisecond value is always non-negative, the following DateTimeFormatter can parse it.
private static final String TIME_ZONE_PST = "America/Los_Angeles";
private static final DateTimeFormatter epochFormatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER)
.optionalStart()
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
.optionalEnd()
.toFormatter()
.withZone(ZoneId.of(TIME_ZONE_PST));
Now parsing into a ZonedDateTime is just one method call:
ZonedDateTime zdt = ZonedDateTime.parse(dateInMillis, epochFormatter);
System.out.println(zdt);
Output is:
2014-10-13T11:37:26.920-07:00[America/Los_Angeles]
It will not work correctly with a negative value: the fraction would still be parsed as positive, which I am assuming would be incorrect. To be sure to be notified in case of a negative value I have specified in the formatter that the number cannot be signed.
A more general solution: use BigDecimal
If you need a more general solution, for example including negative numbers, I think it’s best to let BigDecinmal parse the number and do the math.
BigDecimal bd = new BigDecimal(dateInMillis);
BigDecimal[] wholeAndFractional = bd.divideAndRemainder(BigDecimal.ONE);
long seconds = wholeAndFractional[0].longValueExact();
int nanos = wholeAndFractional[1].movePointRight(9).intValue();
ZonedDateTime zdt = Instant.ofEpochSecond(seconds, nanos)
.atZone(ZoneId.of(TIME_ZONE_PST));
Output is the same as before. Only now we can also handle negative numbers according to expectations:
String dateInMillis = "-1.5";
1969-12-31T15:59:58.500-08:00[America/Los_Angeles]
Even scientific notation is accepted:
String dateInMillis = "1.41322544692E9";
2014-10-13T11:37:26.920-07:00[America/Los_Angeles]
If finer precision than nanoseconds is possible in the string, consider how you want to truncate or round, and instruct BigDecimal accordingly, there are a number of options.
Original answer
Basil Bourque’s answer is a good one. Taking out the nanoseconds from the fractional part into an integer for nanoseconds may entail a pitfall or two. I suggest:
String dateInMillis = "1413225446.92000";
String[] secondsAndFraction = dateInMillis.split("\\.");
int nanos = 0;
if (secondsAndFraction.length > 1) { // there’s a fractional part
// extend fractional part to 9 digits to obtain nanoseconds
String nanosecondsString
= (secondsAndFraction[1] + "000000000").substring(0, 9);
nanos = Integer.parseInt(nanosecondsString);
// if the double number was negative, the nanos must be too
if (dateInMillis.startsWith("-")) {
nanos = -nanos;
}
}
ZonedDateTime zdt = Instant
.ofEpochSecond(Long.parseLong(secondsAndFraction[0]), nanos)
.atZone(ZoneId.of("Asia/Manila"));
System.out.println(zdt);
This prints
2014-10-14T02:37:26.920+08:00[Asia/Manila]
We don’t need 64 bits for the nanoseconds, so I am just using an int.
Assumption: I have assumed that your string contains a floating-point number and that it may be signed, for example -1.50 would mean one and a half seconds before the epoch. If one day your epoch time comes in scientific notation (1.41322544692E9), the above will not work.
Please substitute your desired time zone in the region/city format if it didn’t happen to be Asia/Manila, for example America/Vancouver, America/Los_Angeles or Pacific/Pitcairn. Avoid three letter abbreviations like PST, they are ambiguous and often not true time zones.
Split the number into a pair of 64-bit long integers:
Number of whole seconds since the epoch reference date of first moment of 1970 in UTC
A number of nanoseconds for the fractional second
Pass those numbers to the factory method Instant.ofEpochSecond(long epochSecond, long nanoAdjustment)
With an Instant in hand, proceed to assign a time zone to get a ZonedDateTime.
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Expanding on Basil's and Ole's answers here, for the special case of a negative timestamp i.e. before epoch. Is that even possible? Here's what Jon Skeet writes in "All about java.util.Date":
The Date class uses “milliseconds since the Unix epoch” – that’s the
value returned by getTime(), and set by either the Date(long)
constructor or the setTime() method. As the moon walk occurred before
the Unix epoch, the value is negative: it’s actually -14159020000.
The only real difference between Ole's answer (besides a few extra asserts) is that here, we do not reverse the sign on nanos if the date string starts with a negative sign. The reason for this is, when passing the nanos to the Instant constructor, that is an adjustment, so if we send the nanos as a negative, it will actually adjust the seconds back, and thus the entire ZonedDateTime value is off by the nanos.
This is from the JavaDoc, note the interesting behavior:
This method allows an arbitrary number of nanoseconds to be passed in.
The factory will alter the values of the second and nanosecond in
order to ensure that the stored nanosecond is in the range 0 to
999,999,999. For example, the following will result in the exactly the
same instant:
Instant.ofEpochSecond(3, 1);
Instant.ofEpochSecond(4,-999_999_999);
Instant.ofEpochSecond(2, 1000_000_001);
So the 2nd argument, nanos, we are not setting the value, it is an adjustment. So just the same as for a positive timestamp (after epoch), we want to send in the actual nanos.
Taking Ole's code as a base and adding the above mentioned changes:
String strDateZoned = "Jul 20 1969 21:56:20.599 CDT"; // yes, should use America/Chicago here as Ole points out
DateTimeFormatter dtfFormatter = DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm:ss.SSS zzz");
ZonedDateTime originalZoned = ZonedDateTime.parse(strDateZoned, dtfFormatter);
long epochSeconds = originalZoned.toInstant().getEpochSecond();
int nanoSeconds = originalZoned.toInstant().getNano();
String dateInMillis = epochSeconds + "." + nanoSeconds;
String[] secondsAndFraction = dateInMillis.split("\\.");
int nanos = 0;
if (secondsAndFraction.length > 1) { // there’s a fractional part
// extend fractional part to 9 digits to obtain nanoseconds
String nanosecondsString
= (secondsAndFraction[1] + "000000000").substring(0, 9);
nanos = Integer.parseInt(nanosecondsString);
}
ZonedDateTime zdt = Instant
.ofEpochSecond(Long.parseLong(secondsAndFraction[0]), nanos)
.atZone(ZoneId.of("America/Chicago"));
String formattedZdt = dtfFormatter.format(zdt);
System.out.println("zoneDateTime expected = " + strDateZoned);
System.out.println("zoneDateTime from millis = " + formattedZdt);
assertEquals("date in millis is wrong", "-14159020.599000000", dateInMillis);
assertEquals("date doesn't match expected",strDateZoned, dtfFormatter.format(zdt));
Output from code:
zoneDateTime expected = Jul 20 1969 21:56:20.599 CDT
zoneDateTime from millis = Jul 20 1969 21:56:20.599 CDT
If we reverse the sign on nanos for the case where the seconds part is negative, we can see the difference in the formatted ZonedDateTime:
org.junit.ComparisonFailure: date doesn't match expected
Expected :Jul 20 1969 21:56:20.599 CDT
Actual :Jul 20 1969 21:56:19.401 CDT
P.S. A few more thoughts from the 'All About Dates' post on what Jon Skeet calls "leniency", and elsewhere I have seen called 'normalization' which is perhaps due to POSIX influences:
It’s lenient for no obvious reason: “In all cases, arguments given to
methods for these purposes need not fall within the indicated ranges;
for example, a date may be specified as January 32 and is interpreted
as meaning February 1.” How often is that useful?
Is there a possibility to convert a time format which is represented as a string (00:00:000) to long variable.
I'm not sure what do you mean by Convert to currentTimeMillis() or some float variable., but if you are simply looking to convert the given time to long then you can do something like this using simple split:
String timeString = "01:00:100";
int multiplier[] = {3600000, 60000, 100};
String splits[] = timeString.split(":");
long time = 0;
for (int x = 0; x < splits.length; x++) {
time += (Integer.parseInt(splits[x]) * multiplier[x]);
}
System.out.println(time);
Here, the time is being represented in Milliseconds.
Also, this is plain Java and nothing Android specific.
That simply doesn't make much sense.
currentTimeMillis() returns the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC.
In other words: the result of a call to that method represents a full blown time stamp; not only hh::mm:sss as in your example; but all of that plus year, month, ...
Thus the answer here: you should step back and clarify for yourself what your actual requirements are.
I am trying to calculate the difference between two times, which are represented as longs in the Format HHmm 24 hour time. E.g 4:30pm is represented by the long 0430.
I am happy for the difference to be in minutes.
Is there a simple calculation that can be done to achieve this? I am aware of Java's Date class, however I want to avoid having to store dummy date information just for a calculation on time.
Thanks!
Putting aside the fact that this is a really, really bad way to store times, the easiest way to do this is to convert the HHMM time to minutes since the start of the day:
long strangeTimeFormatToMinutes(long time) {
long minutes = time % 100;
long hours = time / 100;
return minutes + 60 * hours;
}
Then just use plain old subtraction to get the difference.
You may also want to add validation that minutes and hours are in the ranges you expect, i.e. 0-59 and 0-23.
You mentioned that you didn't want to use the Date class because it required you to use a dummy date. The LocalTime class does not require that.
LocalTime start = LocalTime.of(6,15,30,200); // h, m, s, nanosecs
LocalTime end = LocalTime.of(6,30,30,320);
Duration d = Duration.between(start, end);
System.out.println(d.getSeconds()/60);
Pad zeros
First convert your integer to a 4-character string, padding with leading zeros.
For example, 430 becomes 0430 and parsed as 04:30. Or, 15 becomes 0015 and parsed as quarter past midnight, 00:15.
String input = String.format( "%04d", yourTimeAsInteger );
LocalDate
The LocalTime class represents a time-of-day value with no date and no time zone.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "HHmm" );
LocalTime ld = LocalTime.parse( input , f ) ;
My english is not perfect, but I hope you can understand me.
I try to get the difference in seconds between two unix timestamps, but it's only return 0.
That's my code
unixOnline = Long.valueOf(online);
unixOffline = Long.valueOf(offline);
DateTimeZone BERLIN = DateTimeZone.forID("Europe/Berlin");
DateTime dateTimeOnline = new DateTime(unixOnline * 1000L, BERLIN);
DateTime dateTimeOffline = new DateTime(unixOffline * 1000L, BERLIN);
int seconds = Seconds.secondsBetween(new LocalDate(dateTimeOnline), new LocalDate(dateTimeOffline)).getSeconds();
System.out.println("Seconds: " + seconds);
Edit:
Online Timestamp: 1457536522
Offline Timestamp: 1457536642
LocalDate has no time component, so if the times are on the same day, they're effectively turned into the same time. Instead, just diff the DateTimes as they are;
int hours = Hours.hoursBetween(dateTimeOnline, dateTimeOffline).getHours();
(or in your case, since the difference is only 2 minutes, you'll only see the result with Minutes or Seconds)
EDIT: Since the question seems to have nothing to do with the time zone BERLIN which is in the code, this answer is a bit over complicated. Instead, use krzydyn's answer if it's just a time diff between UTC times.
Since you already have timestamps in seconds it can be simple calculated by formula:
int hours = (t2-t1)/3600;
Or if you need fractions:
float hours = (t2-t1)/3600f;
Update: (maybe I got suggested by the answer :)
So to get time diff in seconds is even simpler:
long seconds = t2-t1;
I have to convert seconds in UTC into day then add interval of one day and return seconds in UTC.
Here is what I have:
option #1
public static final long nextDayStartSec(long epochSecondsInUTC) {
return (epochSecondsInUTC / TimeUnit.DAYS.toSeconds(1) + 1) * TimeUnit.DAYS.toSeconds(1);
}
But not all days contain 86400 seconds according to Wikipedia:
Modern Unix time is based on UTC, which counts time using SI seconds,
and breaks up the span of time into days almost always 86400 seconds
long, but due to leap seconds occasionally 86401 seconds.
option #2
public static final long nextDayStartSec(long epochSecondsInUTC) {
return DateUtils.addMilliseconds(DateUtils.round(new Date(TimeUnit.SECONDS.toMillis(epochSecondsInUTC)), Calendar.DATE), -1)
.toInstant().atZone(systemDefault()).toLocalDateTime().toEpochSecond(ZoneOffset.UTC);
}
But it uses wide range of libraries (including Apache Commons) and hard to read.
Is there something simple that I missed?
If you use Java 8, the new time API allows you to write it this way (it adds one day to the given instant):
public static final long nextDayStartSec(long epochSecondsInUTC) {
OffsetDateTime odt = Instant.ofEpochSecond(epochSecondsInUTC).atOffset(ZoneOffset.UTC);
return odt.plusDays(1).toEpochSecond();
}
If you want to get the instant of the start of the next day, it could look like this:
public static final long nextDayStartSec(long epochSecondsInUTC) {
OffsetDateTime odt = Instant.ofEpochSecond(epochSecondsInUTC).atOffset(ZoneOffset.UTC);
return odt.toLocalDate().plusDays(1).atStartOfDay(ZoneOffset.UTC).toEpochSecond();
}