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().
Related
This question already has answers here:
Android convert UTC Date to local timezone [duplicate]
(2 answers)
Closed 5 years ago.
I have a date String like 2017-09-16T05:06:18.157 and I want to convert it to local time (IST). In Indian Standard Time it will be around 2017-09-16 10:36:18.
With Joda-Time, I have tried to convert it to local but I was not able to do it.
Below is my code:
private String getConvertDate(String date_server) {
DateTimeFormatter inputFormatter = DateTimeFormat
.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
.withLocale(Locale.US);
DateTime parsed = inputFormatter.parseDateTime(date_server);
DateTimeFormatter outputFormatter = DateTimeFormat
.forPattern("yyyy-MM-dd HH:mm:ss")
.withLocale(Locale.US)
.withZone(DateTimeZone.getDefault());
return outputFormatter.print(parsed);
}
Good you found a solution with SimpleDateFormat. I'd just like to add more insights about it (basically because the old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs).
The input String (2017-09-16T05:06:18.157) contains only the date (year/month/day) and time (hour/minute/second/millisecond), but no timezone information. So, when calling parseDateTime, Joda-Time just assumes that it's in the JVM default timezone.
If you know that the input is in UTC, but the input itself has no information about it, you must tell it. One way is to set in the formatter:
// set the formatter to UTC
DateTimeFormatter inputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
.withZone(DateTimeZone.UTC);
// DateTime will be in UTC
DateTime parsed = inputFormatter.parseDateTime("2017-09-16T05:06:18.157");
Another alternative is to first parse the input to a org.joda.time.LocalDateTime (a class that represents a date and time without a timezone), and then convert it to a DateTime in UTC:
// parse to LocalDateTime
DateTime = parsed = LocalDateTime.parse("2017-09-16T05:06:18.157")
// convert to a DateTime in UTC
.toDateTime(DateTimeZone.UTC);
Both produces the same DateTime, corresponding to UTC 2017-09-16T05:06:18.157Z.
To format it to "IST timezone" (which is actually not a timezone - more on that below), you can also set the timezone in the formatter:
// convert to Asia/Kolkata
DateTimeFormatter outputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
.withZone(DateTimeZone.forID("Asia/Kolkata"));
System.out.println(outputFormatter.print(parsed));
Or you can convert the DateTime to another timezone, using the withZone() method:
DateTimeFormatter outputFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
// convert to Asia/Kolkata
System.out.println(outputFormatter.print(parsed.withZone(DateTimeZone.forID("Asia/Kolkata"))));
Both will print:
2017-09-16 10:36:18
In your code you're using DateTimeZone.getDefault(), that gets the JVM default timezone (with some tricky details). But the default timezone can be changed without notice, even at runtime, so it's always better to specify which one you want to use.
Also, keep in mind that short names like IST are not real timezones. Always prefer to use IANA timezones names (always in the format Region/City, like Asia/Kolkata or Europe/Berlin).
Avoid using the 3-letter abbreviations (like IST or PST) because they are ambiguous and not standard. Just check in this list that IST can be "India Standard Time", "Israel Standard Time" and "Irish Standard Time".
You can get a list of available timezones (and choose the one that fits best your system) by calling DateTimeZone.getAvailableIDs().
Java new Date/Time API
Joda-Time 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).".
If you can't (or don't want to) migrate from Joda-Time to the new API, you can ignore this section.
In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).
This new API has lots of different date/time types for each situation.
First, you can parse the input to a org.threeten.bp.LocalDateTime, then I use a org.threeten.bp.ZoneOffset to convert it to UTC, resulting in a org.threeten.bp.OffsetDateTime.
Then, I use a org.threeten.bp.ZoneId to convert this to another timezone, and use a org.threeten.bp.format.DateTimeFormatter to format it (this is basically what's suggested by #Ole V.V's comment - just to show how straightforward it is, as there aren't anything much different to do):
// parse to LocalDateTime
OffsetDateTime parsed = LocalDateTime.parse("2017-09-16T05:06:18.157")
// convert to UTC
.atOffset(ZoneOffset.UTC);
// convert to Asia/Kolkata
ZoneId zone = ZoneId.of("Asia/Kolkata");
DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(outputFormatter.format(parsed.atZoneSameInstant(zone)));
The output is:
2017-09-16 10:36:18
try this code:
String serverdateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'";
public String convertServerDateToUserTimeZone(String serverDate) {
String ourdate;
try {
SimpleDateFormat formatter = new SimpleDateFormat(serverdateFormat, Locale.UK);
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Date value = formatter.parse(serverDate);
TimeZone timeZone = TimeZone.getTimeZone("Asia/Kolkata");
SimpleDateFormat dateFormatter = new SimpleDateFormat(serverdateFormat, Locale.UK); //this format changeable
dateFormatter.setTimeZone(timeZone);
ourdate = dateFormatter.format(value);
//Log.d("OurDate", OurDate);
} catch (Exception e) {
ourdate = "0000-00-00 00:00:00";
}
return ourdate;
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone utcZone = TimeZone.getTimeZone("UTC");
simpleDateFormat.setTimeZone(utcZone);
Date myDate =simpleDateFormat.parse(rawQuestion.getString("Asia/Kolkata"));
simpleDateFormat.setTimeZone(TimeZone.getDefault());
String formattedDate = simpleDateFormat.format(myDate);
I have a field that is defined as TIMESTAMP WITH TIME ZONE.
The value to be saved starts off as: "09-23-2019 10:03:11 pm" in the zone of US/Hawaii.
This is what I am trying to save to the DB (all of the date information plus the Zone)
The database stores time information in UTC format.
As of now, the date is being stored in the DB so that it looks like this:
DAYS
---------------------------------------------------------------------------
23-SEP-19 10.03.11.000000 PM -05:00
23-SEP-19 10.03.11.000000 PM -05:00
During the processing, it runs through this code:
dateStr: the date (as seen above)
ZoneLoc: 'US/Hawaii'
public Calendar convDateStrWithZoneTOCalendar(String dateStr,
String ZoneLoc) throws Exception {
// convert the string sent in from user (which uses AM/PM) to one that uses military time (24HR)
// it
String formattedDate = null;
DateFormat readFormat = new SimpleDateFormat(this.getPattern());
DateFormat writeFormat = new SimpleDateFormat("MM-dd-yyyy'T'HH:mm:ss'Z'");
writeFormat.setTimeZone(TimeZone.getTimeZone(ZoneLoc));
Date date = null;
date = readFormat.parse(dateStr);
formattedDate = writeFormat.format(date);
// see if you can parse the date needed WITH the TimeZone
Date d;
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone(ZoneLoc));
d = sdf.parse(formattedDate);
Calendar cal = Calendar.getInstance();
cal.setTime(d);
system.out.println(" ZONELOC VALUE " + ZoneLoc);
system.out.println(" RETURNED VALUE " + cal );
return cal;
}
The calendar info that is returned is:
ZONELOC VALUE IS US/Hawaii
RETURNED VALUE IS
java.util.GregorianCalendar[time=1577678591000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Chicago",offset=-21600000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/Chicago,offset=-21600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2019,MONTH=11,WEEK_OF_YEAR=1,WEEK_OF_MONTH=5,DAY_OF_MONTH=29,DAY_OF_YEAR=363,DAY_OF_WEEK=1,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=3,SECOND=11,MILLISECOND=0,ZONE_OFFSET=-21600000,DST_OFFSET=0]
It looks as though US/Hawaii is not being set in the RETURNED VALUE.
What can I do to be sure that this gets set?
After that, I can place it in the DB and see if the setting will "stick" and not revert back to America/Chicago
Update
#Patrick H - thanks for the input. I made the change with the pattern you specified and was able to save the data. It now looks like this:
2017-08-02 13:38:49 TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [26] as [TIMESTAMP] - [java.util.GregorianCalendar[time=1569294191000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Chicago",offset=-21600000,dstSavings=3600000,useDaylight=true,transitions=235,lastRule=java.util.SimpleTimeZone[id=America/Chicago,offset=-21600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2019,MONTH=8,WEEK_OF_YEAR=39,WEEK_OF_MONTH=4,DAY_OF_MONTH=23,DAY_OF_YEAR=266,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=3,SECOND=11,MILLISECOND=0,ZONE_OFFSET=-21600000,DST_OFFSET=3600000]]
The data in the DB looks like this:
23-SEP-19 10.03.11.000000 PM -05:00
The Zone is still America/Chicago even through US/Hawaii was specified. How can one get US/Hawaii to stick and not revert back to America/Chicago?
According to this output:
java.util.GregorianCalendar[time=1569294191000,...
The time value above (which means 1569294191000 milliseconds since unix epoch (1970-01-01T00:00Z)) is equivalent to 09-23-2019 10:03 PM in Chicago. That's because readFormat is using the system's default timezone (which is probably America/Chicago, just check the value of TimeZone.getDefault()).
To parse the input 09-23-2019 10:03:11 pm and consider it as the local time in Hawaii, you just need to set the corresponding timezone to the SimpleDateFormat instance (in this case, to readFormat, as it needs to know in what timezone the input date is - as you didn't set any, it uses the system's default). You also don't need the other formatters (writeFormat and sdf), only one formatter can be used to get the corresponding date:
SimpleDateFormat parser = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss a");
// the input is in Hawaii timezone
parser.setTimeZone(TimeZone.getTimeZone("US/Hawaii"));
Date date = parser.parse("09-23-2019 10:03:11 pm");
The date above will be equivalent to 10:03 PM in Hawaii. Actually, the date itself contains just the milliseconds from the unix epoch (date.getTime() returns 1569312191000) and has no format nor any timezone information.
You can then set it to a Calendar instance (don't forget to set the calendar's timezone):
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("US/Hawaii"));
cal.setTime(date);
It's been some time since I used oracle's timestamp with timezone type, but I think that'll be enough to save the correct values. The value of calendar is:
java.util.GregorianCalendar[time=1569312191000,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="US/Hawaii",offset=-36000000,dstSavings=0,useDaylight=false,transitions=7,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2019,MONTH=8,WEEK_OF_YEAR=39,WEEK_OF_MONTH=4,DAY_OF_MONTH=23,DAY_OF_YEAR=266,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=10,HOUR_OF_DAY=22,MINUTE=3,SECOND=11,MILLISECOND=0,ZONE_OFFSET=-36000000,DST_OFFSET=0]
Java new 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.
One of the main problems is how hard and confusing it is to work with different timezones.
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).
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.
To parse the input 09-23-2019 10:03:11 pm you can use a DateTimeFormatter and parse it to a LocalDateTime - the input has no timezone information, so we consider only the date and time, and then we can convert it to a timezone.
// parse the input
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// parse AM/PM and am/pm
.parseCaseInsensitive()
// input pattern
.appendPattern("MM-dd-yyyy hh:mm:ss a")
// use English locale for am/pm symbols
.toFormatter(Locale.ENGLISH);
LocalDateTime dt = LocalDateTime.parse("09-23-2019 10:03:11 pm", fmt);
// convert to Hawaii timezone
ZonedDateTime hawaiiDate = dt.atZone(ZoneId.of("US/Hawaii"));
The most recent JDBC drivers have support to the new API (but only for Java 8, I guess), but if you still need to work with Calendar, you can easily convert a ZonedDateTime to it:
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("US/Hawaii"));
calendar.setTimeInMillis(hawaiiDate.toInstant().toEpochMilli());
In Java 8, you can also do:
Calendar calendar = GregorianCalendar.from(hawaiiDate);
If you need interoperability with the old Calendar and Date API's, you can use the new API internally to do the calculations and convert from/to the API's when needed.
According to SimpleDateFormat, I think your formatting string is wrong. You can also see in the returned value that the month, and day are wrong. MONTH=11,DAY_OF_MONTH=29
This is what you currently have:
23-SEP-19 10.03.11.000000 PM -05:00
I think the formatting string should be: 'dd-MMM-yy hh.mm.ss.SSSSSS a Z'
It also looks like the timezone issue could be because there is a colon inside it. The documentation for SimpleDateFormat indicates it needs to be in this format instead for a RFC 822 time zone: -0500 You may find it easier to use the General time zone component instead.
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();
I want to get the current timestamp like that : 1320917972
int time = (int) (System.currentTimeMillis());
Timestamp tsTemp = new Timestamp(time);
String ts = tsTemp.toString();
The solution is :
Long tsLong = System.currentTimeMillis()/1000;
String ts = tsLong.toString();
From developers blog:
System.currentTimeMillis() is the standard "wall" clock (time and date) expressing milliseconds since the epoch. The wall clock can be set by the user or the phone network (see setCurrentTimeMillis(long)), so the time may jump backwards or forwards unpredictably. This clock should only be used when correspondence with real-world dates and times is important, such as in a calendar or alarm clock application. Interval or elapsed time measurements should use a different clock. If you are using System.currentTimeMillis(), consider listening to the ACTION_TIME_TICK, ACTION_TIME_CHANGED and ACTION_TIMEZONE_CHANGED Intent broadcasts to find out when the time changes.
1320917972 is Unix timestamp using number of seconds since 00:00:00 UTC on January 1, 1970. You can use TimeUnit class for unit conversion - from System.currentTimeMillis() to seconds.
String timeStamp = String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
You can use the SimpleDateFormat class:
SimpleDateFormat s = new SimpleDateFormat("ddMMyyyyhhmmss");
String format = s.format(new Date());
Use below method to get current time stamp. It works fine for me.
/**
*
* #return yyyy-MM-dd HH:mm:ss formate date as string
*/
public static String getCurrentTimeStamp(){
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String currentDateTime = dateFormat.format(new Date()); // Find todays date
return currentDateTime;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
It's simple use:
long millis = new Date().getTime();
if you want it in particular format then you need Formatter like below
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String millisInString = dateFormat.format(new Date());
You can get Current timestamp in Android by trying below code
time.setText(String.valueOf(System.currentTimeMillis()));
and timeStamp to time format
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
String dateString = formatter.format(new Date(Long.parseLong(time.getText().toString())));
time.setText(dateString);
Here's a human-readable time stamp that may be used in a file name,
just in case someone needs the same thing that I needed:
package com.example.xyz;
import android.text.format.Time;
/**
* Clock utility.
*/
public class Clock {
/**
* Get current time in human-readable form.
* #return current time as a string.
*/
public static String getNow() {
Time now = new Time();
now.setToNow();
String sTime = now.format("%Y_%m_%d %T");
return sTime;
}
/**
* Get current time in human-readable form without spaces and special characters.
* The returned value may be used to compose a file name.
* #return current time as a string.
*/
public static String getTimeStamp() {
Time now = new Time();
now.setToNow();
String sTime = now.format("%Y_%m_%d_%H_%M_%S");
return sTime;
}
}
Here is the comparison list of the most widely known methods
Solution in Kotlin:
val nowInEpoch = Instant.now().epochSecond
Make sure your minimum SDK version is 26.
java.time
I should like to contribute the modern answer.
String ts = String.valueOf(Instant.now().getEpochSecond());
System.out.println(ts);
Output when running just now:
1543320466
While division by 1000 won’t come as a surprise to many, doing your own time conversions can get hard to read pretty fast, so it’s a bad habit to get into when you can avoid it.
The Instant class that I am using is part of java.time, the modern Java date and time API. It’s built-in on new Android versions, API level 26 and up. If you are programming for older Android, you may get the backport, see below. If you don’t want to do that, understandably, I’d still use a built-in conversion:
String ts = String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
System.out.println(ts);
This is the same as the answer by sealskej. Output is the same as before.
Question: Can I use java.time on Android?
Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Here is another solution, this is in kotlin:
val df: DateFormat = SimpleDateFormat("yyyy.MM.dd HH:mm:ss")
val timeStamp = df.format(Calendar.getInstance().time)
Output example:
"2022.04.22 10:22:35"
I suggest using Hits's answer, but adding a Locale format, this is how Android
Developers recommends:
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
return dateFormat.format(new Date()); // Find todays date
} catch (Exception e) {
e.printStackTrace();
return null;
}
This code is Kotlin version. I have another idea to add a random shuffle integer in last digit for giving variance epoch time.
Kotlin version
val randomVariance = (0..100).shuffled().first()
val currentEpoch = (System.currentTimeMilis()/1000) + randomVariance
val deltaEpoch = oldEpoch - currentEpoch
I think it will be better using this kode then depend on android version 26 or more
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