Date Zero Validation In Java - java

I have a method taking Date field as a input parameter.
public static String formatDate(Date inputDate) {
// if user send the date with inputDate= new Date(00000000) or new Date(0L) because it is Java default date.
I have to send the exception error with message Invalid date.
}
What I did is something as below, But I am unable to get the error while passing the invalid date of zero count-from-epoch like "new Date( 0L )" as inputDate parameter.
public static String formatDate(Date inputDate) {
if (null == inputDate)
throw new FieldFormatException("Empty date field.");
try {
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
return formatter.format(inputDate);
} catch (Exception ex) {
throw new FieldFormatException("Exception in formatting date field." + ex);
}
}

It sounds like you just want:
if (inputDate == null || inputDate.getTime() == 0L)
That will detect if inputDate is null or represents the Unix epoch.
As noted in the comments though:
Rejecting a single value is kinda dangerous - why is new Date(0) "wrong" but new Date(1) "right"?
This prevents you accepting legitimate input that happens to be the Unix epoch

The accepted Answer by Skeet is correct.
tl;dr
input.equals( Instant.EPOCH )
java.time
You are using troublesome old date-time classes that are now supplanted by the java.time classes.
The Instant class takes the place of Date as a moment on the timeline in UTC but with a finer resolution of nanoseconds rather than milliseconds.
You can convert when interfacing with legacy code not yet updated to java.time classes. To convert, call new methods added to the old classes.
Instant instant = myDate.toInstant() ;
Check for null.
if ( null == input ) {
throw new IllegalArgumentException( "Received invalid input: null." ) ;
}
Check for that count-from-epoch value zero that seems to be of special concern to you. The Instant class has a constant for that, for a count of zero nanoseconds from the epoch reference date-time used by Unix and Java: first moment of 1970 in UTC.
if ( input.equals( Instant.EPOCH ) ) {
throw new IllegalArgumentException( "Received invalid input: Instant.EPOCH. Input must be later than 1970-01-01T00:00:00Z." ) ;
}
You might want to check for recent date-time values, if required by your business rules.
if ( input.isBefore( Instant.now() ) ) {
throw new IllegalArgumentException( "Received invalid input. Input must not be in the past." ) ;
}
When generating a string take into account time zone. For any given moment, the date and the time-of-day vary around the globe by zone.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Your desired format happens to be the "basic" version of a standard ISO 8601 format. This format is predefined as a constant: DateTimeFormatter.BASIC_ISO_DATE.
String output = zdt.format( DateTimeFormatter.BASIC_ISO_DATE ) ;

Related

Validate a date in a string java (block errors like Feb 30)

Im trying to do a connection with a database in Java using OOP and DAO pattern with MySQL and need to validate a date string in java and insert it into MySQl, the connection is fine the problem is the validation. I have to use Strings in both because thats what i was asked for but it doesnt recognize months like february or the ones with 30 days i tried with a while but apparentrly it can be done with the method im usinng (TemporalAccesor for unary operator ERROR). I also have to calculate the age of the person based on that date but since is a string, i cant figure out how to connect it with the date of the computer.
Thanks for any help, really need it
Method that throws the exception but allows Feb-31 or Sep-31:
I call it on the method where I fill all the other data and works well except for that detail
Also tried a regex but i saw is not convenient and doesnt catch that either
private static void FechaNacV(){
String date;
Scanner sc = new Scanner(System.in);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu/MM/dd");
date = sc.nextLine();
try {
formatter.parse(date);
System.out.println("Valid date");
} catch (Exception e) {
System.out.println("Invalid Date, try yyyy/mm/dd format again");
FechaNacV();
}
}
ResolverStyle.STRICT
The default resolver style in DateTimeFormatter will adjust your invalid 31st of February to the appropriate 28th or 29th.
If you want to avoid that tolerant adjustment, specify a different resolver style.
DateTimeFormatter
.ofPattern( "uuuu/MM/dd" )
.withResolverStyle( ResolverStyle.STRICT ) ; // Be strict: Reject 31st of February.
Call parse on the LocalDate class rather than the DateTimeFormatter class.
LocalDate ld = null ;
String input = "2022/02/31" ;
DateTimeFormatter formatter =
DateTimeFormatter
.ofPattern( "uuuu/MM/dd" )
.withResolverStyle( ResolverStyle.STRICT ) ; // Be strict: Reject 31st of February.
try {
ld = LocalDate.parse( input , formatter ) ;
} catch ( DateTimeParseException e ) {
System.out.println( e ) ;
}
System.out.println( ld ) ;
See this code run at Ideone.com.
java.time.format.DateTimeParseException: Text '2022/02/31' could not be parsed: Invalid date 'FEBRUARY 31'
null

Convert timestamp to Date time for a particular timezone

I want to convert a timestamp (which is in GMT) to a local date and time.
This is what I have implemented so far, but it is giving me wrong month
Timestamp stp = new Timestamp(1640812878000L);
Calendar convertTimestamp = convertTimeStamp(stp,"America/Phoenix");
System.out.println(convertTimestamp.getTime());
public static Calendar convertTimeStamp( Timestamp p_gmtTime, String p_timeZone) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy HH:MM:SS a", Locale.ENGLISH);
DateFormat formatter = DateFormat.getDateTimeInstance();
if (p_timeZone != null) {
formatter.setTimeZone(TimeZone.getTimeZone(p_timeZone));
} else {
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
}
String gmt_time = formatter.format(p_gmtTime);
Calendar cal = Calendar.getInstance();
cal.setTime(sdf.parse(gmt_time));
return cal;
}
Any help would be appreciated.
You cannot convert a timestamp to another timezone, cause timestamps are always GMT, they are a given moment in the line of time in the universe.
We humans are used to local time on our planet, so a timestamp can be formatted to be more human readable, and in that context it is converted to a local timezone.
Using legacy java.util.* packages, this is done as follows:
DateFormat tzFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
tzFormat.setTimeZone(TimeZone.getTimeZone("CET")); // Use whatever timezone
System.out.println(tzFormat.format(date));
If you need to make "math" over the timestamp on local timezone (like, tomorrow at 8:00 local timezone), then the situation is more complex.
To do this you can resort to a number of hacks (like parsing or modifying the string obtained with the method above), or use the new Java date & time classes that have a specific class to deal with date and time in local time zones:
Instant timestamp = Instant.ofEpochMilli(inputValue);
ZonedDateTime romeTime = timestamp.atZone(ZoneId.of("Europe/Rome"));
Note how this second example uses "Europe/Rome" and not generically "CET". This is very important if you're planning to deal with timezones where DST is used, cause the DST change day (or if they use DST or not) may change from country to country even if they are in the same timezone.
tl;dr
Instant
.ofEpochMilli( // Parse a count of milliseconds since 1970-01-01T00:00Z.
1_640_812_878_000L
) // Returns a `Instant` object.
.atZone( // Adjust from UTC to a time zone. Same moment, same point on the timeline, different wall-clock time.
ZoneId.of( "America/Phoenix" )
) // Returns a `ZonedDateTime` object.
.format( // Generat text representing the date-time value kept within that `ZonedDateTime` object.
DateTimeFormatter
.ofLocalizedDateTime( FormatStyle.MEDIUM )
.withLocale( Locale.US )
) // Returns a `String` object.
See this code run live at IdeOne.com.
Dec 29, 2021, 2:21:18 PM
Details
You are using terrible old date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310. Never use Timestamp, Calendar, Date, SimpleDateFormat, etc.
Use the Instant class to represent a moment as seen in UTC, with an offset of zero hours-minutes-seconds.
long millisecondsSinceBeginningOf1970InUtc = 1_640_812_878_000L ;
Instant instant = Instant.ofEpochMilli( millisecondsSinceBeginningOf1970InUtc ) ;
Specify the time zone in which you are interested.
ZoneID z = ZoneId.of( "Africa/Tunis" ) ;
Adjust from offset of zero to that time zone to produce a ZonedDateTime object.
ZonedDateTime zdt = instant.atZone( z ) ;
Generate text representing that moment by automatically localizing. Use a Locale to specify the human language to use in translation as well as a culture to use in deciding abbreviation, capitalization, order of elements, and so on.
Locale locale = Locale.JAPAN ; // Or Locale.US, Locale.ITALY, etc.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.LONG ).withLocale( locale ) ;
String output = zdt.format( f ) ;
All of this has been addressed many times on Stack Overflow. Search to learn more.

Conditionally create LocalDateTime, ZonedDateTime or OffsetDateTime based on parsed TemporalAccessor

Given a DateTimeFormatter defined as:
public static final DateTimeFormatter DATE_TIME = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append( ISO_LOCAL_DATE )
.optionalStart().appendLiteral( ' ' ).optionalEnd()
.optionalStart().appendLiteral( 'T' ).optionalEnd()
.append( ISO_LOCAL_TIME )
.optionalStart().appendLiteral( ' ' ).optionalEnd()
.optionalStart().appendZoneOrOffsetId().optionalEnd()
.toFormatter();
I'd like to conditionally create a LocalDateTime, ZonedDateTime or OffsetDateTime based on whether the parsing included a zone-id, an offset or neither.
So far I have:
DATE_TIME.parse(
text,
(temporal) -> {
// see if there is an offset
final ZoneOffset offset = temporal.query( TemporalQueries.offset() );
if ( offset != null ) {
return OffsetDateTime.from( temporal );
}
// see if there is a tz
final ZoneId zoneId = temporal.query( TemporalQueries.zoneId() );
if ( zoneId != null ) {
return ZonedDateTime.from( temporal );
}
// otherwise its a LocalDateTime
return LocalDateTime.from( temporal );
}
);
What I have found though is that a zone-offset is never "recognized" - even if the text includes an offset, it is always reported as a zone-id. E.g. given "1999-12-31 12:59:59 +02:00" I'd expect an OffsetDateTime. However the "+02:00" is always parsed as a zone-id. Ultimately it works given the reciprocity between zoned and offset. But as a matter of (probably way over the top) correctness I'd like to treat these as OffsetDateTime.
Am I missing something to be able to make that distinction?
Thanks!
It looks like this was actually a bug in Java 8. If you store the value of DateTimeFormatter#parse in a Temporal variable and print its getClass(), you receive the following when compiling and running with Java 8:
class java.time.ZonedDateTime
However, when compiling and running with Java 11, the output is what you expect:
class java.time.OffsetDateTime
I'm going to search around for the specific bug report and edit this answer if I manage to find it. That way, we can determine the cause of the bug as well as the version of Java it was fixed in.
This is not really an answer. Jacob G. already provided that. I’d just like to supplement.
Workaround for Java 8
The ZoneId that you get from either TemporalQueries.zone() orTemporalQueries.zoneId() is a ZoneOffset when an offset has been parsed from the string. EDIT: UTC+02:00 doesn’t come out as a ZoneOffset, but calling normalized() on the ZoneId converts it into one. Now the instanceof operator will tell you which you have got.
for (String text : new String[] {
"1999-12-31 12:59:59",
"1999-12-31 12:59:59 +02:00",
"1999-12-31 12:59:59 UTC+02:00",
"1999-12-31 12:59:59 America/Porto_Velho",
}) {
TemporalAccessor parsed = DATE_TIME.parse(
text ,
(temporal) -> {
// see if there is an offset or tz
final ZoneId zoneOrOffset = temporal.query( TemporalQueries.zone() );
if ( zoneOrOffset != null ) {
ZonedDateTime zdt = ZonedDateTime.from( temporal );
// EDIT: call normalized() to convert a ZoneId
// with constant offset, e.g., UTC+02:00, to ZoneOffset
if (zoneOrOffset.normalized() instanceof ZoneOffset) {
return zdt.toOffsetDateTime();
} else {
return zdt;
}
}
// otherwise it's a LocalDateTime
return LocalDateTime.from( temporal );
}
);
System.out.format(Locale.ENGLISH, "%-30s %s%n", parsed.getClass(), parsed);
}
Output from this snippet is:
class java.time.LocalDateTime 1999-12-31T12:59:59
class java.time.OffsetDateTime 1999-12-31T12:59:59+02:00
class java.time.OffsetDateTime 1999-12-31T12:59:59+02:00
class java.time.ZonedDateTime 1999-12-31T12:59:59-04:00[America/Porto_Velho]
Simpler way for Java 9 and later
You were working too hard. Using DateTimeFormatter.parseBest() ought to have done your job in Java 8 and does work in Java 9 and later.
String text = "1999-12-31 12:59:59 +02:00";
TemporalAccessor parsed = DATE_TIME.parseBest(text,
OffsetDateTime::from, ZonedDateTime::from, LocalDateTime::from);
System.out.println(parsed.getClass());
System.out.println(parsed);
Output on Java 9:
class java.time.OffsetDateTime
1999-12-31T12:59:59+02:00
EDIT: This way doesn’t make an OFfsetDateTime out of a string with UTC+02:00 as time zone, though.

Java 8 formatted date input

I use this code to format a formatted text field to enter month & day:
SimpleDateFormat sdf = new SimpleDateFormat("MM.dd");
DateFormatter df = new DateFormatter(sdf);
DefaultFormatterFactory ddf = new DefaultFormatterFactory(df, df, df, df);
datumTextField.setValue(new Date(System.currentTimeMillis()));
datumTextField.setFormatterFactory(ddf);
When an invalid date is entered, eg. 13.10, a magican changes it to 1.10 after this line:
DateOfAdding = datumTextField.getText();
so the DateOfAddig value is 1.10.
How to turm the magican off?
tl;dr
MonthDay.parse(
"12.31" ,
DateTimeFormatter.ofPattern( "MM.dd" )
)
Catch DateTimeParseException for invalid input.
java.time.MonthDay
The modern approach uses the java.time classes rather than the troublesome legacy classes seen in the Question.
Among the java.time classes is MonthDay, just what you need.
I suggest collecting input as two different numbers.
MonthDay md = MonthDay.of( x , y ) ;
If you insist, you can collect input as a combined string and parse. If so I suggest you and your user use standard ISO 8601 format: --MM-DD.
MonthDay md = MonthDay.parse( "--12-31" ) ;
Or define a pattern using DateTimeFormatter.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM.dd" ) ;
MonthDay md = MonthDay.parse( input , f ) ;
Trap for DateTimeParseException to detect invalid inputs.
String input = "13.10" ; // Invalid input. Month must be 1-12.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM.dd" ) ;
MonthDay md = null ;
try
{
md = MonthDay.parse( input , f ) ;
} catch ( DateTimeParseException e ) {
// … handle error …
System.out.println( "Invalid input: " + input ) ;
}
See this code run live at IdeOne.com.
Invalid input: 13.10
e: java.time.format.DateTimeParseException: Text '13.10' could not be parsed: Unable to obtain MonthDay from TemporalAccessor: {MonthOfYear=13, DayOfMonth=10},ISO of type java.time.format.Parsed
md.toString(): null
You will want to call the SimpleDateFormat's setLenient method with false as a parameter so that the underlying Calendar used for parsing is set to non-lenient.
A non-lenient calendar will refuse field values that aren't in the expected range for this field, rather than accepting them and modifying other fields to make sense of them : in your case the month was rolled-over 12 and reached 1, while the year was increased by 1 and probably reached 1971 rather than the default-when-unspecified 1970.
From the Calendar's javadoc :
Leniency
Calendar has two modes for interpreting the calendar fields, lenient and non-lenient. When a Calendar is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar recomputes calendar field values for return by get(), all of the calendar fields are normalized. For example, a lenient GregorianCalendar interprets MONTH == JANUARY, DAY_OF_MONTH == 32 as February 1.
When a Calendar is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, a GregorianCalendar always produces DAY_OF_MONTH values between 1 and the length of the month. A non-lenient GregorianCalendar throws an exception upon calculating its time or calendar field values if any out-of-range field value has been set.

How can I convert a date in YYYYMMDDHHMMSS format to epoch or unix time?

EDIT: I have edited my question to include more information, I have tried many ways to do this already, asking a question on StackOverflow is usually my last resort. Any help is greatly appreciated.
I have a date (which is a Timestamp object) in a format of YYYYMMDDHHMMSS (e.g. 20140430193247). This is sent from my services to the front end which displays it in the format: date:'dd/MM/yyyy' using AngularJS.
How can I convert this into Epoch/Unix time?
I have tried the duplicate question that is linked to this, what I get returned is a different date.
I have also tried the following:
A:
//_time == 20140430193247
return _time.getTime()/1000; // returns 20140430193 - INCORRECT
B:
return _time.getTime(); // returns 20140430193247 (Frontend: 23/03/2608) - INCORRECT
C:
Date date = new Date();
//_time = 20140501143245 (i.e. 05/01/2014 14:32:45)
String str = _time.toString();
System.out.println("Time string is " + str);
//Prints: Time string is 2608-03-24 15:39:03.245 meaning _time.toString() cannot be used
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
try {
date = df.parse(str);
} catch (ParseException e) {
}
return date; // returns 20140501143245 - INCORRECT
D:
date = new java.sql.Date(_time.getTime());
return date; // returns 2608-03-24 - INCORRECT
The following shows the todays date correctly:
Date date = new Date();
return date; // returns 1398939384523 (Frontend: 01/05/2014)
Thanks
I got the answer after quite a while of trying different ways. The solution was pretty simple - to parse the time to a string as toString() didn't work.
Date date;
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
try {
date = df.parse(String.valueOf(_time.getTime()));
} catch (ParseException e) {
throw new RuntimeException("Failed to parse date: ", e);
}
return date.getTime();
tl;dr
LocalDateTime
.parse(
"20140430193247" ,
DateTimeFormatter.ofPattern( "uuuuMMddHHmmss" )
)
.atOffset(
ZoneOffset.UTC
)
.toEpochSecond()
java.time
Parse your input string as a LocalDateTime as it lacks an indicator of offset-from-UTC or time zone.
String input = "20140430193247" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuuMMddHHmmss" ) ;
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
Now we have a date with time-of-day, around half-past 7 PM on April 30 of 2014. But we lack the context of offset/zone. So we do not know if this was 7 PM in Tokyo Japan or 7 PM in Toledo Ohio US, two different moments that happened several hours apart.
To determine a moment, you must know the intended offset/zone.
If you know for certain that an offset of zero, or UTC itself, was intended, apply the constant ZoneOffset.UTC to get an OffsetDateTime.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
How can I convert this into Epoch/Unix time?
Do you mean a count of seconds or milliseconds since the first moment of 1970 in UTC?
For a count of whole seconds since 1970-01-01T00:00Z, interrogate the OffsetDateTime object.
long secondsSinceEpoch = odt.toEpochSecond() ;
For milliseconds, extract a Instant object. An Instant represents a moment in UTC, and is the basic building-block class of java.time. Then interrogate for the count.
Instant instant = odt.toInstant() ;
long millisSinceEpoch = instant.toEpochMilli() ;

Categories