jodatime DateTime millis cannot be set with actual epoch millis - java

I'm looking for a reasonable way to set the value of jodatime's DateTime millis. While debugging in Intellij IDEA (v15), I'm looking at the following code:
public String getDayOfWeek(String timezone) {
DateTime now = DateTime.now(DateTimeZone.forID(timezone));
return now.dayOfWeek().getAsText();
}
If I breakpoint the return statement for the purpose of mutating the value of now by changing the millis field of the DateTime instance to reflect a different (valid) epoch time with millis, Intellij errors that the value I'm attempting to set exceeds the capacity of int. It makes sense since epoch with millis is 13-digits and IIRC int can only store 2^32-1.
There isn't a visible field in the DateTime instance for epoch time without millis. I can successfully set the value with the 10-digit epoch time without millis, but clearly that's not going to evaluate properly; I only mention it to say that I'm able to set within-range values in the debugger successfully.
What options are there? Alternately, is there a more elegant way with jodatime to derive the current day of the week that also allows me to mutate the value? The caller doesn't assign the return value, rather, simply uses it for comparison. I don't want to scrap jodatime since this method is part of a quite large class of methods that all use it.
Thanks in advance for your time.

Maybe you ran into this issue because the value used was interpreted as an int instead of a long? You can use one of the DateTime constructors that takes a long parameter to specify the date and time in milliseconds. For example:
import org.joda.time.*;
import org.joda.time.format.*;
public class JodaTimeMillis {
public static void main(final String[] arguments) {
new JodaTimeMillis().run();
}
private void run() {
DateTimeZone timeZone = DateTimeZone.forID("Europe/Amsterdam");
String pattern = "MM/dd/yyyy HH:mm:ss.SSS";
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(pattern);
for (int milliseconds = 619; milliseconds < 629; milliseconds++) {
DateTime dateTime = new DateTime(1234567890000L + milliseconds, timeZone);
System.out.println(milliseconds + ": " + dateTimeFormatter.print(dateTime));
}
}
}
This gives the following output (on my laptop), which shows that the milliseconds part can be specified:
619: 02/14/2009 00:31:30.619
620: 02/14/2009 00:31:30.620
621: 02/14/2009 00:31:30.621
622: 02/14/2009 00:31:30.622
623: 02/14/2009 00:31:30.623
624: 02/14/2009 00:31:30.624
625: 02/14/2009 00:31:30.625
626: 02/14/2009 00:31:30.626
627: 02/14/2009 00:31:30.627
628: 02/14/2009 00:31:30.628
Do you want to run tests from the debugger? You could also consider using unit tests.

Related

java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds

i like to get the duration between to datetime values in minutes.
public long datetimeDiffInMinutes(String dateStop, String dateStart) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDate firstDate = LocalDate.parse(dateStart, formatter);
LocalDate secondDate = LocalDate.parse(dateStop, formatter);
Duration d1 = Duration.between(firstDate, secondDate);
long min = d1.toMinutes();
return min;
}
There will be thrown an exception: java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds
But i dont use "Seconds" in this function. This line throws the exception: Duration d1 = Duration.between(firstDate, secondDate);
The documentation for the method you're calling (Duration.between(Temporal, Temporal)) states:
The specified temporal objects must support the SECONDS unit. For full accuracy, either the NANOS unit or the NANO_OF_SECOND field should be supported.
But LocalDate.isSupported is documented with:
If the unit is a ChronoUnit then the query is implemented here. The supported units are: DAYS, WEEKS, MONTHS, YEARS, DECADES, CENTURIES, MILLENNIA, ERAS
All other ChronoUnit instances will return false.
So no, LocalDate doesn't support seconds, which is required for the method you're calling.
It may be worth considering that a Duration is intended to be an elapsed time - a fixed number of seconds etc. The elapsed time between two dates may depend on the time zone involved - because a day doesn't always have 24 hours when there are time zones involved.
If you're happy assuming a 24-hour day, you could use Duration.ofDays(DAYS.between(firstDate, secondDate)).
You specify your dates with time information. That makes LocalDate a suboptimal choice. LocalDateTime is a better option. That already lets you create a duration.
Results may be off because of DST. Adding the right time zone should solve that:
ZoneId zone = ZoneId.systemDefault(); // or an explicit one
ZonedDateTime firstDateTime = LocalDateTime.parse(dateStart, formatter).atZone(zone);
ZonedDateTime secondDateTime = LocalDateTime.parse(dateStop, formatter).atZone(zone);
Duration d1 = Duration.between(firstDateTime, secondDateTime);
long min = d1.toMinutes();
For differences between dates, Period is a better representation.
Because your format string contains time, however, it looks like you want to be parsing to LocalDateTime instead of LocalDate. This way, the minutes (and seconds) you care about are not discarded:
private static final DateTimeFormatter parser =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public long datetimeDiffInMinutes(String dateStop, String dateStart) {
LocalDateTime firstDate = parser.parse(dateStart, LocalDateTime::from);
LocalDateTime secondDate = parser.parse(dateStop, LocalDateTime::from);
return firstDate.until(secondDate, ChronoUnit.MINUTES);
}
Note that because you don't have information about the time zone and any daylight-saving transitions that may occur, the results might not match what people expect in every case. You should clarify the use case, and get more information about the zone if necessary.

Add 30 minutes to the current time

I have written the code below but if the current date-time is 2022-07-03 09:48:05.448 and I add 30 minutes, my response returns 2022-07-03 09:79:05.448.
But minutes can never be 79, it is supposed to move to the hours instead...
public static String getExpiryDate(int additionalMinutesToCurrentMinute) {
LocalDateTime now = LocalDateTime.now();
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();
int hour = now.getHour();
int minute = now.getMinute() + additionalMinutesToCurrentMinute;
int second = now.getSecond();
int millis = now.get(ChronoField.MILLI_OF_SECOND); // Note: no direct getter available.
String expiryDateAndTime = String.format("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, millis);
return expiryDateAndTime;
}
Explanation
The reason your code does not work as expected is because you are not involving javas Date/Time API at all in your "math".
Your adding the minutes with plain int-arithmetic
int minute = now.getMinute() + additionalMinutesToCurrentMinute;
and then you use plain string formatting
String.format("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, millis);
Nothing in this chain is "clever" and knows about date/time specifics.
Solution
You have to involve the Date/Time API for your math, then it will be clever and correctly adjust the hours as well. Fortunately, there is a method in LocalDateTime already that does what you want:
LocalDateTime expirationTime = LocalDateTime.now().plusMinutes(30);
and that is pretty much all you need.
For the formatting part, either roll with the default representation:
return expirationTime.toString();
or use a DateTimeFormatter:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd HH:mm:ss.AAA");
and then
return expirationTime.format(formatter);
Notes
Instant
You are actually using the incorrect type for an expiration time. Using LocalDateTime will result in your application failing under certain situations. For example if your computer moves across countries, or your government decides to change its timezone. Or when DST hits (summer vs winter time) or leap seconds are added and more...
The correct type would be Instant, which represents a single moment on the timeline, without interpretation of clock-time or calendar-dates.
The API is the same, so you can just use it the same way.
That said, your method should also return Instant and not a String. Keep the clever date/time type as long as possible, dont go to something as low level and raw as a string.
public static Instant getExpiryDate(int additionalMinutes) {
return Instant.now()
.plus(additionalMinutes, ChronoUnit.MINUTES);
}
Design
Design-wise it would be better if the method would not even take int additionalMinutes but also the unit. Otherwise the call-site is hard to read for users:
getExpiryDate(30) // 30 what? minutes? seconds? days?
with the unit, it would be easier to read and harder to misunderstand
getExpiryDate(30, ChronoUnit.MINUTES)
At which point one could argue that the method is kinda obsolete now.
Instead of editing the amount of minutes manually, try using the plusMinutes method on your LocalDateTime like so:
LocalDateTime now = LocalDateTime.now();
LocalDateTime then = now.plusMinutes(30);
This way, the class should increase the hour for you once it passes 60 minutes.

normalize date using the deprecated Time class

the following method is written using the deprecated android Time class
// To make it easy to query for the exact date, we normalize all dates that go into
// the database to the start of the the Julian day at UTC.
public static long normalizeDate(long startDate) {
// normalize the start date to the beginning of the (UTC) day
Time time = new Time();
time.set(startDate);
int julianDay = Time.getJulianDay(startDate, time.gmtoff);
return time.setJulianDay(julianDay);
}
you can find this method here at line 47
please help me to understand it...
I tried different (unix, UTC) values for startDate argument such as 1464174000 and 1464433200 just to understand the output of the method but the method always return 1458000000 which is equivalent to:
03/15/2016 # 12:00am (UTC)
see the output here
so what is the purpose of the method if it always return the same value ?
i want to understand it so that i can write it again with the GregorianCalendar class that is not deprecated
From here
Callers must pass the time in UTC millisecond (as can be returned by toMillis(boolean) or normalize(boolean)) and the offset from UTC of the timezone in seconds (as might be in gmtoff).
So startDate should be in millisecond, not in second. Call with proper value. For example-
public static long normalizeDate(long startDate) {
// normalize the start date to the beginning of the (UTC) day
Time time = new Time();
time.set(1464181063013);
int julianDay = Time.getJulianDay(1464181063013, time.gmtoff);
return time.setJulianDay(julianDay);
}
I hope it will give you a different result.

How to create a joda time duration from java.sql.Time?

Hello I have this excerpt of code:
end = new DateTime(mergeToDateTime(this.endDate, this.empEndTime));
Duration extraTime = new Duration(this.preTime.getTime()); //add the first 30 mins
extraTime = extraTime.plus(new Duration(this.postTime.getTime())); //add the second 30 mins
end = end.plus(extraTime); // extraTime = -3600?
When I look in the debugger my durations are always coming up negative. I have no idea why this is, even though according to the API, it is possible to create a duration out of the a long type, hence the getTime(). (preTime and postTime are java.sql.Time types)
I guess your instances of java.sql.Time were created in such a way that their millisecond values include timezone offset.
For example, deprecated java.sql.Time(int hour, int minute, int second) constructor takes offset of the current timezone into account:
System.out.println(new Time(1, 0, 0).getTime()); // Prints -7200000 in UTC+3 timezone
It looks like timezone offset is introduced by JDBC driver, and it can be easily compensated by converting java.sql.Time to LocalTime (and vice versa):
LocalTime lt = new LocalTime(time);
Then you can convert LocalTime to duration:
Duration d = new Duration(lt.getMillisOfDay());
Aren't you starting out wrong when you use an instant in time as duration? The constructor signature you are using is Duration(long duration), not Duration(long startInstant) -- there is no such constructor, in fact.

Best way to get maximum Date value in java?

I'm writing a bit of logic that requires treating null dates as meaning forever in the future (the date in question is an expiration date, which may or may not exist). Instead of putting in special cases for a null date throughout the code, I want to just convert null into the maximum possible Date. I don't see any obvious ways to get such a value without hard coding it. What's the best way to get the maximum value of whatever Date implementation is being used?
Try
new Date(Long.MAX_VALUE)
which should give you the longest possible date value in Java.
Encapsulate the functionality you want in your own class, using Long.MAX_VALUE will most likely cause you problems.
class ExpirationDate {
Date expires;
boolean hasExpiration() {
return expires == null;
}
Date getExpirationDate() {
return expires;
}
boolean hasExpired(Date date) {
if (expires == null) {
return true;
} else {
return date.before(expires);
}
}
...
}
+1 to the Long.MAX_VALUE suggestions. It seems that this would help you if you sort stuff by your date field.
However, instead of constructing a date from some the large constant value where ever you need the date, use a globally visible singleton to hold a Date instance that represents your special value:
class DateUtil
{
public static final Date NO_EXPIRE = new Date( Long.MAX_VALUE );
}
Then you can use simple identity comparison (mydate == DateUtils.NO_EXPIRE) to test if a particular date is of your special case instead of obj.equals(); (ie. mydate.equals ( DateUtils.NO_EXPIRE ); )
Here is what I do:
public static final TimeZone UTC;
// 0001.01.01 12:00:00 AM +0000
public static final Date BEGINNING_OF_TIME;
// new Date(Long.MAX_VALUE) in UTC time zone
public static final Date END_OF_TIME;
static
{
UTC = TimeZone.getTimeZone("UTC");
final Calendar c = new GregorianCalendar(UTC);
c.set(1, 0, 1, 0, 0, 0);
c.set(Calendar.MILLISECOND, 0);
BEGINNING_OF_TIME = c.getTime();
c.setTime(new Date(Long.MAX_VALUE));
END_OF_TIME = c.getTime();
}
Note that if the TimeZone is NOT UTC you will get offsets from the "end of time", which won't be maximal values. These are especially useful for inserting into Database fields and not having to have NULL dates.
have you considered adopting the use of Joda Time?
It's slated to be included in java 7 as the basis for JSR-310
The feature that may interest you is ZeroIsMaxDateTimeField
which basically swaps zero fields for the maximum value for that field within the date-time.
From Java SE 8 you could use:
LocalDate.MAX
One problem I see is that for sorting on expiration date, using a null isn't easily sortable. So replacing with an actual value (even if it's an arbitrary sentry value well into the future) may be needed.
I suppose another way of treating "no expiration" is simply to say something expires 100 years in the future... Unless your database is dealing with long-term contracts!
I like Instant.MAX because it is more likely to be supported in the future than Long.MAX_VALUE.
Note that as of today, though, Instant.MAX.toEpochMilli() throws an overflow error.
Perhaps one option is to use the maximal system date. You can get it by using:
System.out.println(new Date(Long.MAX_VALUE).toString())
//Output:
//Sun Aug 17 12:42:55 IST 292278994

Categories