Java Date forcing BST "timezone" - java

In JAVA, how can I make sure that all dates are returned as GMT dates ?
For example, even if I try to force a DateFormat with GMT locale, it's applying some sort of logic that retrieves a BST date.
public static void main(String[] args) throws ParseException {
DateFormat dd = new SimpleDateFormat("MMM dd HH:mm:ss zzz yyyy");
dd.setTimeZone(TimeZone.getTimeZone("GMT"));
Date parse = dd.parse("Out 29 23:00:00 GMT 2011");
Date parse2 = dd.parse("Out 30 23:00:00 GMT 2011");
System.out.println(parse); // Prints "Sun Oct 30 00:00:00 BST 2011"
System.out.println(parse2); // Prints "Sun Oct 30 23:00:00 GMT 2011"
System.out.println(Locale.getDefault()); // Prints "en_US"
System.out.println(TimeZone.getDefault().getID()); // Prints "Europe/London"
}
Where is the BST coming from ? Is it related to daylight saving times ? TimeZone class says otherwwise.
System.out.println(TimeZone.getTimeZone("GMT").inDaylightTime(parse)); // Prints "false"
System.out.println(TimeZone.getTimeZone("GMT").inDaylightTime(parse2)); // Prints "false"
The default system locale is en_US.
Edit: Based on Basil Bourque's response, I can get both prints to GMT dates, if I change the default time zone to GMT:
TimeZone.setDefault(TimeZone.getTimeZone("GMT"));

Mystery
Are you sure your comments on both System.out.println lines are correct? I expect output from both lines to have the same time zone, either BST or GMT.
If you are certain those are correct, please post a complete example of working code. Also document your default locale and time zone.
Fully Working Example
Here is my version of your code transformed into a fully working example. I deduce from the BST and out that this is Portuguese Brazil locale.
java.util.Locale.setDefault( new Locale.Builder().setLanguage( "pt" ).setRegion( "BR" ).build() ); // **HACK* Think twice before ever setting the default of your JVM’s locale or time zone. Generally a bad idea.
java.text.DateFormat dd = new java.text.SimpleDateFormat( "MMM dd HH:mm:ss zzz yyyy" );
dd.setTimeZone( java.util.TimeZone.getTimeZone( "GMT" ) );
Date parse = null;
Date parse2 = null;
try {
parse = dd.parse( "Out 29 23:00:00 GMT 2011" );
parse2 = dd.parse( "Out 30 23:00:00 GMT 2011" );
} catch ( ParseException ex ) {
Logger.getLogger( JodaTimeWork.class.getName() ).log( Level.SEVERE , null , ex );
}
System.out.println( parse );
System.out.println( parse2 );
My output when running on a US locale and America/Los_Angeles time zone, thus the PDT time zone.
Sat Oct 29 16:00:00 PDT 2011
Sun Oct 30 16:00:00 PDT 2011
No Time Zone On A Date Object
Be aware that a java.util.Date object has no time zone assigned†. Confusingly, the toString method implementation on that class applies the JVM’s current default time zone. So it seems like the Date object has a time zone, but it does not.
As the correct comment by GriffeyDog said, the DateFormat object has a time zone but the Date object does not.
So I expect both of your System.out.println lines to emit text with the same time zone, as I said up top.
Joda-Time | java.time
This confusing handling of time zones is one of many reasons to avoid java.util.Date/.Calendar & SimpleTextFormat. Those in the know use either the Joda-Time library or the new java.time package built into Java 8. The java.time package was inspired by Joda-Time but is re-architected; each has its strengths and weaknesses.
Example in Joda-Time
Here is an example in Joda-Time 2.7.
Time Zone on DateTime
A DateTime object in Joda-Time knows its own assigned time zone, unlike a java.util.Date object.
Incorrect Localization
Your input data’s use of an uppercase O for Out appears to be incorrect for Portuguese convention. My example corrects that to be lowercase. Joda-Time rejects the uppercase as invalid.
Code
String input1 = "out 29 23:00:00 GMT 2011";
String input2 = "out 30 23:00:00 GMT 2011";
Locale locale_pt_BR = new Locale.Builder().setLanguage( "pt" ).setRegion( "BR" ).build(); //
DateTimeFormatter formatter = DateTimeFormat.forPattern( "MMM dd HH:mm:ss 'GMT' yyyy" ).withLocale( locale_pt_BR ).withZone( DateTimeZone.UTC );
DateTime dateTime1 = null;
DateTime dateTime2 = null;
DateTime dateTime1_Sao_Paulo = null;
DateTime dateTime2_Sao_Paulo = null;
try {
dateTime1 = formatter.parseDateTime( input1 );
dateTime2 = formatter.parseDateTime( input2 );
// Adjust to "America/Sao_Paulo" time zone.
DateTimeZone zone_Sao_Paulo = DateTimeZone.forID( "America/Sao_Paulo" );
dateTime1_Sao_Paulo = dateTime1.withZone( zone_Sao_Paulo );
dateTime2_Sao_Paulo = dateTime2.withZone( zone_Sao_Paulo );
} catch ( IllegalArgumentException e ) {
// … Handle exception.
System.out.println( "ERROR - Unexpected input for parsing into a date-time object." );
}
Dump to console.
System.out.println( "dateTime1 : " + dateTime1 );
System.out.println( "dateTime2 : " + dateTime2 );
System.out.println( "Adjusted to America/Sao_Paulo: " + dateTime1_Sao_Paulo + " & " + dateTime2_Sao_Paulo );
When run.
dateTime1 : 2011-10-29T23:00:00.000Z
dateTime2 : 2011-10-30T23:00:00.000Z
Adjusted to America/Sao_Paulo: 2011-10-29T21:00:00.000-02:00 & 2011-10-30T21:00:00.000-02:00
ISO 8601
If you have any control or influence on the format of your input data, I strongly suggest changing to the standard ISO 8601 format.
Example: 2015-02-15T19:39:11Z.
Time Zone
Avoid the 3 or 4 letter codes for time zones. They are neither standardized nor unique. BST for example can be:
British Summer Time (outdated as of 1971 but still tops in Google hits)
Brazil Standard Time
Bangladesh Standard Time
Use proper time zone names. Example: America/Sao_Paulo.
3-4 Letter Codes Refused By Joda-Time
Because of the frequent duplicate values, it is impossible to responsibly parse such values. So Joda-Time refuses to try.
Note in the example code above how I hard-coded the expected GMT value. See the single-quote marks (APOSTROPHE) around the "GMT" letters. That tells Joda-Time to expect and ignore that string while parsing.
This has a crucial consequence: With no identified time zone or offset-from-UTC, Joda-Time does not know how to interpret the date-time when parsing the string. We set the formatter to a time zone by which to interpret the string with no time zone or offset. If the string did have an offset, setting a time zone on the formatter has a different behavior: after parsing, the formatter adjusts the value to that time zone.
† Even more confusing, the java.util.Date actually does have a time zone, but buried deep within its implementation. That time zone is ignored for most practical purposes. So as shorthand, we say a j.u.Date has no time zone (effectively acts as if in UTC).

Related

Getting an Unparseable date error while calculating difference between Current date/time and Start date/time for an user in Sailpoint

Getting an Unparseable date error while calculating difference between Current date/time and Start date/time for an user.
Error: java.text.ParseException: Unparseable date: "09/11/20 00:00:00 AM CDT" at java.base/java.text.DateFormat.parse(DateFormat.java:395)
I get this error at line no.8, which is
String output2 = sdf1.format((sdf1.parse(startDate)).getTime());
'dateDifference' is a library used to calculate the difference between the current date/time and the start date/time of an user.
if(link.getAttribute("lastLogonTimeStamp")== null){
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
Calendar cur_time = Calendar.getInstance();
cur_time.setTime(new Date());
String output = sdf.format(cur_time.getTime());
System.out.println(" +++++ Output +++++" + output);
SimpleDateFormat sdf1 = new SimpleDateFormat("MM/dd/yy HH:mm:ss a zzz");
String output2 = sdf1.format((sdf1.parse(startDate)).getTime());
System.out.println(" +++++ Start Date +++++" + output2);
int diff = dateDifference(output2);
System.out.println(" +++++ Difference +++++" + diff);
if(diff>0){
System.out.println("Start Date is not a Future Date :" + startDate);
bw.write(id.getName()+","+ntID+","+id.getFirstname() +" "+id.getLastname() +","+id.getEmail()+ "," + id.getAttribute("empType")+ "," +lastLoginDt+ ","+mgrName+","+(String)id.getAttribute("startDate")+","+(String)id.getAttribute("title")+"\n");
count++;
}
}
tl;dr
I would not accept such a poor input string into my own app. But if you insist, you can try to parse ambiguous input such as CDT but this is a guessing game that may fail depending on the input.
ZonedDateTime.parse(
"09/11/20 00:00:00 AM CDT" ,
DateTimeFormatter.ofPattern( "MM/dd/uu HH:mm:ss a z" )
)
Parsing
CDT is not a real time zone. It is a localized indicator of whether Daylight Saving Time (DST) is effect.
Do not use localized formats for data exchange. Use localized values only for presentation to the user. For data exchange, use only ISO 8601 standard formats. The standard was invented for just that purpose, data exchange. The java.time classes use the standard formats by default when parsing/generating strings, so no need to specify formatting patterns.
Do not use Calendar and SimpleDateFormat classes. These terrible date-time classes are now legacy, years ago supplanted by the modern java.time classes defined in JSR 310. Search to learn more as this has been covered many many times already on Stack Overflow.
You can ask DateTimeFormatter class to guess what CDT might mean. But those pseudo-zone values are not standardized, and are not even unique! For example CST might mean "China Standard Time" or might mean "Central Standard Time" (in North America).
I recommend against accepting such poor inputs as yours, as playing guessing games in your code makes for unreliable apps. But if you insist:
String input = "09/11/20 00:00:00 AM CDT";
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/dd/uu HH:mm:ss a z" );
ZonedDateTime zdt = ZonedDateTime.parse( input , f );
zdt.toString() = 2020-09-11T00:00-05:00[America/Chicago]
The text generated by ZonedDateTime#toString is actually an extension to the ISO 8601 standard format, appending the name of the zone in square brackets.
Calculating elapsed time
Apparently you want to calculate the amount of time elapsed between the moment represented by your input and the current moment.
To calculate elapsed time in terms of hours-minutes-seconds, use Duration while capturing the current moment as seen in UTC (an offset from UTC of zero hours-minutes-seconds).
Duration elapsed = Duration.between( zdt.toInstant() , Instant.now() ) ;
To calculate elapsed time in terms of years-months-days, use Period. Access the time zone contained in our ZonedDateTime to get the same timeframe.
Period elapsed = Period.between( zdt , ZonedDateTime.now( zdt.getZone() ) ;
I have rewritten the code in the below format and that worked.
if(lastLogon == null || lastLogon.equalsIgnoreCase("never")){
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
Calendar cur_time = Calendar.getInstance();
cur_time.setTime(new Date());
String output = sdf.format(cur_time.getTime());
SimpleDateFormat dateParser = new SimpleDateFormat("MM/dd/yy HH:mm:ss a zzz");
Date date = dateParser.parse(startDate);
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMddHHmmss");
String output2 = dateFormatter.format(date);
int diff = dateDifference(output2);
if(diff>0){}

How to convert timestamp string to epoch time?

I have time stamp in format 2017-18-08 11:45:30.345.
I want to convert it to epoch time, so I am doing below:
String timeDateStr = "2017-18-08 11:45:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
ZonedDateTime zdt = ZonedDateTime.parse(timeDateStr, dtf);
System.out.println(zdt.toInstant().toEpochMilli());
I am getting below error:
java.time.format.DateTimeParseException: Text '2017-18-08 11:45:30.345' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor
I also tried different formats but still getting errors.
Note: originally the question had the input 2017-18-08 12:60:30.345 (with 60 in the minutes field), then it was edited (the time changed from 12:60 to 11:45), but I decided to keep this answer discussing about the original input (12:60), as it also works for the edited version (11:45).
ZonedDateTime needs a timezone or offset, but the input String doesn't have it (it has only date and time).
There are also another details in the input:
the minute value is 60, which is not accepted: the valid values are from 0 to 59 (actually there's a way to accept this, see "Lenient parsing" below)
the hh is the clock-hour-of-am-pm field, so it also needs the AM/PM designator to be fully resolved. As you don't have it, you should use the HH pattern instead
So the pattern must be yyyy-dd-MM HH:mm:ss.SSS, the input can't have 60 as the minutes value (unless you use lenient parsing, which I'll explain below) and you can't direclty parse it to a ZonedDateTime because it doesn't have a timezone/offset designator.
One alternative is to parse it to a LocalDateTime and then define in which timezone/offset this date is. In the example below, I'm assuming it's in UTC:
// change 60 minutes to 59 (otherwise it doesn't work)
String timeDateStr = "2017-18-08 12:59:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
// parse to LocalDateTime
LocalDateTime dt = LocalDateTime.parse(timeDateStr, dtf);
// assume the LocalDateTime is in UTC
Instant instant = dt.toInstant(ZoneOffset.UTC);
System.out.println(instant.toEpochMilli());
This will output:
1503061170345
Which is the equivalent of 2017-18-08 12:59:30.345 in UTC.
If you want the date in another timezone, you can use the ZoneId class:
// get the LocalDateTime in some timezone
ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
System.out.println(z.toInstant().toEpochMilli());
The output is:
1503057570345
Note that the result is different, because the same local date/time represents a different Instant in each timezone (in each part of the world, the local date/time 2017-18-08 12:59:30.345 happened in a different instant).
Also note that API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
You can also use the system's default timezone with ZoneId.systemDefault(), but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.
There's also the option to convert the LocalDateTime to an offset (like -05:00 or +03:00):
// get the LocalDateTime in +03:00 offset
System.out.println(dt.toInstant(ZoneOffset.ofHours(3)).toEpochMilli());
The output will be equivalent to the local date/time in the offset +03:00 (3 hours ahead of UTC):
1503050370345
Lenient parsing
As #MenoHochschild reminded me in the comments, you can use lenient parsing to accept 60 in the minutes field (using the java.time.format.ResolverStyle class):
String timeDateStr = "2017-18-08 12:60:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS")
// use lenient parsing
.withResolverStyle(ResolverStyle.LENIENT);
// parse to LocalDateTime
LocalDateTime dt = LocalDateTime.parse(timeDateStr, dtf);
In this case, 60 minutes are adjusted to the next hour, and the LocalDateTime will be:
2017-08-18T13:00:30.345
Daylight Saving Time
If you decide to use UTC or a fixed offset (using ZoneOffset class), you can ignore this section.
But if you decide to use a timezone (with ZoneId class), you must also take care of DST (Daylight Saving Time) issues. I'm gonna use the timezone I live in as example (America/Sao_Paulo).
In São Paulo, DST starts at October 15th 2017: at midnight, clocks shift 1 hour forward from midnight to 1 AM. So all local times between 00:00 and 00:59 don't exist in this timezone. If I create a local date in this interval, it's adjusted to the next valid moment:
ZoneId zone = ZoneId.of("America/Sao_Paulo");
// October 15th 2017 at midnight, DST starts in Sao Paulo
LocalDateTime d = LocalDateTime.of(2017, 10, 15, 0, 0, 0, 0);
ZonedDateTime z = d.atZone(zone);
System.out.println(z);// adjusted to 2017-10-15T01:00-02:00[America/Sao_Paulo]
When DST ends: in February 18th 2018 at midnight, clocks shift back 1 hour, from midnight to 23 PM of 17th. So all local times from 23:00 to 23:59 exist twice (in DST and in non-DST), and you must decide which one you want:
// February 18th 2018 at midnight, DST ends in Sao Paulo
// local times from 23:00 to 23:59 at 17th exist twice
LocalDateTime d = LocalDateTime.of(2018, 2, 17, 23, 0, 0, 0);
// by default, it gets the offset before DST ends
ZonedDateTime beforeDST = d.atZone(zone);
System.out.println(beforeDST); // before DST end: 2018-02-17T23:00-02:00[America/Sao_Paulo]
// get the offset after DST ends
ZonedDateTime afterDST = beforeDST.withLaterOffsetAtOverlap();
System.out.println(afterDST); // after DST end: 2018-02-17T23:00-03:00[America/Sao_Paulo]
Note that the dates before and after DST ends have different offsets (-02:00 and -03:00). This affects the value of epochMilli.
You must check when DST starts and ends in the timezone you choose and check the adjustments accordingly.
Corrected your code regarding yyyy-dd-MM. Also minute value could be 1-59 not 60. You provided 60. This is another simple way to solve the issue. Simply use DateFormat class.
String timeDateStr = "2017-18-08 12:59:30.345";
DateFormat df = new SimpleDateFormat("yyyy-dd-MM hh:mm:ss.SSS", Locale.ENGLISH);
try {
Date d = df.parse(timeDateStr);
System.out.println(d.toInstant().toEpochMilli());
} catch (ParseException e) {
e.printStackTrace();
}
Just i had made little bit change in nagendra547's answer
Please reffer to below code:-
String timeDateStr = "2017-18-08 12:59:30.345";
DateFormat df = new SimpleDateFormat("yyyy-dd-mm hh:mm:ss.SSS", Locale.ENGLISH);
try {
Date d = df.parse(timeDateStr);
System.out.println(d.toInstant().toEpochMilli());
} catch (ParseException e) {
e.printStackTrace();
}
Your code will fail for below 3 reasons.
Your date string (2017-18-08 12:60:30.345), doesn't match with the Formatter you used. It should be yyyy-MM-dd HH:mm:ss.SSS instead of yyyy-dd-MM hh:mm:ss.SSS
the range of minutes is (0-59), 60 doesn't come in this range.
Even if you have corrected code based above point it won't run for ZonedDateTime. So you would need to create a LocalDateTime before and then pass a ZoneId to it.
The code should look like below:
String timeDateStr = "2017-18-08 12:59:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
LocalDateTime date = LocalDateTime.parse(timeDateStr, dtf);
ZonedDateTime zdt = date.atZone(ZoneId.of("Europe/London"));
System.out.println(zdt.toInstant().toEpochMilli());

Wrong value for hour, minutes and mills when parsing date string to "yyyy-MM-dd'T'HH:mm:ss'-'hh:mm"

I've been experimenting with different formats for a while now. But I encountered a problem when parsing a date string to a date.
Here's the sample date string I'm trying to parse. "2015-04-13T10:17:00-04:00"
Here's my code:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'-'hh:mm", Locale.ENGLISH);
try {
startCal.setTime(format.parse(data.StartTime));
endCal.setTime(format.parse(data.EndTime));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
The value that I'm getting when printing it on the log is
Log.i("Event exact date", String.valueOf(startCal.getTime().toString()));
04-13 22:38:11.526: I/Event exact date(1665): Mon Apr 13 04:00:00 GMT+08:00 2015
I was expecting the hour value to be 10 and minutes to be 17. I think it is getting the value from the "-hh:mm". I'm getting this value from a web api. Any ideas guys? Thanks
I think the format you want is:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.ENGLISH);
Where the 'XXX', according to SimpleDateFormat API docs, means ISO 8601 time zone.
Full example:
public class TestDateParse {
public static void main(String[] args) throws ParseException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.ENGLISH);
Date date = format.parse("2015-04-13T10:17:00-04:00");
System.out.println("date: " + date);
}
}
Test output:
date: Mon Apr 13 16:17:00 CEST 2015
If you not need "-04:00" zone offset (timezone) then just truncate string "2015-04-13T10:17:00-04:00" to "2015-04-13T10:17:00"
String fullDate = "2015-04-13T10:17:00-04:00";
String truncatedDate = fullDate.substring(0, fullDate.lastIndexOf('-'));
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss",
Locale.ENGLISH);
try {
Date dte = format.parse(truncatedDate);
System.out.println("date=" + dte);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Result is:
date=Mon Apr 13 10:17:00 CEST 2015
use Z for timezone. It should work.
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
for reference, this is the documentation http://developer.android.com/reference/java/text/SimpleDateFormat.html
java.time
You are using troublesome old date-time classes. Avoid them.
Use the java.time framework built into Java 8 and later. Inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extra project. Look for back-ports for Java 6 & 7 and for Android.
Your input string happens to be in standard ISO 8601 format. The java.time classes use ISO 8601 as their defaults when parsing/generating textual representations of date-time values. So no need to specify a formatting pattern.
Offset
That last part -04:00 is indeed an offset-from-UTC as mentioned in other Answers. It means the date-time shown is four hours behind UTC.
String input = "2015-04-13T10:17:00-04:00";
OffsetDateTime odt = OffsetDateTime.parse( input );
Time Zone
You can search the list of time zones to see which zones use that particular offset of -04:00. I see a few dozen zones with that offset.
If you know the intended time zone, apply it (a ZoneId). A time zone is an offset-from-UTC plus the rules for handling anomalies such as Daylight Saving Time (DST). So using a time zone is always better than a mere offset, provided you are certain of the correct zone.
ZoneId zoneId = ZoneId.of( "America/Puerto_Rico" );
ZonedDateTime zdt = odt.atZoneSameInstant( zoneId );
UTC
The OffsetDateTime and ZonedDateTime objects seen above represent the very same moment on the timeline. If you want to see that moment in UTC, as for an Instant.
Instant instant = odt.toInstant(); // …or…
Instant instant = zdt.toInstant(); // Both instants are the same moment on the timeline in UTC.

Java SimpleDateFormat interpret parse-string as UTC

my timezone is GMT+1.
so a "Date"-object with "22.09.1985 00:00UTC" prints "Sun Sep 22 01:00:00 CEST 1985" on the tostring function.
Now i'm trying to create this date by parsing "22/09/1985" with simpleDateFormat
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
sdf.setTimeZone(TimeZone.getDefault());
Date d = sdf.parse("22/09/1985");
=> Sun Sep 22 00:00:00 CEST 1985
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date d = sdf.parse("22/09/1985");
=> Sun Sep 22 02:00:00 CEST 1985
how can i configure simpledateformat that it creates an Date which prints "Sun Sep 22 01:00:00 CEST 1985" with input string "22/09/1985"?
My assumption was wrong,
22.09.1985 00:00UTC is actually 22.09.1985 02:00CET
so
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date d = sdf.parse("22/09/1985");
is exactly what i wanted, the date i compared it with was wrong.
Avoid java.util.Date & Calendar
You’ve found one of the many reasons to avoid using java.util.Date & .Calendar. They are notoriously troublesome. Either use Joda-Time or, in Java 8, the new java.time package which is inspired by Joda-Time and defined by JSR 310.
Search StackOverflow for "joda date" to find many examples.
Time Zone
You said:
my timezone is GMT+1.
Incorrect, your local offset from UTC/GMT is +01. That is not your time zone. A time zone is an offset plus rules about Daylight Saving Time (DST) and other anomalies.
And that offset should have two digits: +01 (or +01:00) rather than +1, according to the ISO 8601 standard.
Avoid the 3 or 4 letter codes such as CET. They are neither standardized nor unique. Use proper time zone names.
Generally speaking, you should specify a time zone in all your date-time work rather than rely on the current JVM's default.
In both Joda-Time and java.time, a date-time object truly knows its assigned time zone. A java.util.Date has no time zone, but seems to because its toString applies the default time zone when creating a String representation, as you sadly learned the hard way.
Example Code
Some code using Joda-Time 2.3.
String input = "22/09/1985";
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Amsterdam" );
DateTimeFormatter formatter = DateTimeFormat.forPattern( "dd/MM/yyyy" );
DateTime dateTime = formatter.withZone( timeZone ).parseDateTime( input );
DateTime dateTimeUtcGmt = dateTime.withZone( DateTimeZone.UTC );
DateTime dateTimeIndia = dateTime.withZone( DateTimeZone.forID( "Asia/Kolkata" ) );
String outputMontreal = DateTimeFormat.forStyle( "FF" ).withZone( DateTimeZone.forID( "America/Montreal" ) ).withLocale( Locale.CANADA_FRENCH ).print( dateTime );
// All of the above date-time represent the very same moment in the timeline of the Universe.
Dump to console…
System.out.println( "dateTime: " + dateTime );
System.out.println( "dateTimeUtcGmt: " + dateTimeUtcGmt );
System.out.println( "dateTimeIndia: " + dateTimeIndia );
System.out.println( "outputMontreal: " + outputMontreal );
When run…
dateTime: 1985-09-22T00:00:00.000+02:00
dateTimeUtcGmt: 1985-09-21T22:00:00.000Z
dateTimeIndia: 1985-09-22T03:30:00.000+05:30
outputMontreal: samedi 21 septembre 1985 18 h 00 EDT
The fact that you're parsing a date string, using a specific time zone, doesn't make the printed Date object to use that time zone. You're still using the same implementation of Date#toString(), which formats the Date object using the default timezone.
What you would need is to format your Date object with that SimpleDateFormat object. And if you have that specific string, then you would need another SimpleDateFormat object for parsing that string:
String dateString = "22/09/1985";
SimpleDateFormat parser = new SimpleDateFormat("dd/MM/yyyy");
Date parsedDate = parser.parse(dateString);
SimpleDateFormat formatter = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(formatter.format(parsedDate));
Java Date doesn't have the concept of TimeZone associated with it. You can only format a Date object using a specified timezone, and get a string. Or else, switch to JodaTime library.
Date is a relatively "dumb" class, as it just represents the number of milliseconds since 1970-01-01 00:00:00 UTC.
If you want to print out a Date as if it were a different timezone, you need to construct a DateFormat / SimpleDateFormat for that TimeZone and format it to a String that way.

Check if 24 hours have passed (reading from a string)

I am saving date's in a file in the following format as a string.
Sat Jul 21 23:31:55 EDT 2012
How can I check if 24 hours have passed? I am a beginner so please explain it a little bit =)
I am not sure if I completely understood the question - do you have two dates for comparison or do you wish to keep checking periodically if 24 hours have elapsed?
If comparing two date/times, I would suggest looking at joda or perhaps date4j. Using joda, one could look into using interval between two dates:
Interval interval = new Interval(previousTime, new Instant());
where previous time would be the time you mentioned
You can do something like this:
try {
// reading text...
Scanner scan = new Scanner( new FileInputStream( new File( "path to your file here..." ) ) );
String dateString = scan.nextLine();
// creating a formatter.
// to understand the format, take a look here: http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
// EEE: Day name of week with 3 chars
// MMM: Month name of the year with 3 chars
// dd: day of month with 2 chars
// HH: hour of the day (0 to 23) with 2 chars
// mm: minute of the hour with 2 chars
// ss: second of the minute with 2 chars
// zzz: Timezone with 3 chars
// yyyy: year with 4 chars
DateFormat df = new SimpleDateFormat( "EEE MMM dd HH:mm:ss zzz yyyy", Locale.US );
// parsing the date (using the format above, that matches with your date string)
Date date = df.parse( dateString );
// now!
Date now = new Date();
// gets the differente between the parsed date and the now date in milliseconds
long diffInMilliseconds = now.getTime() - date.getTime();
if ( diffInMilliseconds < 0 ) {
System.out.println( "the date that was read is in the future!" );
} else {
// calculating the difference in hours
// one hour have: 60 minutes or 3600 seconds or 3600000 milliseconds
double diffInHours = diffInMilliseconds / 3600000D;
System.out.printf( "%.2f hours have passed!", diffInHours );
}
} catch ( FileNotFoundException | ParseException exc ) {
exc.printStackTrace();
}
I would suggest storing your information as a java.util.Calendar which has a compareTo ()function.
If you want to compare now to current time, you can use System.getCurrentTimeMillis() to get the current time.
Define A Day
Do you really mean one day or 24-hours? Because of Daylight Saving Time nonsense, a day can vary in length such as 23 or 25 hours in the United States.
Avoid 3-Letter Time Zone Codes
That String format is a terrible representation of a date-time. It is difficult to parse. It uses a 3-letter time zone code, and such codes are neither standardized nor unique. If possible, choose another format. The obvious choice is ISO 8601, for example: 2014-07-08T04:17:01Z.
Use proper time zone names.
Avoid j.u.Date & .Calendar
The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome. Avoid them.
Instead use either the venerable Joda-Time library or the new java.time package bundled in Java 8 (and inspired on Joda-Time).
Joda-Time
Here is some example code in Joda-Time.
Get the current moment.
DateTime now = DateTime.now();
Parse the input string.
String input = "Sat Jul 21 23:31:55 EDT 2012";
DateTime formatter = DateTimeFormat.forPattern( "EEE MMM dd HH:mm:ss zzz yyyy" ).with Locale( java.util.Locale.ENGLISH );
DateTime target = formatter.parseDateTime( input );
Calculate 24 hours (or next day).
DateTime twentyFourHoursLater = target.plusHours( 24 );
Test if current moment happened after.
boolean expired = now.isAfter( twentyFourHoursLater );
Or, if you want next day rather than 24-hours, use plusDays rather than plusHours. If necessary, adjust to desired time zone. Time zone is crucial as it defines the day/date and applies rules for anomalies such as Daylight Saving Time.
DateTime targetAdjusted = target.withZone( DateTimeZone.forID( "Europe/Paris" ) );
…
DateTime aDayLater = targetAdjusted.plusDays( 1 ); // Go to next day, accounting for DST etc.
boolean expired = now.isAfter( aDayLater ); // Test if current moment happened after.

Categories