Zone parsing for GMT0 - java

I have GMT0 as the default timezone in a system and it causes problem when I'm serializing it and deserializing it just after that.
System.setProperty("user.timezone","GMT0");
DateTimeFormatter zoneFormatter = new DateTimeFormatterBuilder()
.appendZoneOrOffsetId()
.toFormatter();
String formatted = zoneFormatter.format(ZonedDateTime.now());
System.out.println(formatted);
System.out.println(zoneFormatter.parse(formatted));
The first System.out.println prints GMT0 while the second throws the following problem.
Exception in thread "main" java.time.format.DateTimeParseException: Text 'GMT0' could not be parsed, unparsed text found at index 3
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1952)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1777)
is it an expected behavior? Is there a way to do that in a safe manner?

As you noticed in the comments, that's a bug in JDK 8, fixed only in versions >= 9.
If you're using JDK 8 and can't/won't upgrade it, there's a workaround. You can treat the "GMT" part as a literal (the text "GMT" itself) and consider the 0 as the offset seconds, using the respective ChronoField:
DateTimeFormatter zoneParser = new DateTimeFormatterBuilder()
// text "GMT"
.appendLiteral("GMT")
// offset seconds
.appendValue(ChronoField.OFFSET_SECONDS)
.toFormatter();
System.out.println(zoneParser.parse("GMT0"));
Keep in mind that this works only for offset zero. For any other values (such as "GMT2" or "GMT-2") this won't work, because it'll consider the values "2" and "-2" as seconds, but they actually mean "hours".
In case you need to parse all offset values in this format ("GMTn")
Well, JDK 8 also can't handle one-digit offsets, and it always requires a signal, either + or -. So "GMT2" and "GMT-2" won't work with the current API.
There's a harder alternative, though: create your own TemporalField, representing "offset hours". All the details about how to do it are in the documentation, and I'm not sure if all methods are correctly implemented - I'm just sure about isSupportedBy, getFrom and adjustInto, the others maybe need some improvement/adjustment:
public class OffsetHours implements TemporalField {
#Override
public TemporalUnit getBaseUnit() {
return ChronoUnit.HOURS;
}
#Override
public TemporalUnit getRangeUnit() {
return ChronoUnit.FOREVER;
}
#Override
public ValueRange range() {
return ValueRange.of(ZoneOffset.MIN.getTotalSeconds() / 3600, ZoneOffset.MAX.getTotalSeconds() / 3600);
}
#Override
public boolean isDateBased() {
return false;
}
#Override
public boolean isTimeBased() {
return true;
}
#Override
public boolean isSupportedBy(TemporalAccessor temporal) {
return temporal.isSupported(ChronoField.OFFSET_SECONDS);
}
#Override
public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
ValueRange rangeInSecs = temporal.range(ChronoField.OFFSET_SECONDS);
return ValueRange.of(rangeInSecs.getMinimum() / 3600, rangeInSecs.getMaximum() / 3600);
}
#Override
public long getFrom(TemporalAccessor temporal) {
return temporal.getLong(ChronoField.OFFSET_SECONDS) / 3600;
}
#Override
public <R extends Temporal> R adjustInto(R temporal, long newValue) {
return (R) temporal.with(ChronoField.OFFSET_SECONDS, newValue * 3600);
}
}
Now you create an instance of this field and use it in your parser:
// the new field
OffsetHours offsetHoursField = new OffsetHours();
DateTimeFormatter zoneParser = new DateTimeFormatterBuilder()
// text "GMT"
.appendLiteral("GMT")
// offset hours
.appendValue(offsetHoursField)
.toFormatter();
I also recommend creating a TemporalQuery to convert the parsed result to a ZoneOffset:
// get hours and create offset from hours value
TemporalQuery<ZoneOffset> getOffsetFromHours = temporal -> {
return ZoneOffset.ofHours((int) temporal.getLong(offsetHoursField));
};
Now you can parse it:
ZoneOffset offsetZero = zoneParser.parse("GMT0", getOffsetFromHours);
ZoneOffset offsetTwo = zoneParser.parse("GMT2", getOffsetFromHours);
ZoneOffset offsetMinusTwo = zoneParser.parse("GMT-2", getOffsetFromHours);
You can improve it letting the OffsetHours field to be a static instance (or maybe an enum), so you don't need to create it all the time.

Since Java 8, the ZoneId is responsible for handling timezone names and aliases according to IANA Time Zone Database (often referred as TZ DB, zdata). In particular, ZoneId.html#of is used for that conversion.
Different TZ DB versions are shipped by Oracle with different JREs: see tzdata versions and overview of how to update tzdata
The list of TZ DB identifiers is available on wikipedia (TZ column), as for release release 2017c, GMT0 is already listed as deprecated in there, and canonical name for this time zone is Etc/GMT
Despite there is no explicit instructions to use only canonical TZ names, it can be a best practice however, as deprecated aliases may be removed from further tzdata distributions (and thus from Zone.of support) without any special notice

Related

How do I determine if a time entered into a text field falls within the range of two pre-established opening and closing times?

The opening and closing hours for the company are 8am to 10 pm. The code below seems to add 4 hours on to whatever time is entered into the text field. So if I change the opening time to 11:59 I get the correct 8 am start date, but it's not possible to change the end time to 26:01 so I can't get the end time to work correctly. The data has to be stored in the database in UTC, but for display purposes it's displayed in EST.
public static boolean insideBusinessHours(String startTime, String endTime, String date) {
LocalDateTime localStart = stringToLDT_UTC(startTime, date);
LocalDateTime localEnd = stringToLDT_UTC(endTime, date);
String UTCstart = localStart.toString().substring(11,16);
String UTCend = localEnd.toString().substring(11,16);
LocalTime enteredStart = LocalTime.parse(UTCstart);
LocalTime enteredEnd = LocalTime.parse(UTCend);
LocalTime openingHour = LocalTime.of(07, 59);
LocalTime closingHour = LocalTime.of(22, 1);
Boolean startTimeAllowed = enteredStart.isAfter(openingHour);
Boolean endTimeAllowed = enteredEnd.isBefore(closingHour);
if (startTimeAllowed && endTimeAllowed) {
return true;
}
else {
return false;
}
}
public static LocalDateTime stringToLDT_UTC(String time, String date) {
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt = LocalDateTime.parse(date + " " + time, format)
.atZone(ZoneId.systemDefault())
.withZoneSameInstant(ZoneId.of("UTC"))
.toLocalDateTime();
return ldt;
}
Even though dates and times need to be stored in UTC, I believe that it makes more sense to do the comparisons in your local time.
private static final LocalTime OPENING_HOUR = LocalTime.of(8, 0);
private static final LocalTime CLOSING_HOUR = LocalTime.of(22, 0);
public static boolean insideBusinessHours(String startTime, String endTime) {
LocalTime start = LocalTime.parse(startTime);
LocalTime end = LocalTime.parse(endTime);
boolean startTimeAllowed = !start.isBefore(OPENING_HOUR);
boolean endTimeAllowed = !end.isAfter(CLOSING_HOUR);
return startTimeAllowed && endTimeAllowed;
}
Further comments:
You don’t need the date if the opening hours are valid every day (you would need it to convert to or from UTC, which I have avoided).
It seems you had added a minute in each end to make sure to allow start and end to fall on the opening and closing hour, respectively. It’s more correct to require the start time to fall on or after the opening time. Similarly the end time before or on the closing time. In the code I am using not before to mean on or after.
In Java integer literals with a leading 0 are interpreted as octal. So don’t use a leading 0 unless you intended octal. You may find that 07 for the hour of the day looks nice. but you will get a surprise when 08 does not work. And in a different context, your users could get a pretty unpleasant surprise when for example 0700 does work but denotes 448.
Don’t use Boolean objects. Don’t ever if you can avoid it since the risk of a null reference to a Boolean is confusing and from experience often leads to errors. Use primitive boolean (lower case b).
Most seasoned programmers prefer return startTimeAllowed && endTimeAllowed; as terser than your if-else statement.
Link
Octal on Wikipedia.

Java date treatment - March 29

I usually deal with dates with ints according to the pattern yyyyMMdd.
e.g.: today is 20170113, or better 20,170,113.
I need to calculate the distance bethween dates in days, so I wrote this:
public static int calculateDistance(int data1, int data2){
try {
SimpleDateFormat normalDateFormat = new SimpleDateFormat("yyyyMMdd");
long beginTime = normalDateFormat.parse(String.valueOf(data1)).getTime();
long endTime = normalDateFormat.parse(String.valueOf(data2)).getTime();
long diff = endTime - beginTime;
return (int) TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
And this test:
public void testCalculateDistance() {
System.out.println(Calculator.calculateDistance(20090325, 20090328));
System.out.println(Calculator.calculateDistance(20090326, 20090329));
System.out.println(Calculator.calculateDistance(20090327, 20090330));
System.out.println(Calculator.calculateDistance(20090328, 20090331));
System.out.println(Calculator.calculateDistance(20090329, 20090401));
System.out.println(Calculator.calculateDistance(20090330, 20090402));
System.out.println(Calculator.calculateDistance(20090331, 20090403));
System.out.println(Calculator.calculateDistance(20090401, 20090404));
}
The output should always be the same, since I'm making the same modifications to both begin and end. But I get 2 (so 1 day less) iff the interval (end excluded) contains March 29.I also found it happens whenever I have this day of any year within the interval I mean to measure.
Why does it happen?
How can I fix it?
EDIT: I read this and I know how to calculate the difference between dates. The point is that this doesn't seem to work with this kind of classes, so this question is not a duplicate because I needn't know how to find the difference, but I need to know which classes I should use, instead of the ones I used to use.
In this case it is probably preferable to use LocalDate and ChronoUnit from java.time package.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate beginning = LocalDate.parse("20090325",formatter);
LocalDate ending = LocalDate.parse("20090328",formatter);
System.out.println(ChronoUnit.DAYS.between(beginning,ending));

jodatime DateTime millis cannot be set with actual epoch millis

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.

NumberFormatException while parsing date with SimpleDateFormat.parse()

Have a function that creates a time-only Date object. (why this is required is a long story which is irrelevant in this context but I need to compare to some stuff in XML world where TIME (i.e. time-only) is a valid concept).
private static final SimpleDateFormat DF_TIMEONLY = new SimpleDateFormat("HH:mm:ss.SSSZ");
public static Date getCurrentTimeOnly() {
String onlyTimeStr = DF_TIMEONLY.format(new Date()); // line #5
Date onlyTimeDt = null;
try {
onlyTimeDt = DF_TIMEONLY.parse(onlyTimeStr); // line #8
} catch (ParseException ex) {
// can never happen (you would think!)
}
return onlyTimeDt;
}
There are probably at least a couple other ways to create a time-only Date in Java (or more precisely one where the date part is 1970-01-01) but my question is really not about that.
My question is that this piece of code starts randomly throwing NumberFormatException on line #8 after having run in production for long time. Technically I would say that this should be impossible, right ?
Here's an extract of random NumberFormatExceptions that come from above piece of code:
java.lang.NumberFormatException: multiple points
java.lang.NumberFormatException: For input string: ".11331133EE22"
java.lang.NumberFormatException: For input string: "880044E.3880044"
java.lang.NumberFormatException: For input string: "880044E.3880044E3"
First of all I hope we can agree that formally this should be impossible? The code uses the same format (DF_TIMEONLY) as output and then input. Let me know if you disagree that it should be impossible.
I haven't been able to re-produce the problem in a standalone environment. The problem seems to come when the JVM has run for a long time (>1 week). I cannot find a pattern to the problem, i.e. summer time / winter time, AM/PM, etc. The error is sporadic, meaning that one minute it will throw NumberFormatException and the next minute it will run fine.
I suspect that there's some kind of arithmetic malfunction somewhere in either the JVM or perhaps even in the CPU. The above exceptions suggests that there's floating point numbers involved but I fail to see where they would come from. As far as I know Java's Date object is a wrapper around a long which holds the number of millis since the epoch.
I'm guessing what is happening is that there's an unexpected string onlyTimeStr created in line #5 so the problem really lies here rather than in line #8.
Here's an example of a full stacktrace:
java.lang.NumberFormatException: For input string: "880044E.3880044E3"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1241)
at java.lang.Double.parseDouble(Double.java:540)
at java.text.DigitList.getDouble(DigitList.java:168)
at java.text.DecimalFormat.parse(DecimalFormat.java:1321)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:2086)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1455)
at java.text.DateFormat.parse(DateFormat.java:355)
at org.mannmann.zip.Tanker.getCurrentTimeOnly(Tanker.java:746)
Environment: Java 7
The likely cause is the fact that SimpleDateFormat isn't threadsafe, and you're referencing it from multiple threads. While extremely difficult to prove (and about as hard to test for), there is some evidence this is the case:
.11331133EE22 - notice how everything is doubled
880044E.3880044E3 - same here
You probably have at least two threads interleaving. The E was throwing me, I was thinking it was attempting to deal with scientific notation (1E10, etc), but it's likely part of the time zone.
Thankfully, the (formatting) basic fix is simple:
private static final String FORMAT_STRING = "HH:mm:ss.SSSZ";
public static Date getCurrentTimeOnly() {
SimpleDateFormat formatter = new SimpleDateFormat(FORMAT_STRING);
String onlyTimeStr = formatter.format(new Date());
return formatter.parse(onlyTimeStr);
}
There's a couple of other things you could be doing here, too, with a few caveats:
1 - If the timezone is UTC (or any without DST), this is trivial
public static Date getCurrentTimeOnly() {
Date time = new Date();
time.setTime(time.getTime() % (24 * 60 * 60 * 1000));
return time;
}
2 - You're going to have trouble testing this method, because you can't safely pause the clock (you can change the timezone/locale). For a better time dealing with date/time in Java, use something like JodaTime. Note that LocalTime doesn't have a timezone attached, but Date only returns an offset in integer hours (and there are zones not on the hour); for safety, you need to either return a Calendar (with the full timezone), or just return something without it:
// This method is now more testable. Note this is only safe for non-DST zones
public static Calendar getCurrentTimeOnly() {
Calendar cal = new Calendar();
// DateTimeUtils is part of JodaTime, and is a class allowing you to pause time!
cal.setTimeInMillis(DateTimeUtils.currentTimeMillis() % (24 * 60 * 60 * 1000));
return cal;
}
Joda-Time
FYI, the Joda-Time 2.3 library provides a class expressly for your purpose, time-only, without any date: LocalTime. And, it is thread-safe (immutable instances). Seems a much better option than manhandling the troublesome java.util.Date class.
LocalTime localTime = new LocalTime();
Dump to console…
System.out.println( "localTime: " + localTime );
When run…
localTime: 16:26:28.065
java.time
Java 8 brings the new java.time package, inspired by Joda-Time, defined by JSR 310.
In java.time, you will find a LocalTime class similar to the one in Joda-Time.
SimpleDateFormat is not thread safe. the following program will reproduce NumberFormatException while parsing string represented date to date object.
public class MaintainEqualThreadsPatallel {
static int parallelCount = 20;
public static void main(String[] args) throws Exception {
ExecutorService executorPool = Executors.newFixedThreadPool(parallelCount);
int numberOfThreads = 150; // Total thread count = 150*2= 300.
List<Future<Object>> futureReturns = new LinkedList<Future<Object>>();
for (int i = 0; i < numberOfThreads; i++) {
int uniqueRandomValues = uniqueRandomValues(1, 10);
// Callable Thread - call()
Future<Object> submit = executorPool.submit( new WorkerCallable(uniqueRandomValues) );
futureReturns.add(submit);
// Runnable Thread - run()
executorPool.execute( new WorkerThread(uniqueRandomValues) );
}
// WorkerCallable: Blocking main thread until task completes.
waitTillThreadsCompleteWork(futureReturns);
// Terminate Pool threads in-order to terminate main thread
executorPool.shutdown();
}
private static final SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
public static Date numberFormatEx(Date date) throws ParseException { // synchronized
String dateStr = sdf.format(date);
Date dateParsed = sdf.parse(dateStr); // NumberFormatException: For input string: "186E.2186E2"
System.out.println("Date :"+ dateParsed);
return dateParsed;
}
protected void loopFunction(int repeatCount) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName +":START");
for (int i = 1; i <= repeatCount; i++) {
try {
System.out.println(threadName +":"+ i);
sleepThread(100);
numberFormatEx(new Date());
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(threadName +":END");
}
public static void waitTillThreadsCompleteWork(List<Future<Object>> futureReturns) throws Exception {
for (Future<Object> future : futureReturns) {
int threadReturnVal = (int) future.get();
System.out.println("Future Response : "+threadReturnVal);
}
}
public static int uniqueRandomValues(int min, int max) {
int nextInt = ThreadLocalRandom.current().nextInt(min, max);
System.out.println("Random Vlaue : "+nextInt);
return nextInt;
}
public void sleepThread(long mills) {
try {
Thread.sleep(mills);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class WorkerThread extends MaintainEqualThreadsPatallel implements Runnable {
int randomValue = 0;
public WorkerThread(int randomValue) {
this.randomValue = randomValue;
}
#Override
public void run() {
// As separate stack run() function doesn't accepts parameters, pass to constructor.
loopFunction(randomValue);
}
}
class WorkerCallable extends MaintainEqualThreadsPatallel implements Callable<Object> {
int randomValue = 0;
public WorkerCallable(int randomValue) {
this.randomValue = randomValue;
}
public Object call() {
// As separate stack run() function doesn't accepts parameters, pass to constructor.
loopFunction(randomValue);
return randomValue;
}
}
NumberFormatException with different messages:
java.lang.NumberFormatException: multiple points
java.lang.NumberFormatException: For input string: ""
java.lang.NumberFormatException: For input string: "186E.2"
java.lang.NumberFormatException: For input string: "186E.2186E2"
java.lang.NumberFormatException: For input string: "22200222E.222002224EE4"
java.lang.NumberFormatException: For input string: "22200222E.222002224EE44"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at java.text.DigitList.getDouble(DigitList.java:169)
at java.text.DecimalFormat.parse(DecimalFormat.java:2056)
at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)
at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)
at java.text.DateFormat.parse(DateFormat.java:364)
In Multi-Threading/Web Application with Multi-Requests concept parse function leads to NumberFormatException which can be handled using synchronized block.
To overcome NumberFormatException on parse() function use any of the following scenarios.
Separate Object: Every request/thread works on its own object.
public static Date numberFormatEx(Date date) throws ParseException {
SimpleDateFormat ObjInstance = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
String dateStr = ObjInstance.format(date);
Date dateParsed = ObjInstance.parse(dateStr);
System.out.println("Date :"+ dateParsed);
return dateParsed;
}
Unnecessary creating reusable object for each thread.
Static Object synchronized block: Every request/thread shares the common object to perform operation. As multiple threads share same object at same time then the object data gets clear/overrride ""/"186E.2186E2" at some point and leads to error.
static SimpleDateFormat objStatic = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
public static synchronized Date numberFormatEx(Date date) throws ParseException {
String dateStr = objStatic.format(date);
Date dateParsed = objStatic.parse(dateStr); // NumberFormatException: For input string: "186E.2186E2"
System.out.println("Date :"+ dateParsed);
return dateParsed;
}
NOTE: In case of Memory management it better to use synchronized block with static object which is reusable.
I have got the same question, the cause is SimpleDateFormat is not thread-safe, I just add syncronized in the method, and it doesn't happen again.
You can use "sychronized" block to make it thread safe.
Something like:
synchronized (lastUpdatedFormat) {
date =
lastUpdatedFormat.parse(lastUpdatedFormat.format(currentDate));
}
The diagnosis in the accepted answer is correct. I am providing the modern answer: do use java.time, the modern Java date and time API, for your date and time work. In Java 7 too. SimpleDateFormat is notoriously troublesome, its lack of thread safety is only one of its many problems. So don’t use that class.
OffsetTime.now() and ThreeTen Backport
You want the current time only, though with an offset from UTC, if your format pattern is to be believed. We have got a method exactly for that in java.time, the modern Java date and time API. So no reason to format into a string and parse back.
OffsetTime timeOnly = OffsetTime.now(ZoneId.systemDefault());
System.out.println(timeOnly);
When I ran the code just now in my time zone, Europe/Copenhagen, on jdk.1.7.0_67, the output was:
06:21:55.419+01:00
By the way this is also the XML format for the concept of a time with time zone. Are we done?
The Date class you were returning is poorly designed and long outdated, so avoid it if you can. If you need one for a legacy API that you cannot afford to change just now, convert like this:
Instant asInstant = LocalDate.of(1970, Month.JANUARY, 1)
.atTime(timeOnly)
.toInstant();
Date oldfashionedDateObject = DateTimeUtils.toDate(asInstant);
System.out.println("As java.util.Date: " + oldfashionedDateObject);
As java.util.Date: Thu Jan 01 06:21:55 CET 1970
Question: Does it work on Java 7?
Environment: Java 7
java.time just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in. In this case use Date.from(asInstant) for converting from Instant to Date instead of the way shown in the code above.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Wikipedia article: ISO 8601
While the correct answer is the one by Clockwork-Muse (the cause of the problems is the fact that SimpleDateFormat isn't thread safe) I just wanted to deliver another method of creating a time-only Date object:
public static Date getCurrentTimeOnly() {
Calendar rightNow = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
int hour = rightNow.get(Calendar.HOUR_OF_DAY);
int minute = rightNow.get(Calendar.MINUTE);
int second = rightNow.get(Calendar.SECOND);
int msecond = rightNow.get(Calendar.MILLISECOND);
long millisSinceMidnight
= (hour * 60 * 60 * 1000)
+ (minute * 60 * 1000)
+ (second * 1000)
+ (msecond);
return new Date(millisSinceMidnight);
}
This method is somewhat more formally correct, i.e. it handles leap-seconds. It doesn't assume, like other methods, that all days since epoch has always had 24*60*60*1000 milliseconds in them.
It doesn't however handle the case where the leap second is on the current day.

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