18

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.

matt b
  • 138,234
  • 66
  • 282
  • 345
Marcin
  • 7,874
  • 7
  • 45
  • 49
  • Minor aside: you should be using Calendar.getInstance() rather than instantiating GregorianCalendar directly. That remains flexible so that you don't have to change all your code when your app is run in India (e.g.). – Daniel Spiewak Oct 27 '08 at 16:39
  • 1
    @Daniel: Not necessarily. It depends on whether the app running in India is interfacing with Indians, ISO date formats, other un-localized processes, or what have you. – erickson Oct 27 '08 at 16:54
  • While I agree that you should use Calendar.getInstance (and you should use the generalized type Calendar, not GregorianCalendar)... getInstance() only returns a non-GregorianCalendar if your language/country is Thai/Thailand! – matt b Oct 27 '08 at 17:15

3 Answers3

15

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.

Jack Leow
  • 21,945
  • 4
  • 50
  • 55
  • If you are doing anything remotely complicated with date and time manipulation, and it sounds like you are, I also highly recommend the Joda Time library. It is one of the best designed and implemented libraries I have seen, and makes date/time processing as simple as possible (but not simpler). – Dov Wasserman Oct 28 '08 at 04:28
  • 1
    Hey Jack, this is a good answer! It just has one bug -- here's the right line. Update and I'll accept it. DateTimeFormat.forPattern("your pattern").withOffsetParsed(); – Marcin Oct 28 '08 at 09:57
6

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?

Community
  • 1
  • 1
matt b
  • 138,234
  • 66
  • 282
  • 345
3

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.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110