SimpleDateFormat issue when parsing a String in ISO 8601 format - java

Appreciate there are lots of similar posts on this but I couldn't find a specific one to help.
I'm trying to convert this string to a Date in Java
2017-05-16 06:24:36-0700
But it fails each time with this code
Date Login = new SimpleDateFormat("dd/MM/yy HH:mm:ss").parse("2017-05-16 06:24:36-0700");
Now I'm presuming its due to the timezone info at the end - I just can't figure out how to set the format. I tried this but no luck
SimpleDateFormat("dd/MM/yy HH:mm:ssZ")
Any ideas?

The date format passed to your SimpleDateFormat is "dd/MM/yy", while the date you are trying to parse is of the format "yyyy-MM-dd". Try this instead:
Date login = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ").parse("2017-05-16 06:24:36-0700");
As a side note, depending on which version of Java you are using, I would recommend using the new java.time package (JDK 1.8+) or the back port of that package (JDK 1.6+) instead of the outdated (no pun intended) Date and/or Calendar classes.
Instant login = Instant.from(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssZ").parse("2017-05-16 06:24:36-0700"));

I have already upvoted Bryan’s answer exactly because it includes and recommends the java.time solution. I need to add a few thoughts, though.
Your code, reviloSlater, throws away the time zone information (more precsely, zone offset information), I’m not sure I would dare do that from the outset. With java.time classes it’s more natural to include it, and it’s easy to discard at a later point when we are sure we don’t need it.
To parse with offset:
OffsetDateTime loginOdt = OffsetDateTime.parse("2017-05-16 06:24:36-0700",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssZ"));
To drop the time zone offset information
LocalDateTime loginLdt = loginOdt.toLocalDateTime();
A LocalDateTime is a date and a time without any time zone or offset information. In this case of course we get
2017-05-16T06:24:36
Bryan’s java.time code too uses the time zone offset information from the string. Edit: after Bryan’s edit that code now works and gives us:
2017-05-16T13:24:36Z
This is the same point in time (Instant.toString() prints the time in UTC). Another way is, with the OffsetDateTime from before we can just do
Instant login = loginOdt.toInstant();
java.time is loaded with possibilities.

Related

how can i get Calendar.getInstance() based on Turkey timezone

I am developing an android application.
What should I do to get the current time based on Turkish local time?
val now = Calendar.getInstance(TimeZone.getTimeZone("GMT+3"))
the result is:
2020-08-25T18:16:30
but this website result is different:
https://www.timeanddate.com/worldclock/turkey/istanbul
2020-08-25T16:46:30
The output is printed using the following code snippet:
DebugHelper.info("one now => ${now.getDisplayMonthNameDayTime(FULL_PATTERN)}")
Extention Function:
const val FULL_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"
fun Calendar.getDisplayMonthNameDayTime(pattern: String = "dd MMM , HH:mm ") = SimpleDateFormat(
pattern,
Locale.getDefault()
).format(time).toUpperCase(Locale.getDefault())
If you want to use a modern and less troublesome API, then use java.time, especially java.time.ZonedDateTime.
See this minimal example:
public static void main(String[] args) {
ZonedDateTime istanbulDateTime = ZonedDateTime.now(ZoneId.of("Europe/Istanbul"));
System.out.println(istanbulDateTime);
}
Output (some seconds ago):
2020-08-25T16:32:56.069+03:00[Europe/Istanbul]
As an alternative, there is ZoneId.of("Asia/Istanbul"), too, but the values only differ in the description of the continent. Just a matter of taste, I think.
EDIT
After your edit I realized you aren't relying on a time zone but rather an offset. That brings in another alternative from java.time, that is java.time.OffsetDateTime.
For the sake of completeness, here's a possible solution which only takes a ZoneOffset without the need to provide a zone by String:
public static void main(String[] args) {
OffsetDateTime utcPlusThreeDateTime = OffsetDateTime.now(ZoneOffset.ofHours(3));
System.out.println(utcPlusThreeDateTime);
}
which output (a few seconds ago)
2020-08-25T16:53:14.490+03:00
... and yes, since there's API desugaring in Android, you can use it with a suitable gradle plugin.
The solution
Use ZoneId.of("Asia/Istanbul") and a ZonedDateTime from java.time, the modern Java date and time API, as demonstrated in the answer by deHaar.
The problem
You problem is in this line:
).format(time).toUpperCase(Locale.getDefault())
time gives you the time of the Calendar object as a Date (another poorly designed and long outdated class that we should not use anymore). A Date hasn’t got any time zone, so the time zone and offset information from the Calendar is lost. So when you format this Date, you are using the time zone of the SimpleDateFormat, not the time zone of the Calendar.
Your Calendar’s time zone was GMT+03:00 alright. As others have mentioned, you should prefer Europe/Istanbul or Asia/Istanbul, though.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related question: TimeZone problem in Java (it might even be a duplicate?)
Turkey have an issue with real time in java...
you need to do you own hack with GMT+03
TimeZone.getTimeZone("GMT+03")
code:
TimeZone timeZone = TimeZone.getTimeZone("GMT+03");
Calendar calendar = Calendar.getInstance(timeZone);

Not able to work with Java Date Function properly

If I take current date from my application, it comes with variation like below:
scenario 1: when the date is less than 10th of the month, a month is less than 10 of the year --> example: 5/9/18
scenario 2: when the date is >= 10th of the month, a month is less >= 10 of the year --> example: 10/11/18
Note: all the examples are in MM/DD/YY format and timezone is the USA
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE,-2);
DateFormat dateFormat = new SimpleDateFormat("MM/dd/yy HH:mm a");
String PastDate = dateFormat.format(cal.getTime());
info("Date is displayed as : "+ PastDate );
The above piece of code throwing me an error when the scenario 1 is in place. But if I format the date-time as "M/d/yy H:mm a" it works for both the scenario. I need the date add also.
Will it be a good practice to use the 2nd format? or there is any other way to get it done. Expert guidance please..
java.time
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(Locale.US);
ZonedDateTime dayBeforeYesterday = ZonedDateTime.now(ZoneId.of("America/St_Thomas"))
.minusDays(2);
System.out.println(dayBeforeYesterday.format(formatter));
Running just now I got this output:
5/7/18, 8:44 AM
Please specify your desired time zone where I put America/St_Thomas. Think twice before you use ZoneId.systemDefault() for your JVM’s time zone setting since this setting may be changed at any time from other parts of your program or other programs running in the same JVM; but if you trust the setting reflects the user’s time zone, it’s the correct thing to use.
Rather than defining your own output format prefer using one of the built-in formats you get from DateTimeFormatter.ofLocalizedDateTime. Do specify locale (no matter if you use a built-in format or roll your own). Again, use Locale.getDefault() if you trust the JVM’s setting is correct.
Avoid the old date and time classes like Calendar, DateFormat and SimpleDateFormat. They are not only long outdated, they are also poorly designed and the last two in particular notoriously troublesome. Today we have so much better in java.time, the modern Java date and time API.
Link: Oracle tutorial: Date Time explaining how to use java.time.
The number of characters in the format MM indicates that two digits are required in the input. A single character M will match one or two digits. Use M/d/yy H:mm a to support your desired formats.

Java 8 epoch-millis time stamp to formatted date, how?

Before Java-8 I got accustomed to always keep anything date/time related as milliseconds since Epoch and only ever deal with human readable dates/times on the way out, i.e. in a UI or a log file, or when parsing user generated input.
I think this is still safe with Java-8, and now I am looking for the most concise way to get a formatted date out of a milliseconds time stamp. I tried
df = Dateformatter.ofPattern("...pattern...");
df.format(Instant.ofEpochMilli(timestamp))
but it bombs out with Unsupported field: YearOfEra in Instant.getLong(...) which I half understand. Now what to use instead of Instant?
LocalDateTime.ofEpoch(Instant, ZoneId) seems wrong, since I don't care to have local time. I just want to see the local time zone when applying the formatter. Internally it should be just the Instant.
The same goes for ZonedDateTime.ofInstant(Instant, ZoneId), I thought to apply the ZoneId only when formatting. But I notice that the DateTimeFormatter does not itself deal anymore with time zones, it seems, so I reckon I need to use one of the above.
Which one is preferred and why? Or should I use yet another way to format an epoch-millis time stamp as a date/time with time zone?
An Instant does not contain any information about the time-zone, and unlike in other places, the default time-zone is not automatically used. As such, the formatter cannot figure out what the year is, hence the error message.
Thus, to format the instant, you must add the time-zone. This can be directly added to the formatter using withZone(ZoneId) - there is no need to manually convert to ZonedDateTime *:
ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter df = DateTimeFormatter.ofPattern("...pattern...").withZone(zone);
df.format(Instant.ofEpochMilli(timestamp))
* regrettably, in early Java 8 versions, the DateTimeformatter.withZone(ZoneId) method did not work, however this has now been fixed, so if the code above doesn't work, upgrade to the latest Java 8 patch release.
Edit: Just to add that Instant is the right class to use when you want to store an instant in time without any other context.
The error you have when formatting an Instant using a formatter built with a year or other fields is expected; an Instant does not know which year or month or day it is, it only knows how much milliseconds have elapsed since the Epoch. For the same instant, it could be 2 different days on 2 different places of the Earth.
So you need to add a time zone information if you want to print the day. With an Instant, you can call atZone(zone) to combine it with a ZoneId in order to form a ZonedDateTime. This is very much like an instant, only that it has a time zone information. If you want to use the system time zone (the one of the running VM), you can get it with ZoneId.systemDefault().
To print it, you can use the two built-in formatter ISO_OFFSET_DATE_TIME or ISO_ZONED_DATE_TIME. The difference between the two is that the zoned date time formatter will add the zone id to the output.
Instant instant = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(formatter.format(instant.atZone(ZoneId.systemDefault())));
System.out.println(formatter.format(instant.atZone(ZoneId.of("America/Los_Angeles"))));
when run on my machine, which has a system time zone of "Europe/Paris", you'll get:
2016-07-31T18:58:54.108+02:00
2016-07-31T09:58:54.108-07:00
You can of course build your own formatter if those one do not suit you, using ofPattern or the builder DateTimeFormatterBuilder.
I agree that this is somewhat confusing, especially when compared with it's predecessor Joda DateTime.
The most confusing thing is that the documentation for LocalDateTime says that it is "A date-time without a time-zone", and yet LocalDateTime.ofInstant method takes both an instant and a timezone as parameters.
That said, I think that you can achieve what you want by using Instant and LocalDateTime.ofInstant by using the UTC timezone.
public LocalDateTime millisToDateTime(long millis) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of("Z");
}

UTC time different for local and server

I am saving time with other data to show in my app . I am storing the time in utc in db . Now when i run the program locally it works fine but when i run code on server the time is different from utc . My code to get utc timestamp is
private Timestamp getUTCTimestamp() throws ParseException
{
SimpleDateFormat sdf = new SimpleDateFormat(DATEFORMAT);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String utcTime = sdf.format(new Date());
SimpleDateFormat dateFormat = new SimpleDateFormat(DATEFORMAT);
Timestamp dateToReturn = new Timestamp(((Date)dateFormat.parse(utcTime)).getTime());
return dateToReturn;
}
It returns acurate utc time but when i run it on server it doesn't give the utc time e.g i ran the program locally and it gave me "2016-04-17 20:58:55" which was right and then after 10 mins i ran the code on server and it saved the time "2016-04-17 16:02:46" which was different .My server location is in netharlands. I don't understand , shouldn't the utc time be same everywhere??
Why are you formatting and then parsing the formatted string? Just use:
private Timestamp getUTCTimestamp() throws ParseException
{
return new Timestamp(new Date().getTime());
}
As to the issue you're seeing, I think #Stanislav Palatnik's comment is correct. You need to set the timezone on your format that is parsing the time string. Or just use the same format for formatting and parsing.. but again, why do you even need to go through that work?
If you are going to save current time on DB, it is much better to save with NOW() function of MySQL to make sure wherever the code runs, as far as the DB is the same, NOW() values will be consistent and you don't need to create time objects and lines of code to handle current time on java side.
see: https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_now
You are using old troublesome classes that are now outmoded. Avoid using SimpleDateFormat, java.util.Date/.Calendar and such, and minimize use of java.sql types.
Among their many problems is the behavior of toString method, applying a default time zone while generating the string. This confuses debugging efforts.
java.time
The java.time classes supplant the old date-time classes. Built into Java 8 and later, available as back-ports to Java 6 & 7 and to Android.
Do your business logic in java.time. Use java.sql only until JDBC drivers are updated to handle java.time types directly.
Define your column as something close to the SQL standard TIMESTAMP WITH TIME ZONE. Not "WITHOUT".
An Instant is a moment on the timeline in UTC.
Instant instant = Instant.now();
Convert to java.sql.Timestamp for storage in database.
java.sql.Timestamp ts = java.sql.Timestamp.from( instant );
Going the direction.
Instant instant = ts.toInstant();
The toString methods in java.time classes do not apply mysterious time zones when generating the textual representation of their date-time value. And they use standard ISO 8601 formats to further clarity.
So we go in and out of the database
all in UTC, no time zones, very simple, very clear.
When you need a wall-clock value such as far presentation to user, Apple a time zone to the instant to get a ZonedDateTime. Covered in detail in many other pages in Stack Overflow.
Your servers should be assigned a time zone of UTC (or Iceland). But never depend on that. Using the approach shown above makes the server’s time zone irrelevant.
No answer could have helped me because i found the reason and it had nothing to do with the code . I ran this command Date -u on putty and realized that the time isn't synchronized with utc and i don't know why . This post helped me in getting to this reason. I haven't tried the solution so if someone has a better solution feel free to tell me :) This is the
link

Java Date problems with TIMEZONE

I am struggling on this for days.
I have a date field, that gives a date on 'yyyy-MM-dd' format.
My Object have this field like this
#Temporal(TemporalType.DATE)
private Date finishdate;
I am on UTC, and this need to work all over the world, so on UTC-7 or UTC+7
On DataBase this value need to be store with 0 hours.
When the finishdate is filled, the format give me the timezone, so, for example:
I want 2014-10-01, with ZERO HOURS AND MINUTES AND SECONDS, on diferent timezones I catch:
2014-10-01 07:00:00:000
or
2014-09-01 17:00:00:000
The problem seams to be because of the Date liybrary, and i've found a solution with JODA Library, but i was told not to used it, and I need to find another solution.
So, need to convert to UTC Date, all dates,or other thing, but the day must be the same, like 1 October.
Anyone pass through this?
The Joda-Time library fixes issues like this, and I believe that is also the basis of the java.time package in Java 8, but for older Java versions this kind of problem occurs constantly.
The only consistent way I have seen for dealing with this without Joda time is to treat pure dates as a String ("2014-10-01") or Integer type (20141001) instead of a Date. and only convert to dates when needed in calculations. It is a real pain though.
Don't forget that SimpleDateFormat is not thread-safe. The answer saga56 gives may work but you'll have some very weird dates if there's any simultaneous use of the deserialiser. You need to 'new' the SimpleDateFormats each time, or (less favourably) do something else to ensure SimpleDateFormat is strictly limited to 1 thread at a time.
Solution to this issue.
We made an Custom Deserializer to every object of the type Date.
On ObjectMapperFactory, where we serialize or deserialize, i mapped to another class like this:
module.addDeserializer(Date.class, new DateDeserializerByDefault());
Then, on this class we did:
private static SimpleDateFormat dateFormatWithoutTimezome = new SimpleDateFormat("yyyy-MM-dd");
private static SimpleDateFormat dateFormatWithTimezone= new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
private static Pattern pattern = Pattern.compile("([0-9]{4})-([0-9]{2})-([0-9]{2})");
#Override
public Date deserialize(JsonParser jparser, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String content = jparser.getValueAsString();
DateFormat format=(pattern.matcher(content).matches()) ? dateFormatWithoutTimezome : dateFormatWithTimezone;
try {
return format.parse(content);
} catch (ParseException e) {
throw new JsonParseException("Date parse failed", jparser.getCurrentLocation(),e);
}
}
And with this, when we receive Dates on diferent format, or with timezone to be stor we can change it to what we want.
I Hope this solution can help, I was stuck on this for 3,5 days. Dates are a pain in the a**.
The other Answers are correct but outdated.
java.time
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat. The Joda-Time team also advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
LocalDate
A LocalDate represents a date-only value without time-of-day and without time zone.
Your input string is in standard ISO 8601 format, so it can be parsed directly by LocalDate. No need to specify a formatting pattern.
String input = "2014-10-01";
LocalDate localDate = LocalDate.parse( input );
ZonedDateTime
You assume the day starts at time 00:00:00. But that is not always the case. In some time zones Daylight Saving Time (DST) or possibly other anomalies can mean the day starts at some other time on the clock such as 01:00:00. Let java.time determine the starting time of the first moment of a day. Specify a time zone, and assuming the tz database bundled with Java is up-to-date, then a call to LocalDate::atStartOfDay produces a ZonedDateTime for your date and first moment.
ZoneId zoneId = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = localDate.atStartOfDay( zoneId );
If you want the first moment of the day in UTC, specify the constant ZoneOffset.UTC (ZoneOffset being a subclass of ZoneId).
ZonedDateTime zdt = localDate.atStartOfDay( ZoneOffset.UTC );
Alternatively, use the more appropriate OffsetDateTime class. This is for values with a mere offset-from-UTC but lacking the set of rules for handling anomalies such as DST found in a full time zone. In UTC the day always starts at 00:00:00 which is stored in a constant LocalTime.MIN.
OffsetTime ot = OffsetTime.of( LocalTime.MIN , ZoneOffset.UTC );
OffsetDateTime odt = localDate.atTime( offsetTime );
Database
For database work, if you want a date-only value stored you should be using a data type along the lines of the SQL Standard type of DATE.
For a date-time value, nearly every serious database converts incoming data into UTC for storage in a TIMESTAMP WITH TIME ZONE type column. Your JDBC driver should help with this. But test and experiment as the behavior of drivers and databases varies tremendously.
With JDBC 4.2 and later, you may be able to pass/fetch the java.time types directly via setObject/getObject. If not, convert to java.sql types via new methods added to the old classes.

Categories