Converting a ldap date - java

I'm exporting users from an ldap programmatically. Therefor I'm retrieving the users from ldap. One of the attributes is whenCreated.
One of the values I have to convert is: 20090813145607.0Z Directly splitting it up I get the following format: yyyyMMddHHmmss+.0Z. The problem is that the application is running in CET timezone and the time stored is UTC which is probably indicated by the .0Z . It is 14:56 UTC and the local representation is 16:56. For summer time it seems to be 2 hours and for winter time 1 hour.
I checked the SimpleDateFormat and there is a placeholder for the timezone, however its a different format.
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sdf.parse("20090813145607.0Z");
Will show the wrong date as it ignores the dates time zone.
Is there a way to convert it directly?

ISO 8601
As a couple of other Answers mentioned, the date-time format in question is defined by RFC 4517 Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules. See section 3.3.13, Generalized Time.
That section explains this LDAP format is a restricted version of one of the date-time formats defined by ISO 8601. This style using a minimum of separators is known as “basic” in ISO 8601.
In these formats, the Z on the end is short for Zulu and means UTC (basically same as GMT).
The decimal point and digit at the end represents a fraction of a second. Note that a comma is possible instead of the dot (period) in both RFC 4517 and ISO 8601. The comma is actually recommended over the dot in ISO 8601. The RFC 4517 spec allows for only a single digit fraction (some tenths of a fraction) or no dot/comma & digit at all. Note that in contrast: (a) ISO 8601 allows for any number of fractional digits, and (b) java.time objects have nanosecond resolution for up to nine digits of fractional second.
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.
Now in maintenance mode, the Joda-Time project 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.
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time.
Parsing
Define a formatting pattern to fit RFC 4517. Study the DateTimeFormatter class for the pattern coding. This should work: uuuuMMddHHmmss[,S][.S]X. The square brackets mean optional. We accommodate either a dot or comma. Note the singular digit for fraction of second. The X on the end allows for either a Z or an offset-from-UTC such as -08 or -0830 or -08:30 or -083015 or -08:30:15.
String input = "20090813145607.0Z";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "uuuuMMddHHmmss[,S][.S]X" );
OffsetDateTime odt = OffsetDateTime.parse ( input , f );
Instant instant = odt.toInstant ();
Dump to console.
System.out.println ( "input: " + input + " | odt: " + odt + " | instant: " + instant );
input: 20090813145607.0Z | odt: 2009-08-13T14:56:07Z | instant: 2009-08-13T14:56:07Z
Of course you should also be coding a check for java.time.format.DateTimeParseException in case of unexpected input.

Checking the RFC mentioned above it seems like using UTC is the recommended default behavior for ldap dates. Therefor I converted it directly:
public Date parseLdapDate(String ldapDate){
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
return sdf.parse(ldapDate);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}

What about using the split you described above, then reformatting the 0Z timezone into a standard format, then using sdf.parse(...)? Maybe something like this (with appropriate error checking added, of course):
String[] parts = inputDateTime.split("[.]");
String dateTimePart = parts[0];
String timeZonePart = "+0" + parts[1].substring(0, parts[1].length() - 1) + "00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssZ");
Date theDate = sdf.parse(dateTimePart + timeZonePart);

You can use the methods of org.apache.directory.shared.ldap.util.DateUtils:
String ldapDate="20090813145607.0Z";
Date date = DateUtils.parse(ldapDate);
String generalizedTime = DateUtils.getGeneralizedTime(date);

The syntax of the attribute is described in the directory schema. Applications must use the schema when converting, comparing, and ordering data that was retrieved from or stored in the directory. If the syntax of the whenCreated attribute is generalizedTime, then applications must use libraries for generalized time when converting. The syntax for generalizedTime is described in RFC4517.

This is the only piece of code that worked for me :
static String parseLdapDate(String ldapDate) {
long nanoseconds = Long.parseLong(ldapDate); // nanoseconds since target time that you want to convert to java.util.Date
long mills = (nanoseconds / 10000000);
long unix = (((1970 - 1601) * 365) - 3 + Math.round((1970 - 1601) / 4)) * 86400L;
long timeStamp = mills - unix;
Date date = new Date(timeStamp * 1000L); // *1000 is to convert seconds to milliseconds
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); // the format of your date
//sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // give a timezone reference for formating (see comment at the bottom
String formattedDate = sdf.format(date);
return formattedDate;
}

I tried to use the apache util GeneralizedTime class http://directory.apache.org/api/gen-docs/1.0.0-M11/apidocs/org/apache/directory/shared/util/GeneralizedTime.html with mixed results
to convert from current time to Active Direcotry format:
GeneralizedTime gt = new GeneralizedTime(Calendar.getInstance());
String gtADString = gt.toGeneralizedTime(GeneralizedTime.Format.YEAR_MONTH_DAY_HOUR_MIN_SEC_FRACTION, GeneralizedTime.FractionDelimiter.DOT, 1, GeneralizedTime.TimeZoneFormat.Z).replaceFirst("Z", "\\.0Z");
The only problem is that it does not work as advertised. The length of the fraction portion after the dot is supposed to be "1" according to this call but the result still comes out as 3. Instead of "20120410011958.6Z" I get "20120410011958.687Z" so I still have to get the time in seconds and insert ".0" before the Z. So here's what you have to do (in my case I don't care about the fraction so I put zero. AD cares)
GeneralizedTime gt = new GeneralizedTime(Calendar.getInstance());
String gtADString = gt.toGeneralizedTime(GeneralizedTime.Format.YEAR_MONTH_DAY_HOUR_MIN_SEC, GeneralizedTime.FractionDelimiter.DOT, 1, GeneralizedTime.TimeZoneFormat.Z).replaceFirst("Z", "\\.0Z");
Incidentally this code converts from AD GeneralizedTime string format to Java Date
GeneralizedTime gt = new GeneralizedTime(str);
Date d = gt.getCalendar().getTime();

Related

Jodatime malformation even if it looks correct

I have this piece of code
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss Z");
DateTime dateNow = dtf.parseDateTime(new DateTime().toString());
String registerDateStr = dateNow.toString();
But when I run it, it says malformed date:
java.lang.IllegalArgumentException: Invalid format: "2019-06-13T17:57:47.420+08:00" is malformed at ".420+08:00"
The date formed seems correct to me? Did I do something wrong with the format?
You’re working too hard. You don’t need any explicit formatter at all.
DateTime dateNow = new DateTime();
String registerDateStr = dateNow.toString();
System.out.println(registerDateStr);
Output when I ran just now:
2019-06-13T13:04:48.301+02:00
If you want, you can also parse the string back without a formatter, but I see no reason why you should since you will just get a DateTime object equal to the one you started out from:
DateTime parsedBack = DateTime.parse(registerDateStr);
System.out.println(parsedBack);
2019-06-13T13:04:48.301+02:00
Were you aware?
Note that Joda-Time is considered to be a largely “finished” project.
No major enhancements are planned. If using Java SE 8, please migrate
to java.time (JSR-310).
Quoted from the Joda-Time home page
Your format doesn't specify the milliseconds and there's an unneeded space before the timezone information.
This format will work:
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
Shouldn't the result be
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
From the javadoc,
Symbol Meaning Presentation Examples
------ ------- ------------ -------
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
Your format yyyy-MM-dd'T'HH:mm:ss Z didn't work because, you left out S (fraction of second, which is present in your string as .420)

Error when converting to BigQuery timestamp from ISO 8601 date string

I have an ISO 8601 date string in the following format:
String myIsoDateString = "2019-02-27T23:00:00.000Z"
I need to use the date string as part of a query I'm running in BigQuery. I'm trying to use the com.google.cloud.bigquery.QueryParameterValue class to convert it to a QueryParameterValue, with a type of timestamp, like this:
QueryParameterValue.timestamp(myIsoDateString)
This gives me an error:
java.lang.IllegalArgumentException: Invalid format: "2019-02-27T23:00:00.000Z" is malformed at "T23:00:00.000Z"
The inline help in Eclipse for the timestamp method states that it:
Creates a QueryParameterValue object with a type of TIMESTAMP. Must be in the format"yyyy-MM-dd HH:mm:ss.SSSSSSZZ", e.g. "2014-08-19 12:41:35.220000+00:00".
How do I convert myIsoDateString to the required format? Is there a better method I can use that will handle converting from an ISO 8601 string to a timestamp in BigQuery?
To anyone who is just trying to parse ISO 8601 in BigQuery (this post is the first Google result), try this:
SELECT
PARSE_TIMESTAMP('%Y-%m-%dT%H:%M:%E*SZ', '2018-10-12T13:22:27.120Z')
I would expect the answer the answer by Felipe Hoffa to work and give you what you want: replace the T with a space. Here are a couple of other suggestions.
Be explicit: While the documentation gives a quite clear example of an expected string, it’s not clear from the error message nor from the documentation (link below, but you’ve got it in Eclipse already) whether QueryParameterValue.timestamp accepts the Z as an offset. If we want to be sure and also be more explicit about supplying the format asked for (always nice for those managing your code after you):
DateTimeFormatter timestampFormatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSSxxx");
String myIsoDateString = "2019-02-27T23:00:00.000Z";
OffsetDateTime dateTime = OffsetDateTime.parse(myIsoDateString);
QueryParameterValue.timestamp(dateTime.format(timestampFormatter));
This passes the string 2019-02-27 23:00:00.000000+00:00 to timestamp.
Pass a long: timestamp comes in an overloaded version taking a Long argument. Again it’s not documented what the long value should be. Edit: Your own answer revealed that timestamp(Long) requires microseconds since the epoch. The fully correct way to convert without any risk of unnecessary loss of precision is:
String myIsoDateString = "2019-02-27T23:00:00.000Z";
Instant dateTime = Instant.parse(myIsoDateString);
long microsecondsSinceEpoch = TimeUnit.SECONDS.toMicros(dateTime.getEpochSecond())
+ TimeUnit.NANOSECONDS.toMicros(dateTime.getNano());
QueryParameterValue.timestamp(microsecondsSinceEpoch);
A value of 1 551 308 400 000 000 is passed to timestamp.
Disclaimer: I’m not a BigQuery user.
Links
QueryParameterValue documentation
Oracle tutorial: Date Time
The error says this is not the right format:
"2019-02-27T23:00:00.000Z"
But this one would be:
"yyyy-MM-dd HH:mm:ss.SSSSSSZZ"
Try replacing the T between date and time with a .
ISO 8601 versus SQL
The ISO 8601 standard uses a T in the middle to separate the year-month-day portion from the hour-minute-second portion.
The SQL style uses a SPACE in the middle rather than a T.
Replace.
String input = "2019-02-27T23:00:00.000Z".replace( "T" , " " ) ;
QueryParameterValue.timestamp( input ) ;
Ditto for going the other direction. The java.time classes use the ISO 8601 formats by default when parsing/generating text.
String input = "2019-02-27 23:00:00.000Z".replace( " " , "T" ) ;
Instant instant = Instant.parse( input ) ;
ZonedDateTime zdt = instant.atZone( ZoneId.of( "Pacific/Auckland" ) ) ;
This works:
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
Convert myIsoDateString to microseconds and store it as a long:
long myDateInMicroseconds = DateTime.parse(myIsoDateString).toDateTime(DateTimeZone.UTC).getMillis() * 1000;
Then pass it as the argument to QueryParameterValue.timestamp:
QueryParameterValue.timestamp(myDateInMicroseconds)

Simpledateformat unparseable date

I have a String in a database (match.getDate) that has the following date format:
01/04/2018
This is the date I want to format, stored as day/month/year. I want to format this for my Android app.
I want to format the date into:
Sun 01 Apr 2018
My code below:
SimpleDateFormat fDate = new SimpleDateFormat("dd/MM/yyyy");
try {
textViewDate.setText(fDate.parse(match.getDate()).toString());
} catch (ParseException ex) {
System.out.println(ex.toString());
}
This outputs:
Sun Apr 08 00:00:00 GMT+00:00 2018.
I have also tried "EE, MM d, yyyy", but it gives me:
java.text.ParseException: Unparseable date: "01/04/2018"
The other answers solved your problem, but I think it's important to know some concepts and why your first attempt didn't work.
There's a difference between a date and a text that represents a date.
Example: today's date is March 9th 2018. That date is just a concept, an idea of "a specific point in our calendar system".
The same date, though, can be represented in many formats. It can be "graphical", in the form of a circle around a number in a piece of paper with lots of other numbers in some specific order, or it can be in plain text, such as:
09/03/2018 (day/month/year)
03/09/2018 (monty/day/year)
2018-03-09 (ISO8601 format)
March, 9th 2018
9 de março de 2018 (in Portuguese)
2018年3月5日 (in Japanese)
and so on...
Note that the text representations are different, but all of them represent the same date (the same value).
With that in mind, let's see how Java works with these concepts.
a text is represented by a String. This class contains a sequence of characters, nothing more. These characters can represent anything; in this case, it's a date
a date was initially represented by java.util.Date, and then by java.util.Calendar, but those classes are full of problems and you should avoid them if possible. Today we have a better API for that.
In Android, you can use the java.time classes if available in the API level you're using, or the threeten backport for API levels lower than that (check here how to use it). You'll have easier and more reliable tools to deal with dates.
In your case, you have a String (a text representing a date) and you want to convert it to another format. You must do it in 2 steps:
convert the String to some date-type (transform the text to numerical day/month/year values) - that's called parsing
convert this date-type value to some format (transform the numerical values to text in a specific format) - that's called formatting
Why your attempts didn't work:
the first attempt gave you the wrong format because you called Date::toString() method, which produces an output (a text representation) in that format (Sun Apr 08 00:00:00 GMT+00:00 2018) - so the parsing was correct, but the formatting wasn't
in the second attempt, you used the output pattern (EE dd MMM yyyy, the one you should use for formatting) to parse the date (which caused the ParseException).
For step 1, you can use a LocalDate, a type that represents a date (day, month and year, without hours and without timezone), because that's what your input is:
String input = "01/04/2018";
DateTimeFormatter inputParser = DateTimeFormatter.ofPattern("dd/MM/yyyy");
// parse the input
LocalDate date = LocalDate.parse(input, inputParser);
That's more reliable than SimpleDateFormat because it solves lots of strange bugs and problems of the old API.
Now that we have our LocalDate object, we can do step 2:
// convert to another format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EE dd MMM yyyy", Locale.ENGLISH);
String output = date.format(formatter);
Note that I used a java.util.Locale. That's because the output you want has the day of week and month name in English, and if you don't specify a locale, it'll use the JVM's default (and who guarantees it'll always be English? it's better to tell the API which language you're using instead of relying on the default configs, because those can be changed anytime, even by other applications running in the same JVM).
And how do I know which letters must be used in DateTimeFormatter? Well, I've just read the javadoc.
Use this date formatter method I have created
public static String dateFormater(String dateFromJSON, String expectedFormat, String oldFormat) {
SimpleDateFormat dateFormat = new SimpleDateFormat(oldFormat);
Date date = null;
String convertedDate = null;
try {
date = dateFormat.parse(dateFromJSON);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(expectedFormat);
convertedDate = simpleDateFormat.format(date);
} catch (Exception e) {
e.printStackTrace();
}
return convertedDate;
}
and call this method like
dateFormater(" 01/04/2018" , "EE dd MMM yyyy" , "dd/MM/yyyy")
and you will get the desired output
You need two date formatters here. One to parse the input, and a different formatter to format the output.
SimpleDateFormat inDateFmt = new SimpleDateFormat("dd/MM/yyyy");
SimpleDateFormat outDateFmt = new SimpleDateFormat("EEE dd MMM yyyy");
try {
Date date = inDateFmt.parse(match.getDate());
textViewDate.setText(outDateFmt.format(date));
} catch (ParseException ex) {
System.out.println(ex.toString());
}
Try this, you can create any date format you want with this
public String parseTime(String date){
SimpleDateFormat format = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss");
try {
Date date1 = format.parse(date.replace("T"," "));
String d= new SimpleDateFormat("yyyy/dd/MM HH:mm:ss").format(date1);
return d;
}catch (Exception e){
e.printStackTrace();
}
return "";
}
Try with new SimpleDateFormat("EEE dd MMM yyyy", Locale.ENGLISH);
Sample Code:
DateFormat originalFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH);
DateFormat targetFormat = new SimpleDateFormat("EEE dd MMM yyyy", Locale.ENGLISH);
Date date = originalFormat.parse("01/04/2018");
String formattedDate = targetFormat.format(date); // Sun 01 Apr 2018
tl;dr
LocalDate
.parse(
"01/04/2018" ,
DateTimeFormatter // Parses & generates text in various formats
.ofPattern( "dd/MM/uuuu" ) // Define a formatting pattern to match your input.
) // Returns a `LocalDate` object.
.toString() // Generates text in standard ISO 8601 format.
2018-04-01
Use data types appropriately
I have a String in a database (match.getDate) that has the following date format:
Do not store date-time values as text.
You should be storing date-time values in a database using date-time data types. In standard SQL, a date-only value without time-of-day and without time zone is stored in a column of type DATE.
Another problem is that you are trying to represent a date-only value in Java class that represents a moment, a date with time-of-day in context of time zone or offset-from-UTC. Square peg, round hole. Using a date-only data types makes your problems go away.
java.time
The other Answers used outmoded classes, years ago supplanted by the modern java.time classes built into Java 8 and later, and built into Android 26 and later. For earlier Java & Android, see links below.
In Java, a date-only value without time-of-day and without time zone is represented by the LocalDate class.
LocalDate ld = LocalDate.parse( "2020-01-23" ) ; // Parsing a string in standard ISO 8601 format.
For a custom formatting pattern, use DateTimeFormatter.
String input = "01/04/2018" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" ) ;
LocalDate ld = LocalDate.parse( input , f ) ;
Generate a string in standard ISO 8601 format.
String output = ld.toString() ;
Generate a string in your custom format.
String output = ld.format( f ) ;
Tip: Use DateTimeFormatter.ofLocalizedDate to automatically localize your output.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
first of check your match.getDate() method which format given date if is given above define format date then used below code and show date in define above format ...
String date="09/03/2018";
SimpleDateFormat parseDateFormat = new SimpleDateFormat("dd/MM/yyyy"); // if your match.getDate() given this format date.and if is given different format that time define that format.
DateFormat formatdate = new SimpleDateFormat("EEE dd MMM yyyy");
try {
Date date1=parseDateFormat.parse(date);
Log.d("New Date",formatdate.format(date1));
} catch (ParseException e) {
e.printStackTrace();
}
output:: Fri 09 Mar 2018

Showing Local Time based on client / server offset in java

My client/browser is in India and I get the timezoneoffset from javascript
using the following code:
var now = new Date();
var localOffSet = now.getTimezoneOffset(); -330 // for India
int localOffSetMin = (localOffSet)*(-1);
My server is located in New York so I get the offset for it using:
TimeZone timeZone = now.getTimeZone();
int serverOffset = timeZone.getRawOffset();
int serverOffSetMinutes = serverOffset / 60000; // -300 for America/New York
In order to find the local time on my machine, I use this:
int offSets = Math.abs(serverOffSetMinutes-localOffSetMin);
now.setTime(createDt); // createDt is date field value for some column
now.add(Calendar.MINUTE, offSets); // adds offset
Date localDt = now.getTime();
But the date/time I get is 1 hour ahead of the expected time. What am I missing?
Date and Time manipulation with Java SE
You can print a list of supported TimeZones by using the following code.
System.out.println(TimeZone.getAvailableIDs().toString());
You can then find and print the difference between the timezones with the following code. You must be mindful of daylight savings time.
public void printTimeZoneDifference(String from, String to) {
TimeZone easternStandardTime = TimeZone.getTimeZone(from);
TimeZone indiaStandardTime = TimeZone.getTimeZone(to);
long milliseconds = easternStandardTime.getRawOffset() - indiaStandardTime.getRawOffset() + easternStandardTime.getDSTSavings() - indiaStandardTime.getDSTSavings();
String difference = String.format("%02d min, %02d sec", TimeUnit.MILLISECONDS.toMinutes(milliseconds), TimeUnit.MILLISECONDS.toSeconds(milliseconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds)));
System.out.println("The difference in time between" + easternStandardTime.getDisplayName() + " and " + indiaStandardTime.getDisplayName() + " is " + difference);
}
Although if I were to write something like this I would probably pass a TimeZone object as a parameter and keep the method solely responsible for substraction. Then I would either print the results or make it part of a different method. I didn't structure the post that way because I wanted to include all relevant code in the post.
Date and Time manipulation with Joda
This type of manipulation has already been solved in Java. The Joda Time Library is probably your best bet if you are doing a lot of date manipulation. If you are only manipulating time in this one instance then it would be a bit over kill to include the dependency in your runtime.
Again print out the TimeZones.
public void printDateTimeZones() {
for(String zone : DateTimeZone.getAvailableIDs()) {
System.out.println(zone);
}
}
Then you can return a String of the period (difference) between the two DateTimeZones using the default formatting with the following code.
public String printPeriod(String from, String to) {
Period period = new Period(new DateTime(DateTimeZone.forID(to)), new DateTime(DateTimeZone.forID(from)));
return PeriodFormat.getDefault().print(period);
}
Similarly Joda provides a format builder class which allows you to specify your preferred formatting.
public String printPeriod(String from, String to) {
PeriodFormatter formatter = new PeriodFormatterBuilder()
.printZeroRarelyFirst()
.appendYears().appendSuffix(" Years").appendSeparator(",")
.appendMonths().appendSuffix(" Months").appendSeparator(",")
.appendWeeks().appendSuffix(" Weeks").appendSeparator(",")
.appendDays().appendSuffix(" Days").appendSeparator(",")
.appendHours().appendSuffix(" Hours").appendSeparator(",")
.appendSeconds().appendSuffix(" Seconds").appendSeparator(",")
.appendMillis().appendSuffix(" Milliseconds")
.toFormatter();
return formatter.print(new Period(new DateTime(DateTimeZone.forID(from)), new DateTime(DateTimeZone.forID(to))));
}
A java.util.Date object has no timezone information. It has only a long value, which is the number of milliseconds from 1970-01-01T00:00:00Z (also known as "unix epoch" or just "epoch"). This value is absolutely independent of timezone (you can say "it's in UTC" as well).
To convert this value to another timezone, you don't need to do all these math between the timezones. You just get this millis value and convert it to the desired timezone.
To get the value from javascript, just do:
var d = new Date();
var millis = d.getTime();
The variable millis will contain the number of milliseconds from epoch. In the test I've made, this value is 1499101493296.
To create a java.util.Date object, just do:
Date date = new Date(1499101493296L);
To format this date in the timezone you want, use a SimpleDateFormat:
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
System.out.println(sdf.format(date));
The output will be:
03/07/2017 22:34:53
If you want a different format, check the javadoc for more information.
Also note that I used a timezone name using IANA format (always in the format Continent/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like IST or EST) because they are ambiguous and not standard.
To use another timezone, you can use one the IANA's names - check all the available names using TimeZone.getAvailableIDs().
New Java Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
Although you can also use Joda-Time, it is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
Once you have the millis value, the code for creating a date and converting to some timezone is very similar:
ZoneId zone = ZoneId.of("Asia/Kolkata");
ZonedDateTime z = Instant.ofEpochMilli(1499101493296L).atZone(zone);
System.out.println(z); // 2017-07-03T22:34:53.296+05:30[Asia/Kolkata]
The output will be:
2017-07-03T22:34:53.296+05:30[Asia/Kolkata]
If you want a different format, use a DateTimeFormatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss x");
System.out.println(z.format(fmt)); // 03/07/2017 22:34:53 +0530
The output will be:
03/07/2017 22:34:53 +0530
If you want a different format, check the javadoc for more details.
To use another timezone, you can use one the IANA's names - check all the available names using ZoneId.getAvailableZoneIds().

Output RFC 3339 Timestamp in Java

I want to output a timestamp with a PST offset (e.g., 2008-11-13T13:23:30-08:00). java.util.SimpleDateFormat does not seem to output timezone offsets in the hour:minute format, it excludes the colon. Is there a simple way to get that timestamp in Java?
// I want 2008-11-13T12:23:30-08:00
String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").format(new Date());
System.out.println(timestamp);
// prints "2008-11-13T12:23:30-0800" See the difference?
Also, SimpleDateFormat cannot properly parse the example above. It throws a ParseException.
// Throws a ParseException
new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").parse("2008-11-13T13:23:30-08:00")
Starting in Java 7, there's the X pattern string for ISO8601 time zone. For strings in the format you describe, use XXX. See the documentation.
Sample:
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")
.format(new Date()));
Result:
2014-03-31T14:11:29+02:00
Check out the Joda Time package. They make RFC 3339 date formatting a lot easier.
Joda Example:
DateTime dt = new DateTime(2011,1,2,12,45,0,0, DateTimeZone.UTC);
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
String outRfc = fmt.print(dt);
From the "get it done dept," one solution is to use regexes to fix up the string after SimpleDateFormat has completed. Something like s/(\d{2})(\d{2})$/$1:$2/ in Perl.
If you are even remotely interested in this, I will edit this response with the working Java code.
But, yeah. I am hitting this problem too. RFC3339, I'm looking at you!
EDIT:
This works for me
// As a private class member
private SimpleDateFormat rfc3339 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
String toRFC3339(Date d)
{
return rfc3339.format(d).replaceAll("(\\d\\d)(\\d\\d)$", "$1:$2");
}
I spent quite a lot of time looking for an answer to the same issue and I found something here : http://developer.android.com/reference/java/text/SimpleDateFormat.html
Suggested answer:
String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZZZZZ").format(new Date());
If you notice I am using 5 'Z' instead of one. This gives the output with a colon in the offset like this: "2008-11-13T12:23:30-08:00". Hope it helps.
The problem is that Z produces the time zone offset without a colon (:) as the separator.
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ss.SZ");
Is not what exactly you need?
We can simply use ZonedDateTime class and DateTimeFormatter class for this.
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssxxx");
ZonedDateTime z2 = ZonedDateTime.now(ZoneOffset.UTC).truncatedTo(ChronoUnit.SECONDS);
System.out.println("format =======> " + z2.format(format));
Output: format =======> 30-03-2020T05:57:37+00:00
I found a stray PasteBin that helped me out with the issue: http://pastebin.com/y3TCAikc
Just in case its contents later get deleted:
// I want 2008-11-13T12:23:30-08:00
String timestamp = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").format(new Date());
System.out.println(timestamp);
// prints "2008-11-13T12:23:30-0800" See the difference?
// Throws a ParseException
new SimpleDateFormat("yyyy-MM-dd'T'h:m:ssZ").parse("2008-11-13T13:23:30-08:00")
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'h:m:ss.SZ");
I made a InternetDateFormat class for RFC3339.
But source code comment is Japanese.
PS:I created English edition and refactoring a little.
i tried this format and worked for me yyyy-MM-dd'T'HH:mm:ss'Z'
java.time
The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern Date-Time API*.
Also, quoted below is a notice from the home page of Joda-Time:
Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.
Solution using java.time, the modern Date-Time API: The largest city in the Pacific Time Zone is Los Angeles whose timezone name is America/Los_Angeles. Using ZoneId.of("America/Los_Angeles"), you can create an instance of ZonedDateTime which has been designed to adjust the timezone offset automatically on DST transitions.
If you need timezone offset but not the timezone name, you can convert a ZonedDateTime into OffsetDateTime using ZonedDateTime#toOffsetDateTime. Some other uses of OffsetDateTime are to create a Date-Time instance with a fixed timezone offset (e.g. Instant.now().atOffset(ZoneOffset.of("+05:30")), and to parse a Date-Time string with timezone offset.
Demo:
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
ZoneId zoneIdLosAngeles = ZoneId.of("America/Los_Angeles");
ZonedDateTime zdtNowLosAngeles = ZonedDateTime.now(zoneIdLosAngeles);
System.out.println(zdtNowLosAngeles);
// With zone offset but without time zone name
OffsetDateTime odtNowLosAngeles = zdtNowLosAngeles.toOffsetDateTime();
System.out.println(odtNowLosAngeles);
// Truncated up to seconds
odtNowLosAngeles = odtNowLosAngeles.truncatedTo(ChronoUnit.SECONDS);
System.out.println(odtNowLosAngeles);
// ################ A winter date-time ################
ZonedDateTime zdtLosAngelesWinter = ZonedDateTime
.of(LocalDateTime.of(LocalDate.of(2021, 11, 20), LocalTime.of(10, 20)), zoneIdLosAngeles);
System.out.println(zdtLosAngelesWinter); // 2021-11-20T10:20-08:00[America/Los_Angeles]
System.out.println(zdtLosAngelesWinter.toOffsetDateTime()); // 2021-11-20T10:20-08:00
// ################ Parsing a date-time string with zone offset ################
String strDateTime = "2008-11-13T13:23:30-08:00";
OffsetDateTime odt = OffsetDateTime.parse(strDateTime);
System.out.println(odt); // 2008-11-13T13:23:30-08:00
}
}
Output from a sample run:
2021-07-18T03:27:15.578028-07:00[America/Los_Angeles]
2021-07-18T03:27:15.578028-07:00
2021-07-18T03:27:15-07:00
2021-11-20T10:20-08:00[America/Los_Angeles]
2021-11-20T10:20-08:00
2008-11-13T13:23:30-08:00
ONLINE DEMO
You must have noticed that I have not used a DateTimeFormatter to parse the Date-Time string of your question. It is because your Date-Time string is compliant with ISO-8601 standards. The modern Date-Time API is based on ISO 8601 and does not require using a DateTimeFormatter object explicitly as long as the Date-Time string conforms to the ISO 8601 standards.
Learn more about the modern Date-Time API from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
I tested a lot with this one, works well for me... In particular when it comes to parsing (and for formatting too), it is the closest I have found so far
DateTimeFormatter rfc3339Formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
DateTimeFormatter rfc3339Parser = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('-')
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral('-')
.appendValue(ChronoField.DAY_OF_MONTH, 2)
.appendLiteral('T')
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.optionalStart()
.appendFraction(ChronoField.NANO_OF_SECOND, 2, 9, true) //2nd parameter: 2 for JRE (8, 11 LTS), 1 for JRE (17 LTS)
.optionalEnd()
.appendOffset("+HH:MM","Z")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT)
.withChronology(IsoChronology.INSTANCE);
Test cases at https://github.com/guyplusplus/RFC3339-DateTimeFormatter

Categories