I have a difference of how the date 1st Jan 0001 UTC is represented in Java and in Javascript
In Java:
TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
Calendar cal = Calendar.getInstance(utcTimeZone);
cal.clear();
//1st Jan 0001
cal.set(1, 0, 1);
Date date = cal.getTime();
System.out.println(date);//Sat Jan 01 00:00:00 GMT 1
System.out.println(date.getTime());// -62135769600000
In JavaScript:
var date = new Date();
date.setTime(-62135769600000);
date.toUTCString(); //"Sat, 30 Dec 0 00:00:00 GMT"
Why the date, 1 Jan 0001 UTC, that is represented by the time -62135769600000L in Java, is not represented as 1st of January when displayed in Javascript?
It looks like this is because GregorianCalendar in Java is actually a hybrid between the Gregorian calendar and the Julian calendar:
GregorianCalendar is a hybrid calendar that supports both the Julian and Gregorian calendar systems with the support of a single discontinuity, which corresponds by default to the Gregorian date when the Gregorian calendar was instituted (October 15, 1582 in some countries, later in others). The cutover date may be changed by the caller by calling setGregorianChange().
If you take 1500-01-01 for example, the Java and Javascript values will be 10 days apart.
To make it a pure GregorianCalendar, you can use this:
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.setGregorianChange(new Date(Long.MIN_VALUE));
then you get a value of -62135596800000 for 0001-01-01, which gives the same date for Javascript.
Cutover calendars are a pain in the neck - they make all kinds of things odd, and are almost never useful. (I suspect that use cases where they are appropriate may have different requirements, too. I decided not to implement it for Noda Time in the end :)
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*.
Solution using java.time, the modern Date-Time API:
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
public class Main {
public static void main(String[] args) {
System.out.println(OffsetDateTime.of(1, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC).toInstant().toEpochMilli());
}
}
Output:
-62135596800000
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
JavaScript:
const date = new Date();
date.setTime(-62135596800000);
console.log(date.toUTCString()); // Mon, 01 Jan 0001 00:00:00 GMT
* 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.
Related
I heard that Unix time does not include "Leap Second". And I also heard that Java Calendar API does not include Leap second.
Since 1972, 27 seconds were added as the Leap second. And Unix time began 1970-01-01 00:00:00 (UTC).
So, I thought that there are 27 seconds difference between current UTC time and Unix time.
To clarify my thought, I did some experiment like below. 1614766198 was a Unix time at 2021-03-03 10:10:00 (UTC+0)
import java.util.Calendar;
import java.util.TimeZone;
public class CanendarTest {
public static void main(String[] args) throws InterruptedException {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTimeInMillis(1614766198L * 1000);
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH));
System.out.println(cal.get(Calendar.DAY_OF_MONTH));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
}
}
The result of above code was
output
2021
2
3
10
9
58
Output seems like "2021-03-03 10:09:58".
So, My Question is that, Why Java Calendar API return 2 second difference from 1970-01-01 00:00:00 (UTC) not 27 second difference?
1614766198 was a Unix time at 2021-03-03 10:10:00 (UTC+0)
This is not correct. The following UNIX command
TZ=UTC date -r 1614766198
outputs
Wed 3 Mar 2021 10:09:58 UTC
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*.
Solution using java.time, the modern Date-Time API:
import java.time.Instant;
public class Main {
public static void main(String[] args) {
Instant instant = Instant.ofEpochSecond(1614766198);
System.out.println(instant);
}
}
Output:
2021-03-03T10:09:58Z
ONLINE DEMO
An Instant represents an instantaneous point on the timeline in UTC. The Z in the output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
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.
There is requirement to see if some date (ex: expiry date) is greater than or equal to today. Presently JODA time library has been used to achieve this simple comparison. Even some post are recommending that like this.
But recently found some problem with timezones. Date exists in PST and when converted to LocalDate following conversion comes false at 5:00 pm PST, when it should be true -
LocalDate now = LocalDate.fromDateFields(new Date()); // Current date in PST
LocalDate expiryDate = LocalDate.fromDateFields(expiresOn); // expiresOn is java.util.Date
boolean notExpired = expiryDate.isEqual(now) || expiryDate.isAfter(now);
When looked closely, LocalDate expiryDate was using UTC chronology. So at 5:00pm PST, when variable expiryDate contains contains "2021-01-16", variable now becomes "2021-01-17"
Please recommend, what is the better-way to deal with this problem.
I am trying to understand, what special advantages I might achieve by using joda time, because the same compassion can be done using SimpleDateFormatter.
The date-time API of java.util 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.
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.
Learn about the modern date-time API from Trail: Date Time.
LocalDate uses JVM's timezone by default
Whenever timezone is involved, make sure to specify the same while creating an instance of LocalDate. A LocalDate uses JVM's timezone by default and you should never compare a LocalDate from one timezone to that of another without converting both of them in the same timezone (the recommended one is UTC). Same is the case with LocalDateTime. Instead of using LocalDate, you should do all processing with objects which have both date and time (e.g. LocalDateTime) and if required you can derive the LocalDate from them.
Also, the java.util.Date object simply represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value.
Therefore, if you are deriving expiryDate from a java.util.Date object, it is essentially date-time in UTC.
You can convert now-in-PST and expiryDate into java.time.Instant and compare them. A java.time.Instant is an instantaneous point on the UTC time-line.
Demo using the modern date-time API:
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Calendar;
import java.util.Date;
public class Main {
public static void main(String[] args) {
LocalDateTime nowInPST = LocalDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println(nowInPST);
// Convert it to date in UTC
Instant nowInPSTConvertedToInstant = nowInPST.atZone(ZoneId.of("America/Los_Angeles"))
.withZoneSameInstant(ZoneId.of("Etc/UTC"))
.toInstant();
// Some java.util.Date
Calendar calendar = Calendar.getInstance();
calendar.set(2020, 0, 10, 10, 10, 10);
Date date = calendar.getTime();
Instant expiry = date.toInstant();
System.out.println(nowInPSTConvertedToInstant.isBefore(expiry));
}
}
Output:
2021-01-17T10:58:38.490041
false
Note: Check the following notice at the Home Page of Joda-Time
Joda-Time is the de facto standard date and time library for Java
prior to Java SE 8. Users are now asked to migrate to java.time
(JSR-310).
Simplify your expression
The following statement
boolean notExpired = expiryDate.isEqual(now) || expiryDate.isAfter(now);
can be simplified as
boolean notExpired = !expiryDate.isBefore(now);
You should consider two APIs:
Joda-Time that you have been using until now is a good library, but in maintenance mode.
The chief developer of Joda-Time, Stephen Colebourne, went on to develop java.time, the modern Java date and time API, drawing on lessons from good and not so good experiences from Joda-Time.
It’s not perfectly clear from your question. I am assuming that expiration has been recorded in UTC and appears to be one day early because it is looked at in Pacific Time. So I am showing you how to keep everything in UTC so comparisons make sense and are accurate.
Joda-Time
System.setProperty("user.timezone", "America/Vancouver");
Date expiresOn = new Date(1_610_841_600_000L); // Jan 17 UTC
System.out.println(expiresOn);
LocalDate now = LocalDate.now(DateTimeZone.UTC);
System.out.println(now);
LocalDate expiryDate = new DateTime(expiresOn, DateTimeZone.UTC).toLocalDate();
System.out.println(expiryDate);
boolean notExpired = expiryDate.isEqual(now) || expiryDate.isAfter(now);
System.out.println("Expired? " + (notExpired ? "No" : "Yes"));
Output when running now:
Sat Jan 16 16:00:00 PST 2021
2021-01-17
2021-01-17
Expired? No
The Joda-Time home page 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).
java.time
LocalDate now = LocalDate.now(ZoneOffset.UTC);
System.out.println(now);
LocalDate expiryDate = expiresOn.toInstant()
.atOffset(ZoneOffset.UTC)
.toLocalDate();
System.out.println(expiryDate);
boolean notExpired = expiryDate.isEqual(now) || expiryDate.isAfter(now);
System.out.println("Expired? " + (notExpired ? "No" : "Yes"));
2021-01-17
2021-01-17
Expired? No
A note on taste
My taste is for avoiding unnecessary negations in variable names (and elsewhere). I’d find it simpler to do:
boolean expired = expiryDate.isBefore(now);
System.out.println("Expired? " + expired);
Expired? false
Links
Joda-Time Home
Oracle tutorial: Date Time explaining how to use java.time.
I'm using the SimpleDateFormat object with the Date object as shown below. The problem lis that the Date object shows the wrong date, which is a few minutes off from the original string. The Date object appears to store the time in total milliseconds in the debugger.
Any ideas on the problem?
import java.text.SimpleDateFormat;
import java.util.Date;
Date played_at_local;
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSSZ");
played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-05:00");
//played_at_local shows "Mon Apr 11 22:35:29 America/Chicago 2011" in debugger
Try removing the fractional seconds from the format string. I just ran into the same issue, but with a slightly different format. My input format wasn't in ISO format (no "T", and no "Z"), but the symptom was the same -- time was off by some random number of minutes and seconds, but everything else was fine. This is what my log results looked like:
When using the fractional second format:
SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
# Parsed date: 2011-05-27 17:11:15.271816 => Fri May 27 17:15:46 EDT 2011
# Parsed date: 2011-05-27 17:09:37.750343 => Fri May 27 17:22:07 EDT 2011
# Parsed date: 2011-05-27 17:05:55.182921 => Fri May 27 17:08:57 EDT 2011
# Parsed date: 2011-05-27 16:55:05.69092 => Fri May 27 16:56:14 EDT 2011
# Parsed date: 2011-05-27 16:38:35.50348 => Fri May 27 16:39:25 EDT 2011
I fixed it by removing the fractional seconds from the format.
SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
# Parsed date: 2011-05-27 17:11:15.271816 => Fri May 27 17:11:15 EDT 2011
# Parsed date: 2011-05-27 17:09:37.750343 => Fri May 27 17:09:37 EDT 2011
# Parsed date: 2011-05-27 17:05:55.182921 => Fri May 27 17:05:55 EDT 2011
# Parsed date: 2011-05-27 16:55:05.69092 => Fri May 27 16:55:05 EDT 2011
# Parsed date: 2011-05-27 16:38:35.50348 => Fri May 27 16:38:35 EDT 2011
What I think is happening is that my "fractional seconds" part of the input string is too long (the same is true in the OP example). It appears to be expecting only three decimal places. If you do the math (take the first example):
fractional seconds = 0.271816 seconds
What DateFormat sees is 271816 / 1000 of a second
271816 / 1000 == 271 seconds
271 / 60 = 4 minutes
271 % 60 = 31 seconds
17:11:15 to 17:15:46 is exactly 4 minutes, 31 seconds off
Try this, working for me Z should be useed in date, or rmove from Format String
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSS'Z'");
played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726Z-05:00");
There are three major problems in your code:
You have used .SSSSSS for the fraction of a second whereas the SimpleDateFormat does not support a precision beyond milliseconds (.SSS). It also means that you need to limit the digits in the fraction of a second to three.
You have used Z to parse the timezone offset, -05:00 whereas the correct pattern for this is XXX.
You have used hh for a time in 24-Hour format whereas the correct pattern for this is HH. The symbol, hh is used for a time in 12-Hour (i.e. with am/pm) format.
Apart from this, I recommend you always use Locale with a date parsing/formatting API because parts of a date-time string are represented in different ways in different Locales.
Demo:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ENGLISH);
Date date = sdf.parse("2011-04-11T22:27:18.491-05:00");
// Print the default string i.e. Date#toString
System.out.println(date);
// Print the date-time in a custom format
sdf.setTimeZone(TimeZone.getTimeZone("GMT-05:00"));
System.out.println(sdf.format(date));
}
}
Output:
Tue Apr 12 04:27:18 BST 2011
2011-04-11T22:27:18.491-05:00
Some facts about legacy date-time API:
The java.util.Date object is not a real date-time object like the modern date-time types; rather, it represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value. If you need to print the date-time in a different timezone, you will need to set the timezone to SimpleDateFormat and obtain the formatted string from it.
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* .
Using modern date-time API:
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
OffsetDateTime odt = OffsetDateTime.parse("2011-04-11T22:27:18.491726-05:00");
// Print the default string i.e. OffsetDateTime#toString
System.out.println(odt);
// Print the date-time in a custom format. Note: OffsetDateTime#toString drops
// seconds if it is zero
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSSSSXXX");
System.out.println(dtf.format(odt));
}
}
Output:
2011-04-11T22:27:18.491726-05:00
2011-04-11T22:27:18.491726-05:00
Note: For DateTimeFormatter, the symbol, u means year whereas the symbol, y means year-of-era. It doesn't make any difference for a year in the [AD][2] era, but it matters for a year in the BC era. Check this answer to learn more about it.
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.
05:00 -->> 0500
and
hh --> HH // error not because of this ,but date is in 24hr format.
played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-05:00");
should be
played_at_local = dateFormat.parse("2011-04-11T22:27:18.491726-0500");
You could try this method:
http://docs.oracle.com/javase/6/docs/api/java/sql/Timestamp.html#valueOf(java.lang.String)
The key thing is that fractional digits are optional and you can use a variable number of them. However, this does not seem to account for the time zone.
From the docs:
valueOf
public static Timestamp valueOf(String s)
Converts a String object in JDBC timestamp escape format to a Timestamp value.
Parameters:
s - timestamp in format yyyy-mm-dd hh:mm:ss[.f...]. The fractional seconds may be omitted.
Returns:
corresponding Timestamp value
Throws:
IllegalArgumentException - if the given argument does not have the format yyyy-mm-dd hh:mm:ss[.f...]
Try this :
dTime = new SimpleDateFormat("HH:mm:ss:SS");
String sTime = (dTime.format(new java.util.Date())).toString();
Hope this help
java.time and ThreeTenABP
There is no way that SimpleDateFormat can parse your datetime string correctly. On the other hand java.time, the modern Java date and time API, supports your format out of the box.
import org.threeten.bp.OffsetDateTime;
String dateTimeString = "2011-04-11T22:27:18.491726-05:00";
OffsetDateTime playedAtLocal = OffsetDateTime.parse(dateTimeString);
System.out.println("Parsed into " + playedAtLocal);
Output is:
Parsed into 2011-04-11T22:27:18.491726-05:00
SimpleDateFormat only supports milliseconds, exactly three decimals on the seconds, not two, not four, not six (admittedly I’m unsure whether some Android versions have a version of SimpleDateFormat that can do better, but your question shows that your version cannot). SimpleDateFormat is also notoriously troublesome and long outdated, so you wouldn’t want to use it anyway.
java.time is so much nicer to work with. You notice that we didn’t even need an explicit formatter and thus didn’t need to write a format pattern string, which is always an error-prone task. Your date time string is in ISO 8601 format, and java.time classes parse ISO 8601 as their default.
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 Java 6 and 7 get the ThreeTen Backport, the backport of the modern 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.
Wikipedia article: ISO 8601
I have an extensive DATE-TIME conversion class, but i came across a scenario that i cannot resolve:
I have a java.util.date: Tue May 10 00:00:00 BST 2011
I have a java.sql.time: 03:58:44
I need to create a java.util.date: Tue May 10 03:58:44 BST 2011
The only approach i came up with is:
public static Date getDate(Date date, Time time) {
Calendar calendar=Calendar.getInstance();
calendar.set(date.getYear(), date.getMonth(), date.getDay(), time.getHours(), time.getMinutes(), time.getSeconds());
return calendar.getTime();
}
Totally deprecated code, and does not work:
java.lang.IllegalArgumentException at java.sql.Time.getYear(Unknown Source)
Any ideas?
java.sql.Time is just a wrapper over the java.util.Date. You can use it as if you would add two java.util.Date objects.
For example, set Calendar to java.sql.Time:
calendar.setTime(time);
Now extract the hour/minute/seconds fields, i.e.:
calendar.get(Calendar.HOUR);
Next, set the Calendar to java.util.Date object and add these three fields to its time, i.e.:
calendar.add(Calendar.HOUR, hour);
And get the Date back:
calendar.getTime();
Easiest way would be to just add the milli secs together to create a new date, ala
public static Date getDate(Date date, Time time) {
return new Date(date.getTime() + time.getTime())
}
vickirk's solution wasn't so bad, but has timezone issues, which results in the one hour less you observed.
I suppose, BST means British Summer Time, which is GMT +0100. Now, java.util.Date and its descendants internally work with numbers of milliseconds since midnight Jan 01, 1970 GMT. The timezone is not taken into account until you stringfy the date/time with toString(). And they use your local timezone for that, which is BST, apparently. That means, what is really stored in these objects, is
java.util.date: Mon May 09 23:00:00 GMT 2011
java.sql.time: 02:58:44 GMT
When you add the internal values (which are retrieved by getTime()) like vickirk suggested, you obtain a date which contains
Tue May 10 01:58:44 GMT 2011, which then results in
Tue May 10 02:58:44 BST 2011 on stringification.
So the explanation for the one hour less is that the timezone offset applies twice, when you stringified the values separately, whereas it applies only once after the addition, because you stringfy only once now. Or, from another point of view, adding the internal value of the point in time 03:58:44 BST is equivalent to adding a time span of 2h 58m 44s.
So to get a time span of 3h 58m 44s encoded in a java.sql.Time, you have to make up for the time zone offset manually. You do that by parsing the time string "00:00:00" with java.sql.Time, which will result in an internal value of -3600000 which is equivalent to 31 Dec 1969 23:00:00 GMT, i.e. one hour before the epoch. This is the negative of the time zone offset.
public static Date mergeDate(Date date, Time time) {
long tzoffset = -(Time.valueOf("00:00:00").getTime());
return new Date(date.getTime() + time.getTime() + tzoffset);
}
Of course, all this is a dirty hack, which is necessary because you insist on interpreting the Time's value as a time span, while it really is a point in time.
Instead, you can use this.
public static Date getDate(Date date, Time time) {
Calendar calendar=Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
calendar.setTime(date);
calendar.add(Calendar.MILLISECOND, (int) time.getTime());
return calendar.getTime();
}
Can you do
java.util.Date newDate = new java.util.Date(sqlDate.getTime());
try these
public static Date getDate(Date date, Time time) {
Calendar calendar=Calendar.getInstance();
calendar.setTime(date);
Calendar calendar1=Calendar.getInstance();
calendar1.setTime(time);
calendar.set(Calendar.MINUTE, calendar1.get(Calendar.MINUTE));
calendar.set(Calendar.SECOND, calendar1.get(Calendar.SECOND));
calendar.set(Calendar.HOUR_OF_DAY, calendar1.get(Calendar.HOUR_OF_DAY));
return calendar.getTime();
}
I recommend that you use java.time, the modern Java date and time API, for your date and time work. If you cannot avoid getting the date as a java.util.Date (a class that doesn’t represent a date) and your time of day as a java.sql.Time, convert both to modern types and combine them from there.
Java 8 and later
// Time zone to use throughout
ZoneId zone = ZoneId.systemDefault();
// Initialize values to be used for demonstration
Instant startOfDay = LocalDate.of(2011, Month.MAY, 10)
.atStartOfDay(zone)
.toInstant();
Date date = Date.from(startOfDay);
Time time = Time.valueOf(LocalTime.of(3, 58, 44));
// Do work
LocalTime localTime = time.toLocalTime();
LocalDateTime combination = date.toInstant()
.atZone(zone)
.toLocalDate()
.atTime(localTime);
// Print result
System.out.println(combination);
Output:
2011-05-10T03:58:44
Only if you indispensably need a Date, typically for a legacy API that you cannot afford to upgrade to java.time just now, convert back:
Instant inZone = combination.atZone(zone).toInstant();
Date oldfashionedDate = Date.from(inZone);
System.out.println(oldfashionedDate);
In my time zone the output is:
Tue May 10 03:58:44 CEST 2011
Java 6 and 7
For Java 6 and 7 use the backport of java.time, ThreeTen Backport (links at the botton). For the backport we need to use DateTimeUtils for converting to modern types:
LocalTime localTime = DateTimeUtils.toLocalTime(time);
LocalDateTime combination = DateTimeUtils.toInstant(date)
.atZone(zone)
.toLocalDate()
.atTime(localTime);
Output is the same as before. Also if you need to convert back to a Date, use DateTimeUtils:
Instant inZone = combination.atZone(zone).toInstant();
Date oldfashionedDate = DateTimeUtils.toDate(inZone);
Again output is the same as before.
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).
Java 8+ APIs available through desugaring
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
I have a string from an email header, like Date: Mon, 27 Oct 2008 08:33:29 -0700. What I need is an instance of GregorianCalendar, that will represent the same moment. As easy as that -- how do I do it?
And for the fastest ones -- this is not going to work properly:
SimpleDateFormat format = ... // whatever you want
Date date = format.parse(myString)
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(date)
because it will normalize the timezone to UTC (or your local machine time, depending on Java version). What I need is calendar.getTimeZone().getRawOffset() to return -7 * milisInAnHour.
I'd recommend looking into the Joda Time library, if that's an option. I'm normally against using a third-party library when the core platform provides similar functionality, but I made this an exception because the author of Joda Time is also behind JSR310, and Joda Time is basically going to be rolled into Java 7 eventually.
http://joda-time.sourceforge.net/
So anyway, if Joda Time is an option, something like this should work:
DateTimeFormatter formatter =
DateTimeFormat.forPattern("your pattern").withOffsetParsed();
DateTime dateTime = formatter.parseDateTime("your input");
GregorianCalendar cal = dateTime.toGregorianCalendar();
I hope this helps.
And for the fastest ones -- this is not going to work properly ...
because it will normalize the timezone to UTC (or your local machine time, depending on Java version). What I need is calendar.getTimeZone().getRawOffset() to return -7 * milisInAnHour.
Well technically this does work, because while it will return an object with TimeZone equal to the current system TimeZone, the time will be modified to account for the offset.
This code:
String dateString = "Mon, 27 Oct 2008 08:33:29 -0700";
DateFormat df = new SimpleDateFormat("E, dd MMM yyyy hh:mm:ss Z");
Date parsed = df.parse(dateString);
System.out.println("parsed date: " + parsed);
Calendar newCalendar = Calendar.getInstance();
newCalendar.setTime(parsed);
outputs:
parsed date: Mon Oct 27 11:33:29 EDT 2008
which technically is correct, since my system timezone is EDT / UTC minus four hours (which is three hours ahead of yours). If you express time as the number of milliseconds since January 1, 1970, 00:00:00 GMT (which is how the Date object stores it's date/time), then these date/times are equal, it's just the TimeZone that is different.
Your issue is really How do I convert a Date/Calendar into my timezone? For that, take a look at my response to the previous question How to handle calendar TimeZones using Java?
java.time
Solution using java.time, the modern date-time API:
The modern date-time API offers OffsetDateTime to represent a date-time object with a timezone offset. It can be converted to Instant which represents an instantaneous point on the timeline. An Instant is independent of any timezone i.e. it has a timezone offset of +00:00 hours, designated as Z in the ISO 8601 standards.
Instant#toEpochMilli converts this instant to the number of milliseconds from the epoch of 1970-01-01T00:00:00Z. This value can be set into an object of GregorianCalendar which will then represent the same moment.
Demo:
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
String strDateTime = "Mon, 27 Oct 2008 08:33:29 -0700";
OffsetDateTime odt = OffsetDateTime.parse(strDateTime, DateTimeFormatter.RFC_1123_DATE_TIME);
System.out.println(odt);
// In case you want a time zone neutral object, convert to Instant
Instant instant = odt.toInstant();
System.out.println(instant);
// Edit: If the requirement is a GregorianCalendar having the offset from
// the string — typically for an old API not yet upgraded to java.time:
ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, DateTimeFormatter.RFC_1123_DATE_TIME);
GregorianCalendar gc = GregorianCalendar.from(zdt);
System.out.println("As Date: " + gc.getTime());
System.out.println("Time zone ID: " + gc.getTimeZone().getID());
System.out.println("Hour of day: " + gc.get(Calendar.HOUR_OF_DAY));
// ...
}
}
Output:
2008-10-27T08:33:29-07:00
2008-10-27T15:33:29Z
As Date: Mon Oct 27 15:33:29 GMT 2008
Time zone ID: GMT-07:00
Hour of day: 8
Calling getTime() on the GregorianCalendar converts to a Date (another old and error-prone class) which doesn’t have a time zone, so the offset is lost. Printing the time zone ID and the hour of day demonstrates that both offset and time of day are preserved in the GregorianCalendar.
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.