I have a string date with picoseconds (between 9 to 12 digits after the period), and when I try to parse the date with the DateTimeFormatter :
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSSSSS");
it throws an exception which says that i cant create a format with more than 9 digits after the period.
Is there a another class which can parse the date, or a different way to parse.
Any solution will help me, but prefer classes / ways which work with LocalDateTime
No solution
I have heard of no class, library, or database that represents picosecond resolution of time.
As commented on the Question, and as famously taught by Dr. Grace Hopper, light travels only about a foot in a nanosecond.
Light travels but a mere fraction of a millimeter in a picosecond. Think of the width of an individual grain of ground pepper, as Hopper showed in lectures. This is smaller than the distance taken by computer technology to move electrical signals around. So such a fine measurement of time is not likely to be practical or relevant in our lifetime.
Truncate
If the picos are not really necessary to your application, I suggest truncating your string, lopping off any digits after the first nine of the fractional second. Then parse as a Instant or LocalDateTime etc.
Roll-your-own class
If you must represent the picoseconds, I suggest splitting your input string in two, based on the decimal fraction delimiter (which under ISO 8601 standard can be either a comma or a period FULL STOP, with comma preferred).
Parse the first part as a LocalDateTime or Instant or a count of whole seconds since an epoch such as 1970-01-01T00:00:00Z.
Then parse the second part as 64-bit long integer.
You could write a little class named something like PicoMoment, with a parse method, to represent these two parts internally. Look to the OpenJDK source code to see similar representations made internally in the java.time classes.
If you go this route, I suggest you follow the java.time API as a model.
Related
By default, the toString method of Instant uses the DateTimeFormatter.ISO_INSTANT formatter. That formatter won’t print the digits for fraction-of-second if they happen to be 0.
java-time examples:
2015-10-08T17:13:07.589Z
2015-10-08T17:13:07Z
Joda-Time examples (and what I'd expect from java.time):
2015-10-08T17:13:07.589Z
2015-10-08T17:13:07.000Z
This is really frustrating to parse in some systems. Elasticsearch was the first problem I encountered, there's no pre-defined format that supports optional millis, but I can probably work around that with a custom format. The default just seems wrong.
It appears that you can’t really build your own format string for Instants anyway. Is the only option implementing my own java.time.format.DateTimeFormatterBuilder.InstantPrinterParser?
Just create a DateTimeFormatter that keeps three fractional digits.
DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendInstant(3).toFormatter();
Then use it. For example:
System.out.println(formatter.format(Instant.now()));
System.out.println(formatter.format(Instant.now().truncatedTo(ChronoUnit.SECONDS)));
…prints (at the time I run it):
2015-10-08T21:26:16.571Z
2015-10-08T21:26:16.000Z
Excerpt of the class doc:
… The fractionalDigits parameter allows the output of the fractional second to be controlled. Specifying zero will cause no fractional digits to be output. From 1 to 9 will output an increasing number of digits, using zero right-padding if necessary. The special value -1 is used to output as many digits as necessary to avoid any trailing zeroes. …
My Objective is
to create a java class that can handle the below two requirements
(A) 1. Verify if the format of a timestamp matches with expected format.
CCYY-MM-DD'T'hh:mm:ss'.0000000000+'uh:um"
Ex: the expected format is not static.
It may be either of these
"2013-09-10T18:30:20.123456+10:00" or
"2013-09-10T18:30:20.123+10:00".
I am not bothered about the
precision and value. Only the format matters.
(B) 2. Verify if the timestamp is in a certain range.
Ex: Verify if the timestamp is in
between "2013-09-10 18:27" and "2013-09-10 18:33". (verification is only upto minute level precision) (may be a delta of + or - 2min)
As suggested by one of the member, I have edited the post to target at
One specific question.
The QUESTION :
How to validate the custom timestamp upto microsec precision using JAVA class ?
The two arguments for this class will be
1) Expected FORMAT as a String
2) timestamp value as a String
Based on analysis from various search results, below is my understanding :
Java (by default) does not parse/format Timestamp at microsecond level( I used SimpleDateFormat)
If 6 digits are given in milliseconds place, it will re-calculate the value into seconds and the dateformat will be updated and the new dateformat will have 3 digits in milliseconds precision.
I have also seen a thread which suggests to use java.sql.Timestamp.
Tried this approach but not working.
I was not able to convert my strTimestamp 2013-09-10T18:30:20.123456+10:00 into Timestamp object.
Timestamp ts = Timestamp.valueOf(strTimestamp);
java.lang.IllegalArgumentException:
Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
I was not able convert my input format into Timestamp object.
I have a workaround to validate using regular expression :
2013-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])T(0[0-9]|1[0-9]|2[0-3]):(0[0-9]|[1-5][0-9]):(0[0-9]|[1-5][0-9]).[0-9][0-9][0-9][0-9][0-9][0-9]\+10:00
The problem with this reg ex is that, my expected timestamp format is not static. So i have to use a regex for every pattern.
So I am trying to figure out if there is any robust solution in java, which can be self sufficient even if the expected format changes.
java.time in Java 8
JSR 310 defined a new java.time package in Java 8. Its date-time class resolves to nanoseconds. That gives you 9 digits after the decimal point.
The java.time package is inspired by Joda-Time but entirely re-architected. Concepts are similar.
Like Joda-Time, the java.time package uses ISO 8601 formats as its defaults for parsing and formatting. So you can input or output strings such as 2013-09-10T18:30:20.123456789+10:00.
An early release of Java 8 is available now. Official release should be this month.
A project to backport this package to earlier versions of Java was underway. I do not know of its current status or success. The backport project is independent of Oracle and the OpenJDK project.
Milliseconds
The old bundled classes, java.util.Date & .Calendar, use a precision of milliseconds.
Ditto for the excellent Joda-Time library, milliseconds precision.
So not enough digits in the fractional seconds to meet your needs.
A java.sql.Timestamp is not going to help you, because that is a java.util.Date.
The code is fairly simple, if you use the right format String with SimpleDateFormat, which you let do the heavy lifting. Here's an entire working solution:
public static boolean isNear(String timestamp, int microPlaces, Date near, int minutes) {
if (!timestamp.matches(".*\\.\\d{" + microPlaces + "}\\D.*") {
return false;
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ");
try {
Date date = sdf.parse(timestamp.replaceAll(":(?=\\d\\d$)", ""));
return Math.abs(date.getTime() - near.getTime()) <= minutes * 60000;
} catch (ParseException ignore) {
return false; // string was not of correct format
}
}
This may not be exactly what you had in mind - if not, you should be able to use it as a basis for what you want. The key points are:
The S format string means "microseconds", and it doesn't require all the digits - so your timestamp can have any number
Java 6 needs the colon removed from the timezone. Java 7 doesn't need this - use the X format string instead of Z
A failure to parse a date from the input throws a ParseException - do what you want with this event
I chose to make the API give central date for the range and a +/- minute value. You may need to pass two dates - up to you. Use Date.before() and Date.after() to compare if you do that.
Here's some test code testing your examples and a couple of edge cases:
public static void main(String[] args) throws Exception {
Date near = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm").parse("2013-09-10T18:32");
System.out.println(isNear("2013-09-10T18:30:20.123456+10:00", near, 2));
System.out.println(isNear("2013-09-10T18:30:20.123+10:00", near, 2));
System.out.println(isNear("2013-09-10T18:10:20.123+10:00", near, 1));
System.out.println(isNear("XXXX-09-10T18:10:20.123+10:00", near, 1));
}
Output:
true
true
false
false
Really I`m also trying to find answer to this problem. As I have no ability to add comment to the Bohemian answer. I want to mention that 'S' pattern in SimpleDateFormat is used not for microseconds but for milliseconds. It means that for pattern "yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ" provided microsecond digits in string would be parsed as milliseconds.
So the first three digits would be passed as XXX seconds and their value value would be added to date. So we can receive mistake about 16 minutes.
ISO 8601 defines a syntax for representing a time interval.
There are four ways to express a time interval:
Start and end, such as "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"
Start and duration, such as "2007-03-01T13:00:00Z/P1Y2M10DT2H30M"
Duration and end, such as "P1Y2M10DT2H30M/2008-05-11T15:30:00Z"
Duration only, such as "P1Y2M10DT2H30M", with additional context information
If any elements are missing from the end value, they are assumed to be the same as for the start value including the time zone. This feature of the standard allows for concise representations of time intervals. For example, the date of a two-hour meeting including the start and finish times could be simply shown as "2007-12-14T13:30/15:30", where "/15:30" implies "/2007-12-14T15:30" (the same date as the start), or the beginning and end dates of a monthly billing period as "2008-02-15/03-14", where "/03-14" implies "/2008-03-14" (the same year as the start).
In addition, repeating intervals are formed by adding "R[n]/" to the beginning of an interval expression, where R is used as the letter itself and [n] is replaced by the number of repetitions. Leaving out the value for [n] means an unbounded number of repetitions. So, to repeat the interval of "P1Y2M10DT2H30M" five times starting at "2008-03-01T13:00:00Z", use "R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M".
I am looking for a good Java parser (if possible compatible with the Joda-Time library) to parse this syntax. Any pointers to a good library ?
java.time
The java.time framework built into Java 8 and later has a Duration.parse method for parsing an ISO 8601 formatted duration:
java.time.Duration d = java.time.Duration.parse("PT1H2M34S");
System.out.println("Duration in seconds: " + d.get(java.time.temporal.ChronoUnit.SECONDS));
Prints Duration in seconds: 3754
For anyone on a project that might be restricted from using 3rd party libraries (licensing reasons, or whatever), Java itself provides at least a portion of this capability, since Java 1.6 (or earlier?), using the javax.xml.datatype.DatatypeFactory.newDuration(String) method and Duration class. The DatatypeFactory.newDuration(String) method will parse a string in "PnYnMnDTnHnMnS" format. These classes are intended for XML manipulation, but since XML uses ISO 8601 time notation, they also serve as convenient duration parsing utilities.
Example:
import javax.xml.datatype.*;
Duration dur = DatatypeFactory.newInstance().newDuration("PT5H12M36S");
int hours = dur.getHours(); // Should return 5
I haven't personally used any duration format except the 4th one you list, so I can't vouch for whether it successfully parses them or not.
I take it you have already tried Joda-Time? Feeding the example strings from your question through Interval.parse(Object) reveals that it can handle "start and end", "start and duration" and "duration and end", but not implied fields nor repetition.
2007-03-01T13:00:00Z/2008-05-11T15:30:00Z => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
2007-03-01T13:00:00Z/P1Y2M10DT2H30M => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
P1Y2M10DT2H30M/2008-05-11T15:30:00Z => from 2007-03-01T13:00:00.000Z to 2008-05-11T15:30:00.000Z
2007-12-14T13:30/15:30 => java.lang.IllegalArgumentException: Invalid format: "15:30" is malformed at ":30"
R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M => java.lang.IllegalArgumentException: Invalid format: "R5"
The only other comprehensive date/time library that I know of is JSR-310, which does not appear to handle intervals like these.
At this point, building your own improvements on top of Joda-Time is probably your best choice, sorry. Are there any specific ISO interval formats that you need to handle beyond those already supported by Joda-Time?
The only library which is capable to model all the features of interval parsing you want is actually my library Time4J (range-module). Examples:
// case 1 (start/end)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/2014-06-20T16:00Z"));
// output: [2012-01-01T14:15:00Z/2014-06-20T16:00:00Z)
// case 1 (with some elements missing at end component and different offset)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/08-11T16:00+00:01"));
// output: [2012-01-01T14:15:00Z/2012-08-11T15:59:00Z)
// case 1 (with missing date and offset at end component)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/16:00"));
// output: [2012-01-01T14:15:00Z/2012-01-01T16:00:00Z)
// case 2 (start/duration)
System.out.println(MomentInterval.parseISO("2012-01-01T14:15Z/P2DT1H45M"));
// output: [2012-01-01T14:15:00Z/2012-01-03T16:00:00Z)
// case 3 (duration/end)
System.out.println(MomentInterval.parseISO("P2DT1H45M/2012-01-01T14:15Z"));
// output: [2011-12-30T12:30:00Z/2012-01-01T14:15:00Z)
// case 4 (duration only, in standard ISO-format)
Duration<IsoUnit> isoDuration = Duration.parsePeriod("P2DT1H45M");
// case 4 (duration only, in alternative representation)
Duration<IsoUnit> isoDuration = Duration.parsePeriod("P0000-01-01T15:00");
System.out.println(isoDuration); // output: P1M1DT15H
Some remarks:
Other interval classes exist with similar parsing capabilities, for example DateInterval or TimestampInterval in the package net.time4j.range.
For handling durations only (which can span both calendar and clock units as well), see also the javadoc. There are also formatting features, see nested class Duration.Formatter or the localized version net.time4j.PrettyTime (actually in 86 languages).
Interoperability is offered with Java-8 (java.time-package) but not with Joda-Time. For example: The start or end component of a MomentInterval can easily be queried by getStartAsInstant() or getEndAsInstant().
Repeating intervals are supported by the class IsoRecurrence. Example:
IsoRecurrence<MomentInterval> ir =
IsoRecurrence.parseMomentIntervals("R5/2008-03-01T13:00:00Z/P1Y2M10DT2H30M");
ir.intervalStream().forEach(System.out::println);
Output:
[2008-03-01T13:00:00Z/2009-05-11T15:30:00Z)
[2009-05-11T15:30:00Z/2010-07-21T18:00:00Z)
[2010-07-21T18:00:00Z/2011-10-01T20:30:00Z)
[2011-10-01T20:30:00Z/2012-12-11T23:00:00Z)
[2012-12-11T23:00:00Z/2014-02-22T01:30:00Z)
I'm storing messages from an amazon cloud and ordering them by their timestamp in a sorted map.
I am parsing the timestamp from the cloud with the following code:
Date timestamp = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'", Locale.ENGLISH).parse(time);
and then I am storing in them in a sorted map with the key being the date.
The issue is that the date only comes down to seconds precision.
I can have several messages sent in 1 second, so I need them to be ordered with millisecond precision. Is there a data structure that allows this?
Well as long as your source has a higher resolution than 1 second. Looks like that from the pattern, but you haven't shown us any input example.
Date is just a wrapper around a long milliseconds since 1970-01-01. So you have that already. Date.getTime() will return that, with millisecond precision.
Why would you think that Date only has one second precision? Date.compareTo(Date anotherDate) compares on a millisecond level.
So your SortedMap should work fine unless you are doing something strange.
I am not sure if you have done this, but you can create your own comparator and use that.
As a side note, depending on your applications setup you may want to be careful with how you use SimpleDateFormat, there are some issues with it.
java.time
I am providing the modern answer: use java.time, the modern Java date and time API, for your date and time work. First of all because it is so much nicer to work with than the old date and time classes like Date and (oh, horrors) SimpleDateFormat, which are poorly designed. We’re fortunate that they are long outdated. An added advantage is: Your date-time string is in ISO 8601 format, and the classes of java.time parse this format as their default, that is, without any explicit formatter.
String stringFromCloud = "2014-06-14T08:55:56.789Z";
Instant timestamp = Instant.parse(stringFromCloud);
System.out.println("Parsed timestamp: " + timestamp);
Output:
Parsed timestamp: 2014-06-14T08:55:56.789Z
Now it’s clear to see that the string has been parsed with full millisecond precision (Instant can parse with nanosecond precision, up to 9 decimals on the seconds). Instant objects will work fine as keys for your SortedMap.
Corner case: if the fraction of seconds i 0, it is not printed.
String stringFromCloud = "2014-06-14T08:56:59.000Z";
Parsed timestamp: 2014-06-14T08:56:59Z
You will need to trust that when no fraction is printed, it is because it is 0. The Instant will still work nicely for your purpose, being sorted before instants with fraction .001, .002, etc.
What went wrong in your parsing?
First, you’ve got a problem that is much worse than missing milliseconds: You are parsing into the wrong time zone. The trailing Z in your incoming string is a UTC offset of 0 and needs to be parsed as such. What happened in your code was that SimpleDateFormat used the time zone setting of your JVM instead of UTC, giving rise to an error of up to 14 hours. In most cases your sorting would still be correct. Around transition from summer time (DST) in your local time zone the time would be ambiguous and parsing may therefore be incorrect leading to wrong sort order.
As the Mattias Isegran Bergander says in his answer, parsing of milliseconds should work in your code. The reason why you didn’t think so is probably because just a minor one of the many design problems with the old Date class: even though internally it has millisecond precision, its toString method only prints seconds, it leaves out the milliseconds.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
What is the rationale behind Apple using Etc/GMT timezone when they return the receipt from the App Store for auto-renewable subscriptions.
What exactly is the Etc/GMT time zone? Does the Java SDK understand this time zone? Or do I have to use other third-party libraries like Joda-Time?
Etc/GMT is not strictly the same thing as UTC or GMT. They represent the same instant in time only when the offset is 0. In all other cases, they are quite different.
Apple explains the designation here.
A quote directly from the link gives an example:
We use POSIX-style signs in the Zone names and the output abbreviations,
even though this is the opposite of what many people expect.
POSIX has positive signs west of Greenwich, but many people expect
positive signs east of Greenwich. For example, TZ='Etc/GMT+4' uses
the abbreviation "GMT+4" and corresponds to 4 hours behind UTC
(i.e. west of Greenwich) even though many people would expect it to
mean 4 hours ahead of UTC (i.e. east of Greenwich).
Offset versus zone
Understand:
An offset-from-UTC is simply a number of hours-minutes-seconds, ahead of the baseline of UTC, or behind UTC.
A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region.
Positive versus negative numbering
Different protocols in various industries have varied in their numbering, with some considering offsets ahead of UTC to be positive numbers while others used negative. Symmetrically, some considered offsets behind UTC to be negative while others used positive.
In most modern protocols I’ve seen, such as the ISO 8601, offsets ahead of UTC (towards the east) are positive, while offsets behind UTC (towards the west) are negative. So the offsets used by zones in the Americas have negative numbers such as America/Los_Angeles having an offset of -07:00 or -08:00 nowadays (varies during the year because of Daylight Saving Time (DST)).
I suggest you learn to think of this manner (right of UTC is positive, left of UTC is negative) as mainstream, and the opposite as a minor annoying variation.
Time zone names are generally in the format Continent/Region, such as America/Edmonton, Europe/Paris, Africa/Tunis, Asia/Kolkata, and Pacific/Auckland. See this list on Wikipedia (may not be up-to-date). There are some exceptions. The Etc/GMT… names carry the opposite plus/minus convention:
Etc/GMT+1 = -01:00 offset = One hour behind UTC
Etc/GMT+12 = -12:00 offset = Twelve hours behind UTC
…and…
Etc/GMT-1 = +01:00 offset = One hour ahead of UTC
Etc/GMT-12 = +12:00 offset = Twelve hours ahead of UTC
Confusing? Welcome to the wacky world of date-time handling. It only gets weirder from here.
Key points:
Understand the meaning and intentions of those people publishing data. Never assume the meaning of an input string.
Use java.time classes only for all your date-time work. Never use the terrible legacy classes java.util.Date, Calendar, SimpleDateFormat, and such.
Fortunately the java.time classes can help you through this muddle. See the correct Answer by Ole V.V. using the ZoneId class.
Your questions
rationale behind Apple using Etc/GMT timezone
They mean an offset of zero, UTC itself. The string Etc/GMT is one canonical label for an offset-from-UTC of zero hours-minutes-seconds.
The letter Z (pronounced “Zulu”) seen commonly at the end of date-time strings means the same thing, an offset of zero.
What exactly is the Etc/GMT time zone?
The string Etc/GMT is a name for a time zone which has had only one offset-from-UTC ever, an offset of zero hours-minutes-seconds.
Most other time zones such as Europe/Berlin or Africa/Casablanca have varied in their offset over history. For example, in that Africa/Casablanca zone in Morocco, the politicians have decided last year that rather than switching an hour twice a year for standard time & DST, they will now stay permanently on DST year-round. I say “permanently” with a chuckle, as that really means “until the politicians change their mind again”. Politicians around the world have shown a penchant for redefining their time zones with surprising frequency.
Does the Java SDK understand this time zone?
Yes. See the Answer by Ole V.V.: ZoneId.of( "Etc/GMT" )
Or do I have to use other third-party libraries like Joda-Time?
FYI, the Joda-Time project is now in maintenance mode, advising migration to the java.time classes. See Tutorial by Oracle.
You should be using java.time classes for all your date-time handling.
Etc/GMT is just a standard way of saying UTC, GMT, GMT0 or GMT+00:00.
The Java JDK understands all of the formats. You can easily see this in action by doing the following:
import java.util.TimeZone;
public class Playground {
public static void main(String... args) {
for (String s : TimeZone.getAvailableIDs()) {
System.out.println(s);
}
}
}
This will print out all the different TimeZone formats that your Java JDK can parse:
...
Etc/GMT
Etc/GMT+0
Etc/GMT-0
Etc/GMT0
Etc/Greenwich
Etc/UCT
Etc/UTC
Etc/Universal
...
When conveying a point in time across time zones it is a recommended standard to use either UTC or GMT (the two are roughly equivalent, for most purposes we don’t distinguish). It appears that Apple does exactly this.
The JDK understands Etc/GMT just fine.
ZoneId etcGmt = ZoneId.of("Etc/GMT");
JDK uses the tz database (formerly known as the Olson database; link at the bottom). The list of time zone names in the database are in the other link at the bottom. Etc/GMT is listed there. You will notice that it is given as the canonical name for GMT (there are also some aliases, some current and some deprecated).
As an aside, my code line of course uses ZoneId from java.time, the modern Java date and time API. This is the JDK class that you will want to use (there is also an old and poorly designed class for time zones that you don’t want to use).
I don’t think you meant to ask, but for anyone interested: JDK also understands Etc/GMT+0, Etc/GMT+1, Etc/GMT0, Etc/GMT-0, Etc/GMT-1, etc. (no pun intended) since they are in the database too. And it correctly handles the reversed sign mentioned in the quote in the accepted answer by David Peden.
Links
Wikipedia article: tz database
List of tz database time zones
Documentation of ZoneId
I suggest you to use a Java library for App Store receipts and stop thinking about the date format.
Add the artifact (by.dev.madhead.utils.appstore_receipts_validator:model:2.0.0).
Use any HTTP client to call the App Store and get the response back (here I use Ktor):
suspend fun verify(receipt: String, password: String): VerifyReceiptResponse {
val rawResponse = client.post<String> {
url("https://buy.itunes.apple.com/verifyReceipt")
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
body = VerifyReceiptRequest(
receipt,
password,
true
)
}
}
Parse the response with Jackson:
val response = objectMapper.readValue(rawResponse)
Now you can use plain old Java API to work with the response.