Java TimeZone conversions - java

I understand that java Date is timezoneless and trying to set different timezone on Java Calendar wouldn't convert date to an appropriate Time Zone. So I have tried following code
public static String DATE_FORMAT="dd MMM yyyy hh:mm:ss";
public static String CURRENT_DATE_STRING ="31 October 2011 14:19:56 GMT";
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(dateFormat.parseObject(CURRENT_DATE_STRING));
but it outputs wrong date Mon Oct 31 16:19:56 when it must be 12:19:56?

The main issue here is your date format string is using hh (12-hour clock) instead of HH (24-hour)
Secondly, your date format should specify that your date string contains the timezone.
(Alternatively you could uncomment the commented line, to tell it the correct timezone).
Thirdly, you should use a DateFormat to output the time to screen aswell...
Finally, UTC = GMT, so the UTC time is also 14:19:56
(GMT, 'British Winter Time', is the same as UTC, whereas BST is one hour ahead)
public class DateFormatTest {
public static String DATE_FORMAT="dd MMM yyyy HH:mm:ss z";
public static String CURRENT_DATE_STRING ="31 October 2011 14:19:56 GMT";
public static void main(String[] args) throws ParseException {
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
//dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
Date d= dateFormat.parse(CURRENT_DATE_STRING);
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(dateFormat.format(d));
}
}
Output: 31 Oct 2011 14:19:56 UTC
HTH

tl;dr
On that date, at that time, in some eastern Europe time zones, the clocks were running two hours ahead of UTC. So the hour of 14 in UTC (GMT) will appear as 16 (not 12) in zones such as Europe/Helsinki.
ZonedDateTime
.parse
(
"31 October 2011 14:19:56 GMT" ,
DateTimeFormatter.ofPattern
(
"dd MMMM uuuu HH:mm:ss z" ,
Locale.US
)
)
.withZoneSameInstant
(
ZoneId.of( "Europe/Helsinki" )
)
.toString()
2011-10-31T16:19:56+02:00[Europe/Helsinki]
java.time
I understand that java Date is timezoneless
Actually, a java.util.Date represents a moment as seen in UTC, an offset of zero hours-minutes-seconds.
Beware of Date::toString. That terrible toString method dynamically applies the JVM’s current default time zone while generating its text. This creates an illusion of that time zone having been part of the object. One of many reasons to never use this class.
trying to set different timezone on Java Calendar wouldn't convert date to an appropriate Time Zone
You should be using the modern java.time classes, never Calendar. Specifically, use ZonedDateTime to represent a moment as seen through the wall-clock time used by the people of a particular region (a time zone).
"31 October 2011 14:19:56 GMT";
Your input of "31 October 2011 14:19:56 GMT" does not match your formatting pattern "dd MMM yyyy hh:mm:ss". That pattern fails to account for the offset of your input, an offset of zero hours-minutes-seconds indicated by the GMT at the end.
Firstly, do not exchange date-time values using such formats. Learn to use ISO 8601 standard formats for exchanging date-time values as text. The java.time classes conveniently use these standard formats by default when parsing/generating text, so no need to specify a pattern at all.
But if you must parse that particular input string of yours, define a formatting pattern to match.
String input = "31 October 2011 14:19:56 GMT";
Locale locale = Locale.US;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd MMMM uuuu HH:mm:ss z" , locale );
ZonedDateTime zdt = ZonedDateTime.parse( input , f );
Notice how we specified a Locale, to determine the human language and cultural norms used in translation of your input.
EEST – Eastern European Summer Time
Apparently you want to view this moment as seen in the time zone of eastern Europe. I will arbitrarily choose one of the several time zones in that area.
Be aware that EEST is not a real time zone. Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST or EEST as they are not true time zones, not standardized, and not even unique(!).
ZoneId zoneHelsinki = ZoneId.of( "Europe/Helsinki" );
ZonedDateTime zdtHelsinki = zdt.withZoneSameInstant( zoneHelsinki );
Dump to console.
System.out.println( "zdt = " + zdt );
System.out.println( "zdtHelsinki = " + zdtHelsinki );
zdt = 2011-10-31T14:19:56Z[GMT]
zdtHelsinki = 2011-10-31T16:19:56+02:00[Europe/Helsinki]
Notice how the hour changed from 14 to 16 because at that moment the clocks in Finland were running two hours ahead of UTC.
but it outputs wrong date Mon Oct 31 16:19:56 when it must be 12:19:56?
No the hour 14 is in UTC. Eastern Europe runs ahead of UTC, not behind it. As seen above, Finland on that day was running two hours ahead, so the little hand on the clocks of Finland were pointing at 4 (16 hour) rather than 12.
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….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Use Joda Time. It's recommended by many StackOverflow users and is well documented with examples on timezone conversion.
Good luck!

What's the whole output? Date.toString() should print time zone. Maybe it's not in UTC in your case.

Related

Date.toString() adds an hour around epoch in London

How can I get Date.toString() to produce an output that SimpleDateFormat can parse correctly for Dates around 1 Jan 1970 (I assume this applies to winter of 1968 and 1969 as well)
If I run the following,
System.out.println(TimeZone.getDefault());
Date date = new Date(0);
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
Date date2 = sdf.parse(date.toString());
System.out.println("date: " + date);
System.out.println("date2: " + date2);
Date date3 = sdf.parse(date2.toString());
System.out.println("date3: " + date3);
This prints
sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=3600000,useDaylight=true,transitions=242,lastRule=java.util.SimpleTimeZone[id=Europe/London,offset=0,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
date: Thu Jan 01 01:00:00 GMT 1970
date2: Thu Jan 01 02:00:00 GMT 1970
date3: Thu Jan 01 03:00:00 GMT 1970
The problem is that London was in BST on 1 Jan 1970. So the correct date should be either
date: Thu Jan 01 01:00:00 BST 1970
or
date: Thu Jan 01 00:00:00 GMT 1970
but it seems a confusion of the two.
And while I would love to not support java.util.Date, it's not an option for me.
tl;dr
Your input is invalid as BST (British Summer Time) was not in effect during the winter.
BST cannot be reliably parsed, as it is a non-standard non-unique pseudo-zone.
There is no need to mess around with SimpleDateFormat. Let the modern java.time classes do the heavy lifting.
And while I would love to not support java.util.Date, it's not an option for me.
At the edges of your code, convert to-from the legacy and modern classes.
// Convert from legacy to modern.
Instant instant = myJavaUtilDate.toInstant() ;
// Convert from modern to legacy.
java.util.Date myJavaUtilDate = Date.from( instant ) ;
No BST in winter
Apparently the “B” in your BST is meant to be British. But BST in that context means British Summer Time. This means Daylight Saving Time (DST) which is engaged in the summer time, not the winter. So your input string of a January date combined with BST is nonsensical.
Double-Summertime
There is a further complication to your example of a moment in 1970 with a British time zone.
The practice of DST in Britain using an offset of one hour ahead of UTC (+01:00) in summer, and an offset of zero (+00:00) in the winter for Standard Time is current practice. That has not always been the case.
Back in 1970, Britain was trialling a “double-summertime”. In that experiment of 1968-1971, winter time was one hour ahead of UTC rather than zero, and summer time was two hours ahead of UTC instead of the one hour used nowadays. This put British time more in common with continental Europe and was hoped to reduce accidents.
So if we adjust a moment in January of 1970, we expect to jump to one hour ahead for time zone Europe/London. Whereas a moment in January of 2019, we expect no jump, the time-of-day in Britain will be the same as UTC (an offset-from-UTC of zero hours-minutes-seconds).
Avoid pseudo-zones
Avoid these 2-4 character pseudo-zones such as BST. They are not standardized. They are not even unique! So BST can be interpreted to be the time zone Pacific/Bougainville just as well as British Summer Time.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as BST or EST or IST as they are not true time zones, not standardized, and not even unique(!).
Convert
You can convert between the legacy and modern date-time classes easily. New conversion methods have been added to the old classes. Look for from, to, and valueOf methods, per the naming conventions.
java.util.Date ⇄ java.time.Instant
java.util.GregorianCalendar ⇄ java.time.ZonedDateTime
java.sql.Date ⇄ java.time.LocalDate
java.sql.Timestamp ⇄ java.time.Instant
Converting
Your input string of 00:00 on January 1, 1970 happens to be the epoch reference date used by both the legacy and modern date-time classes. We have a constant for that value.
Instant epoch = Instant.EPOCH ;
instant.toString(): 1970-01-01T00:00:00Z
See that same moment through your time zone of Europe/London.
ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = epoch.atZone( z ) ;
zdt.toString(): 1970-01-01T01:00+01:00[Europe/London]
Notice that above-mentioned Double-Summertime experiment in effect then. If we try the same code for 2019, we get an offset-from-UTC of zero.
ZonedDateTime zdt2019 =
Instant
.parse( "2019-01-01T00:00Z" )
.atZone( ZoneId.of( "Europe/London" ) )
;
zdt2019.toString(): 2019-01-01T00:00Z[Europe/London]
To convert to a java.util.Date, we need an java.time.Instant object. An Instant represents a moment in UTC. We can extract an Instant from our ZonedDateTime object, effectively adjusting from a zone to UTC. Same moment, different wall-clock time.
Instant instant = zdt.toInstant():
We should now be back where we started, at the epoch reference date of 1970-01-01T00:00:00Z.
instant.toString(): 1970-01-01T00:00:00Z
To get the java.util.Date object you may need to interoperate with old code not yet updated to java.time classes, use the new Date.from method added to the old class.
java.util.Date d = Date.from( instant ) ; // Same moment, but with possible data-loss as nanoseconds are truncated to milliseconds.
d.toString(): Thu Jan 01 00:00:00 GMT 1970
By the way, be aware of possible data-loss when converting from Instant to Date. The modern classes have a resolution of nanoseconds while the legacy classes use milliseconds. So part of your fractional second may be truncated.
See all the code above run live at IdeOne.com.
To convert the other direction, use the Date::toInstant method.
Instant instant = d.toInstant() ;
ISO 8601
Avoid using text in custom formats for exchanging date-time values. When serializing date-time values as human-readable text, use only the standard ISO 8601 formats. The java.time classes use these formats by default.
Those strings you were experimenting with parsing are a terrible format and should never be used for data-exchange.
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.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
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….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I may cite https://bugs.openjdk.java.net/browse/JDK-6609362?jql=text%20~%20%22epoch%20gmt%22:
Please use Z to format and parse historical time zone offset changes
to avoid confusions with historical time zone name changes.
public class Test {
public static void main(String[] args) throws ParseException {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
SimpleDateFormat f = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
Date d = new Date(0);
for (int i = 0; i < 10; i++) {
String s = f.format(d);
System.out.println(s);
d = f.parse(s);
}
} } ```
This is why I hate the Date Library.
As implied, BST should be used during the summer, and the calendar defines it as such.
sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=3600000,useDaylight=true,transitions=242,lastRule=java.util.SimpleTimeZone[id=Europe/London,offset=0,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Except, for, TIL of The adventures of year-round British Summer Time!
A further inquiry during 1966–67 led the government of Harold Wilson to introduce the British Standard Time experiment, with Britain remaining on GMT+1 throughout the year. This took place between 27 October 1968 and 31 October 1971, when there was a reversion to the previous arrangement.
If you test dates around this period you will find the dates drifting off by an hour each parse, up to the switchover points.
The time code for Europe/London is GMT, with daylight savings using BST.
The toString method of Date "normalizes" the output by removing daylight savings time to pick what time zone to print. The options are GMT and BST. The Europe/London time of 01:00:00 printed as 01:00:00 GMT even though it is operating on GMT+1 time. So in other words, date.toString()does not work properly for this swath of time around the epoch because it uses GMT as a time zone for a time zone that is ostensibly not GMT/CET. The time itself is correct, but not the time zone.
The "simplest" solution I can come up with is relatively nasty from a sanity checkpoint, but can probably be made more elegant.
private static final Date experimentEnd = new Date(1971-1900, 11-1, 11);
private static final Date experimentStart = (new Date(1968-1900, 10-1, 26));
private static boolean bstExperimentTime(Date date) {
return date.after(experimentStart) && date.before(experimentEnd);
}
public static String forDateParsing(Date date) {
if(bstExperimentTime(date))
return date.toString().replace("GMT", "CET");
return date.toString();
}
public static String forDatePrinting(Date date) {
if(bstExperimentTime(date))
return date.toString().replace("GMT", "BST");
return date.toString();
}
Any date you need to parse with default "Europe/London" time zone needs to be passed through the parse formatter to convert GMT -> CET, which is the correct GMT+1.
Any date you need to print with default "Europe/London" time zone needs to be passed through the parse formatter to convert GMT -> BST, which is the correct display.

Java date parsing not working for ET timezone while working for IST

I am trying to parse the String to date. String having date format as
"dd-MMM-yyyy Z" and String having value "12-DEC-2018 ET". Its giving the error
java.text.ParseException: Unparseable date: "12-DEC-2018 ET".
The same code is working for String having value "12-DEC-2018 IST".
below is the code snippet:
public static void main(String[] args) throws ParseException {
String dateInputIST ="12-DEC-2018 IST";
String dateInputET ="12-DEC-2018 ET";
SimpleDateFormat sdfmt1 = new SimpleDateFormat("dd-MMM-yyyy Z");
SimpleDateFormat sdfmt2= new SimpleDateFormat("dd/MM/yyyy");
Date dDate = sdfmt1.parse( dateInputIST );
String strOutput = sdfmt2.format( dDate );
System.out.println(strOutput);
Date etDate = sdfmt1.parse(dateInputET);
strOutput = sdfmt2.format(etDate);
System.out.println(strOutput);
}
Could someone please help. I needed to parse the time in any timezone.
Thanks,
Navin
Change
String dateInputET ="12-DEC-2018 ET";
to
String dateInputET ="12-DEC-2018 EDT";
'ET' is not a recognized time zone.
Pseudo-zones
ET, EST, and IST are not actually time zones. Those 2-4 letter pseudo-zones are not standardized and are not even unique! For example, IST can mean India Standard Time, Ireland Standard Time, Iceland Standard Time, and more.
Real time zone names take the format of Continent/Region such as Africa/Tunis.
Date & zone, separately
Date with time zone has no real meaning.
Handle the date as a LocalDate object.
String input = "12-DEC-2018"
DayeTimeFormatter f = DateTimeFormatter.ofPattern( "dd-MM-uuuu" , Locale.US ) ;
LocalDate ld = LocalDate.parse( input , f ) ;
Handle your desired time zone separately, as a ZoneId object.
ZoneId zNewYork = ZoneId.of( "America/New_York" ) ;
To combine, determine the first moment of the day.
ZonedDateTime zdtNewYork = ld.atStartOfDay( z ) ;
Generate text representing that moment in standard ISO 8601 format extended to append the name of the time zone in square brackets.
To see that same moment in UTC, extract a Instant.
Instant instant = zdtNewYork.toInstant() ;
Adjust into another zone.
ZonedDateTime zdtKolkata = instant.atZone( ZoneId.of( "Asia/Kolkata" ) ) ;
To focus on the date only, get a LocalDate for the day of that same moment when viewed through the lens of the wall-clock time used in India.
LocalDate ldKolkata = zdtKolkata.toLocalDate() ;
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.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
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….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
java.time
DateTimeFormatter dateZoneFormatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("dd-MMM-uuuu v")
.toFormatter(Locale.ENGLISH);
String dateInputIST ="12-DEC-2018 IST";
String dateInputET ="12-DEC-2018 ET";
TemporalAccessor parsed = dateZoneFormatter.parse(dateInputIST);
System.out.println("Date: " + LocalDate.from(parsed) + " Time zone: " + ZoneId.from(parsed));
parsed = dateZoneFormatter.parse(dateInputET);
System.out.println("Date: " + LocalDate.from(parsed) + " Time zone: " + ZoneId.from(parsed));
On my computer the output from this snippet was:
Date: 2018-12-12 Time zone: Atlantic/Reykjavik
Date: 2018-12-12 Time zone: America/New_York
Format pattern letter v is for the generic time-zone name, that is, the name that is the same all year regardless of summer time (DST), for example Eastern Time or short ET.
If you want to control the interpretation of ambiguous time zone abbreviations (of which there are a lot), you may use the two-arg appendGenericZoneText​(TextStyle, Set<ZoneId>) where the second argument contains the preferred zones. Still better if there is a way for you to avoid relying on time zone abbreviations altogether since, as I said, they are very often ambiguous.
I am not sure what sense a date with a time zone makes, though.
As an additional point, always specify locale for your formatters so they will also work if the default locale is changed or one day your program runs in a JVM with a different default locale.
Avoid SimpleDateFormat and Date
I don’t think SimpleDateFormat will be able to parse your string. It’s just the same since that class is already long outdated and is renowned for being troublesome, so you should never want to use it anyway.

Why does util.Date forwards the date instead of subtracting it?

I am trying to convert IST to UTC epoch in Java
But instead of subtracting 5.30 hours from IST, it adds 5.30 in IST
public static long convertDateToEpochFormat(String date) {
Date convertedDate = null;
try {
LOGGER.info(date);
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
LOGGER.info(date);
convertedDate = formatter.parse(date);
LOGGER.info(convertedDate);
} catch (ParseException e) {
e.printStackTrace();
}
return convertedDate.getTime() / 1000L;
}
The log statements I obtained is :
2017-01-01 00:00:00
2017-01-01 00:00:00
Sun Jan 01 05:30:00 IST 2017
It should ideally be Dec 31 18:30:00 because of UTC conversion.
Can anyone tell me whats wrong ?
tl;dr
Why does util.Date forwards the date instead of subtracting it?
Because India time is ahead of UTC, not behind.
Instant.parse(
"2017-01-01 00:00:00".replace( " " , "T" ) + "Z"
).atZone(
ZoneId.of( "Asia/Kolkata" )
).toString()
2017-01-01T05:30+05:30[Asia/Kolkata]
Using java.time
You are using troublesome old date-time classes that are now legacy, supplanted by the java.time classes.
ISO 8601
Your input string is almost in standard ISO 8601 format. To comply fully, replace that SPACE in the middle with a T. The java.time classes use standard formats when parsing/generating strings. So no need to specify a formatting pattern.
String input = "2017-01-01 00:00:00".replace( " " , "T" ) ;
If that input is meant to represent a moment in UTC, append a Z, short for Zulu, means UTC.
String input = "2017-01-01 00:00:00".replace( " " , "T" ) + "Z" ; // Assuming this input was intended to be in UTC.
2017-01-01T00:00:00Z
When possible, just use the ISO 8601 formats in the first place when serializing date-time values to strings.
Instant
Parse that input string as an Instant, a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = Instant.parse( input ) ;
instant.toString(): 2017-01-01T00:00:00Z
ZonedDateTime
You seem to want this value adjusted into India time. Apply a ZoneId to get a ZonedDateTime.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString(): 2017-01-01T05:30+05:30[Asia/Kolkata]
See this code run live at IdeOne.com.
India time is ahead of UTC
Your Question expects the India time to go backwards, behind the UTC value. This makes no sense. India time is ahead of UTC, not behind UTC. The Americas have time zones behind UTC as they lay westward. East of the Prime Meridian in Greenwich are offsets ahead of UTC. In modern times, ISO 8601 and most other protocols mark such offsets with a plus sign: +05:30. Note that some old protocols did the opposite (used a negative sign).
Midnight UTC = 5:30 AM India
So midnight in UTC, 00:00:00 at the Prime Meridian, is simultaneously five-thirty in the morning in India.
So all three of these represent the same simultaneous moment, the same point in the timeline:
2017-01-01T00:00:00Z
2017-01-01T05:30+05:30[Asia/Kolkata]
2016-12-31T16:00-08:00[America/Los_Angeles]
Avoid count-from-epoch
Do not handle time as an integer count from epoch as you are doing by returning a long from your method as seen in the Question. In your Java code pass around date-time values using date-time objects, java.time objects specifically. When passing date-time values outside your Java code, serialize to strings using the practical ISO 8601 formats.
Relying on an integer count-from-epoch values is confusing, difficult to debug, impossible to read by humans, and will lead to frustration and errors (even worse: unobserved errors).
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.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
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
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
The answer by Basil Bourque is not only correct, it is also very informative. I have already upvoted it. I’ll try just a little bit of a different angle.
As I understand your question, your date-time string 2017-01-01 00:00:00 should be interpreted in IST, AKA Asia/Kolkata time, and you want to convert it to seconds (not milliseconds) since the epoch. And you are asking why you are getting an incorrect result.
I think the answer is rather banal: When the date-time string is in India time, you should not set UTC time on the formatter you use for parsing it. This is sure to get an incorrect result (if you were to format the date-time into UTC, you would do well in setting UTC as time zone on the formatter used for formatting, but this is a different story).
I agree with Basil Bourque that you should avoid the outdated classes Date and SimpleDateFormat. So here’s my suggestion (assuming you do need epoch seconds and cannot use an Instant as Basil Bourque recommends).
private static DateTimeFormatter parseFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
public static long convertDateToEpochFormat(String date) {
return LocalDateTime.parse(date, parseFormatter)
.atZone(ZoneId.of("Asia/Kolkata"))
.toInstant()
.getEpochSecond();
}
This will convert your example string into an instant of 2016-12-31T18:30:00Z and return 1483209000. Please check for yourself that it is correct.
I have been assuming all the way that by IST you meant Indian Standard Time. Please be aware that three and four letter time zone abbreviations are ambiguous. For example, my JVM thinks that IST means Israel Standard Time. If you intended the same, please substitute Asia/Jerusalem for Asia/Kolkata. If you meant Irish Standard Time (another recognized/semi-official interpretation), please use Europe/Dublin. You will of course get different output in each case.

Converting string to java.util.date

I am working with date strings that need to be converted to java.util.date objects.
I'm using the following code to do this:
public void setDates(String from, String to) throws ParseException
{
Date fromDate = new Date();
Date toDate = new Date();
SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
fromDate = df.parse(from);
toDate = df.parse(to);
this.setDepartDate(fromDate);
this.setReturnDate(toDate);
}
The problem is that the string values that I have to convert are always(And I have no control over this) in the following format: "20 September, 2013".
This causes my function to through a ParseException when it reaches fromDate = df.parse(from);
Could anyone help me understand why, and perhaps suggest a solution?
Check out the SimpleDateFormat JavaDocs for the available format options, but basically, you need to change your date format to something more like dd MMMM, yyyy
try {
String dateValue = "20 September, 2013";
SimpleDateFormat df = new SimpleDateFormat("dd MMMM, yyyy");
Date date = df.parse(dateValue);
System.out.println(date);
} catch (ParseException exp) {
exp.printStackTrace();
}
Which outputs...
Fri Sep 20 00:00:00 EST 2013
As per the javadoc use following format
SimpleDateFormat df = new SimpleDateFormat("dd MMMMM, yyyy");
Also decide if this parsing needs to be Lenient or not and if it needs to be strict use setLenient(false)
By default, parsing is lenient: If the input is not in the form used
by this object's format method but can still be parsed as a date, then
the parse succeeds. Clients may insist on strict adherence to the
format by calling setLenient(false).
Also note that SimpleDateFormat is not threadsafe. If there is a choice I recommend using Joda Time Library that provide much enhanced functionality.
You wrote
[...] in the following format: "20 September, 2013".
Then your SimpleDateFormat should be
"dd MMM, yyyy"
You can check out the SimpleDateFormat documentation.
When you parse a date, you need to know some context or use some assumptions. You can use SimpleDateFormat, but you may need to pre-parse the string to see which format it is before you use it. You may have to try multiple format to see if one or more way to parse the date.
BTW is 01/02/30 the 1st Feb 1930 or 2nd Jan 2030 or 30th feb 2001, you need to know something about what the date is likely to mean or have some control over the format.
LocalDate
The modern approach uses the java.time classes that supplanted the troublesome old date-time classes (Date, Calendar, etc.) years ago.
String input = "20 September, 2013" ;
Locale locale = Locale.US ; // Determines the human language and cultural norms used in parsing the input string.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "d MMMM, uuuu" , locale ) ;
LocalDate ld = LocalDate.parse( input , f ) ;
See this code run live at IdeOne.com.
ld.toString(): 2013-09-20
ZonedDateTime
If you want a time-of-day with that date, such as the first moment of the day, you must specify a time zone. A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment during runtime(!), so your results may vary. Better to specify your [desired/expected time zone][2] explicitly as an argument.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
Apply that ZoneId to get a ZonedDateTime. Never assume the day starts at 00:00:00. Anomalies such as Daylight Saving Time (DST) mean the day may start at another time such as 01:00:00. Let java.time determine the first moment of the day.
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
Instant
To adjust into UTC, extract an Instant.
Instant instant = zdt.toInstant() ;
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.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
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, and later
Built-in.
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
Much 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….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
You need to use following date pattern, dd MMMM, yyyy
Try this code,
String dateValue = "20 September, 2013";
// Type of different Month views
SimpleDateFormat sdf = new SimpleDateFormat("dd MMMM, yyyy"); //20 September, 2013
SimpleDateFormat sdf1 = new SimpleDateFormat("dd MM, yyyy"); //20 09, 2013
SimpleDateFormat sdf2 = new SimpleDateFormat("dd MMM, yyyy"); //20 Sep, 2013
Date date = sdf.parse(dateValue); // returns date object
System.out.println(date); // outputs: Fri Sep 20 00:00:00 IST 2013

Best way to extract TimeZone object from a String?

I have a database field that contains a raw date field (stored as character data), such as
Friday, September 26, 2008 8:30 PM Eastern Daylight Time
I can parse this as a Date easily, with SimpleDateFormat
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
Date scheduledDate = dbFormatter.parse(rawDate);
What I'd like to do is extract a TimeZone object from this string. The default TimeZone in the JVM that this application runs in is GMT, so I can't use .getTimezoneOffset() from the Date parsed above (because it will return the default TimeZone).
Besides tokenizing the raw string and finding the start position of the Timezone string (since I know the format will always be EEEE, MMMM dd, yyyy hh:mm aa zzzz) is there a way using the DateFormat/SimpleDateFormat/Date/Calendar API to extract a TimeZone object - which will have the same TimeZone as the String I've parsed apart with DateFormat.parse()?
One thing that bugs me about Date vs Calendar in the Java API is that Calendar is supposed to replace Date in all places... but then they decided, oh hey let's still use Date's in the DateFormat classes.
I found that the following:
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
dbFormatter.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
Date scheduledDate = dbFormatter.parse("Friday, September 26, 2008 8:30 PM Eastern Daylight Time");
System.out.println(scheduledDate);
System.out.println(dbFormatter.format(scheduledDate));
TimeZone tz = dbFormatter.getTimeZone();
System.out.println(tz.getDisplayName());
dbFormatter.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
System.out.println(dbFormatter.format(scheduledDate));
Produces the following:
Fri Sep 26 20:30:00 CDT 2008
Friday, September 26, 2008 08:30 PM Eastern Standard Time
Eastern Standard Time
Friday, September 26, 2008 08:30 PM Central Daylight Time
I actually found this to be somewhat surprising. But, I guess that shows that the answer to your question is to simply call getTimeZone on the formatter after you've parsed.
Edit:
The above was run with Sun's JDK 1.6.
#Ed Thomas:
I've tried something very similar to your example and I get very different results:
String testString = "Friday, September 26, 2008 8:30 PM Pacific Standard Time";
DateFormat df = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
System.out.println("The default TimeZone is: " + TimeZone.getDefault().getDisplayName());
System.out.println("DateFormat timezone before parse: " + df.getTimeZone().getDisplayName());
Date date = df.parse(testString);
System.out.println("Parsed [" + testString + "] to Date: " + date);
System.out.println("DateFormat timezone after parse: " + df.getTimeZone().getDisplayName());
Output:
The default TimeZone is: Eastern Standard Time
DateFormat timezone before parse: Eastern Standard Time
Parsed [Friday, September 26, 2008 8:30 PM Pacific Standard Time] to Date: Sat Sep 27 00:30:00 EDT 2008
DateFormat timezone after parse: Eastern Standard Time
Seems like DateFormat.getTimeZone() returns the same TimeZone before and after the parse()... even if I throw in an explicit setTimeZone() before calling parse().
Looking at the source for DateFormat and SimpleDateFormat, seems like getTimeZone() just returns the TimeZone of the underlying Calendar... which will default to the Calendar of the default Locale/TimeZone unless you specify a certain one to use.
I recommend checking out the Joda Time date and time API. I have recently been converted to a believer in it as it tends to be highly superior to the built-in support for dates and times in Java. In particular, you should check out the DateTimeZone class. Hope this helps.
http://joda-time.sourceforge.net/
http://joda-time.sourceforge.net/api-release/index.html
tl;dr
ZonedDateTime.parse(
"Friday, September 26, 2008 8:30 PM Eastern Daylight Time" ,
DateTimeFormatter.ofPattern( "EEEE, MMMM d, uuuu h:m a zzzz" )
).getZone()
java.time
The modern way is with the java.time classes. The Question and other Answers use the troublesome old legacy date-time classes or the the Joda-Time project, both of which are now supplanted by the java.time classes.
Define a DateTimeFormatter object with a formatting pattern to match your data.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "EEEE, MMMM d, uuuu h:m a zzzz" );
Assign a Locale to specify the human language of the name-of-day and name of month, as well as the cultural norms for other formatting issues.
f = f.withLocale( Locale.US );
Lastly, do the parsing to get a ZonedDateTime object.
String input = "Friday, September 26, 2008 8:30 PM Eastern Daylight Time" ;
ZonedDateTime zdt = ZonedDateTime.parse( input , f );
zdt.toString(): 2008-09-26T20:30-04:00[America/New_York]
You can ask for the time zone from the ZonedDateTime, represented as a ZoneId object. You can then interrogate the ZoneId if you need more info about the time zone.
ZoneId z = zdt.getZone();
See for yourself in IdeOne.com.
ISO 8601
Avoid exchanging date-time data in this kind of terrible format. Do not assume English, do not accessorize your output with things like the name-of-day, and never use pseudo-time-zones such as Eastern Daylight Time.
For time zones: Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
For serializing date-time values to text, use only the ISO 8601 formats. The java.time classes use these formats by default when parsing/generating strings to represent their value.
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.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Well as a partial solution you could use a RegEx match to get the timezone since you will always have the same text before it. AM or PM.
I don't know enough about Java timezones to get you the last part of it.
The main difference between Date and Calendar is, that Date is just a value object with no methods to modify it. So it is designed for storing a date/time information somewhere. If you use a Calendar object, you could modify it after it is set to a persistent entity that performs some business logic with the date/time information. This is very dangerous, because the entity has no way to recognize this change.
The Calendar class is designed for operations on date/time, like adding days or something like that.
Playing around with your example I get the following:
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class TimeZoneExtracter {
public static final void main(String[] args) throws ParseException {
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
System.out.println(dbFormatter.getTimeZone());
dbFormatter.parse("Fr, September 26, 2008 8:30 PM Eastern Daylight Time");
System.out.println(dbFormatter.getTimeZone());
}
}
Output:
sun.util.calendar.ZoneInfo[id="Europe/Berlin"...
sun.util.calendar.ZoneInfo[id="Africa/Addis_Ababa"...
Is this the result you wanted?
Ed has it right. you want the timeZone on the DateFormat object after the time has been parsed.
String rawDate = "Friday, September 26, 2008 8:30 PM Eastern Daylight Time";
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
Date scheduledDate = dbFormatter.parse(rawDate);
System.out.println(rawDate);
System.out.println(scheduledDate);
System.out.println(dbFormatter.getTimeZone().getDisplayName());
produces
Friday, September 26, 2008 8:30 PM Eastern Daylight Time
Fri Sep 26 20:30:00 CDT 2008
Eastern Standard Time

Categories

Resources