Java simple date format british time - java

I am using simple date format to allow users to specify which time zone they are sending data in:
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss,z");
This works fine:
e.g.
df.parse("2009-05-16 11:07:41,GMT");
However, if someone is always sending time in London time (i.e. taking into account daylight savings), what would be the approriate time zone String to add?
e.g. this doesnt work:
df.parse("2009-05-16 11:07:41,Western European Time");
System.out.println(date);
Sat May 16 12:07:41 BST 2009
I want to match the time to british time taking into daylight savings.
Thanks.

In daylight saving time, it's BST. In the rest of the year it's GMT.
I suggest that you use the generic name (for the whole year), which is Europe/London. You can use something like this:
String userInput = "2009-05-16 11:07:41,Europe/London";
String[] tokens = userInput.split(",");
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone(tokens[1]));
System.out.println(df.parse(tokens[0]));
The output in this case is:
Sat May 16 11:07:41 GMT+01:00 2009

So what exactly is your question - what ID for the TimeZone you should use? The following will print a list of all the available time zone identifiers:
for (String id : TimeZone.getAvailableIDs()) {
System.out.println(id);
}

I don't think it's possible to do exactly what you want to do using SimpleDateFormat on its own. TimeZone.parse(String) only accepts time zones, not time zone IDs, for example:
Time Zone ID Time Zone (Winter) Time Zone (Summer)
-------------------------------------------------------------
Europe/London GMT BST
If parse(...) accepted Europe/London there would be one hour in spring that would not be a valid Europe/London time and one hour in autumn that would map to two UTC times.
I think that the best you can do is follow Bruno Rothgiesser's suggestion, however you could accept the time zone ID as a separate user input, or do an additional string processing step to separate the time zone id from the user input string, and use it to work out whether the user probably means GMT or BST. The user's Locale might be a better way of working out what he/she means - although there are some assumptions involved in that idea.
The "what the user probably means" algorithm has to deal with two special cases - you can use TimeZone.inDaylightTime(Date) with userTime +/- 1 hour to work out if you might have one of these.

Related

How to find epoch format current time of GMT using java

I have written below code which is running, and giving output. But I'm not sure It's a right one.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = new Date();
sdf.setTimeZone(TimeZone.getTimeZone("GMT-7"));
String value = sdf.format(date);
System.out.println(value);
Date date2 = sdf.parse(value);
long result = date2.getTime();
System.out.println(result);
return result;
The above code what I'm trying is, I just need to get the current time of GMT time zone and convert it as epoch format which is gonna used in Oracle db.
Can someone tell me that format, and the above code is right?
First, you should not store time since the epoch as a timestamp in your database. Look into the date-time datatypes that your DMBS offers. In Oracle I think that a date column will be OK. For most other DBMS you would need a datetime column. timestamp and timestamp with timezone may be other and possibly even sounder options depending on your exact requirements.
However, taking your word for it: Getting the number of milliseconds since the epoch is simple when you know how:
long millisecondsSinceEpoch = System.currentTimeMillis();
System.out.println(millisecondsSinceEpoch);
This just printed:
1533458641714
The epoch is defined in UTC, so in this case we need to concern ourselves with no other time zones.
If you needed seconds rather than milliseconds, it’s tempting to divide by 1000. However, doing your own time conversions is a bad habit since the libraries already offers them, and using the appropriate library methods gives clearer, more explanatory and less error-prone code:
long secondsSinceEpoch = Instant.now().getEpochSecond();
System.out.println(secondsSinceEpoch);
1533458641
You said:
I just need to get the current time of GMT time zone…
Again taking your word:
OffsetDateTime currentTimeInUtc = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(currentTimeInUtc);
long millisecondsSinceEpoch = currentTimeInUtc.toInstant().toEpochMilli();
System.out.println(millisecondsSinceEpoch);
2018-08-05T08:44:01.719265Z
1533458641719
I know that GMT and UTC are not exactly the same, but for most applications they can be (and are) used interchangeably.
Can someone tell me (if) the above code is right?
When I ran your code just now, its output agreed with mine except the milliseconds were rounded down to whole thousands (whole seconds):
1533458641000
Your code has some issues, though:
You are using the old, long out-dated and poorly designed classes SimpleDateFormat, Date and TimeZone. The first in particular has a reputation for being troublesome. Instead we should use java.time, the modern Java date and time API.
Bug: In your format pattern string you are using lowercase hh for hour of day. hh is for hour within AM or PM, from 1 through 12, so will give you incorrect results at least half of the day. Uppercase HH is for hour of day.
Don’t use GMT-7 as a time zone. Use for example America/Los_Angeles. Of course select the time zone that makes sense for your situation. Edit: You said:
I just want to specify the timezone for sanjose. GMT-7 is refer to
sanjose current time.
I believe many places are called San Jose. If you mean San Jose, California, USA, you are going to modify your program to use GMT-8 every time California goes back to standard time and opposite when summer time (DST) begins?? Miserable idea. Use America/Los_Angeles and your program will work all year.
Since you ask for time in the GMT time zone, what are you using GMT-7 for at all?
There is no point that I can see in formatting your Date into a string and parsing it back. Even if you did it correctly, the only result you would get would be to lose your milliseconds since there are no milliseconds in your format (it only has second precision; this also explained the rounding down I observed).
Links
Oracle tutorial: Date Time explaining how to use java.time, the modern Java date and time API.
San Jose, California on Wikipedia
Why not use Calendar class?
public long getEpochTime(){
return Calendar.getInstance(TimeZone.getTimeZone("GMT-7")).getTime().getTime()/1000; //( milliseconds to seconds)
}
It'll return the current Date's Epoch/Unix Timestamp.
Based on Harald's Comment:
public static long getEpochTime(){
return Clock.system(TimeZone.getTimeZone("GMT-7").toZoneId() ).millis()/1000;
}
Here is a solution using the java.time API
ZonedDateTime zdt = LocalDateTime.now().atZone(ZoneId.of("GMT-7"));
long millis = zdt.toInstant().toEpochMilli();

Unexpected result when using SimpleDateFormatter with a timezone

My goal is to parse a date string with the format yyyy-MM-dd hh:mm:ss.SSS for a given timezone, using only Java 6 without any external libraries. My first approach was to configure a SimpleDateFormatter with a timezone. However, I'm not able to understand the results. Have a look at the following code:
List<String> zones = Arrays.asList("UTC", "CET", "Europe/London", "Europe/Berlin");
for(String zone: zones) {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
df.setTimeZone(TimeZone.getTimeZone(zone));
Date result = df.parse("1970-01-01 00:00:00.000");
System.out.println(zone + ": " + result.getTime());
}
The output is as follows:
UTC: 0
CET: -3600000
Europe/London: -3600000
Europe/Berlin: -3600000
The first two results are as expected. In UTC, the unix epoch starts at exactly zero milliseconds, and there is a one hour difference to CET.
What I don't understand are the values for London and Berlin. Since the milliseconds for London equal the milliseconds for CET instead of UTC, I first assumed that daylight savings time is taken into account (since it's currently summer on the northern hemisphere and summertime in London is UTC+1, which equals CET). However, why is the value for Berlin the same? Shouldn't it be UTC+2 (or CET+1 if you want)?
My questions:
What's the explanation for the results? Is DST really taken into account?
Is there a better way for doing the conversion in Java 6?
The results are correct, because that's the offsets used by London and Berlin in 1970 (both used +01:00).
It's not related to Daylight Saving Time, because nowadays it happens during March and October in these countries. Actually one could argue that London was in a "long" DST period, from 1968 to 1971, because the offset was +01:00 during this whole period.
It's just the nature of timezones: they're defined by governments and laws, and the respective politicians can change the offsets used by their countries for whatever reasons they have.
In the same links above, you can see that Berlin adopted DST only in 1980, and London changed the offset to zero (GMT) in 1971.
PS: as discussed in the comments, short names like CET are ambiguous and not standard (for lots of them, there's more than one timezone that uses the same abbreviation). Always prefer to use IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Some names (such as CET) are recognized and set to arbitrary defaults - mainly due to retro-compatibility (or bad design) issues - but it's usually done in unexpected ways. Avoid such short names if you can: names like Europe/Berlin are much more clear and have no ambiguities.

Java how to get list of time zone ID’s for the given time zone abbreviation

Is it possible to find the list of time zone ID's for a given time zone abbreviation? For example, for the abbreviation IST, the time zone ID's are Asia/Jerusalem, Asia/Kolkata and Europe/Dublin.
Interesting question. Since the abbreviations aren’t standardized, there cannot be an authoritative answer nor a bulletproof way to get such an answer. Off the top of my head I thought of two approaches:
Get them from your JVM.
Find them on the net.
Getting the zones from your JVM:
String givenAbbr = "IST";
LocalDateTime summerSouthernHemisphere = LocalDate.of(2018, Month.JANUARY, 31).atStartOfDay();
LocalDateTime summerNorthernHemisphere = LocalDate.of(2018, Month.JULY, 31).atStartOfDay();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("z");
Set<ZoneId> zones = new HashSet<>();
for (String id : ZoneId.getAvailableZoneIds()) {
ZoneId zone = ZoneId.of(id);
String abbr = summerSouthernHemisphere.atZone(zone).format(dtf);
if (abbr.equals(givenAbbr)) {
zones.add(zone);
}
abbr = summerNorthernHemisphere.atZone(zone).format(dtf);
if (abbr.equals(givenAbbr)) {
zones.add(zone);
}
}
System.out.println(zones);
This prints:
[Asia/Calcutta, Eire, Europe/Dublin, Asia/Jerusalem, Asia/Tel_Aviv, Israel, Asia/Kolkata, Asia/Colombo]
Some of these are just names for the same time zone, though. For example Eire has the same rules as Europe/Dublin. So a further filtering could be made if desired. You may use oneZoneId.getRules().equals(anotherZoneId.getRules()) to determine if two ZoneId objects have the same zone rules.
For abbreviation CST the list is even longer and has more synonyms:
[PRC, America/Matamoros, Asia/Taipei, America/Regina, America/El_Salvador,
America/North_Dakota/New_Salem, Asia/Harbin, America/Costa_Rica,
America/North_Dakota/Center, America/Guatemala, America/Winnipeg,
Asia/Chongqing, America/Rankin_Inlet, America/Indiana/Knox,
America/Belize, SystemV/CST6CDT, Mexico/General,
America/North_Dakota/Beulah, CST6CDT, America/Swift_Current,
America/Knox_IN, Asia/Chungking, Asia/Macao, Asia/Shanghai,
America/Indiana/Tell_City, America/Menominee, America/Bahia_Banderas,
America/Managua, Canada/East-Saskatchewan, Asia/Macau, America/Havana,
America/Resolute, US/Central, US/Indiana-Starke, Cuba, America/Monterrey,
America/Chicago, America/Merida, America/Mexico_City, Canada/Central,
America/Tegucigalpa, America/Rainy_River, Canada/Saskatchewan, SystemV/CST6]
One limitation of my approach is that some time zones are known by more than one name and therefore more than one three or four letter abbreviation. My code above catches only one of these.
Another limitation is that picking two dates like I do will never give you all possibilites in the past and the future, and may even miss some where I just don’t hit the right date. I have tried to pick one date where it is winter on the northern hemisphere and summer on the southern, and one where it is the other way around. This will cover most cases for the present, but you never know if there is a time zone or three where the transition don’t follow summer and winter as we know it. If you want better coverage, there are a couple of excellent suggestions in Hugo’s answer.
Get them from the Internet
The other answer is, of course, the one that your search has no doubt already brought up: such lists are public on the Internet. For example Wikipedia’s List of time zone abbreviations and Time Zone Abbreviations – Worldwide List on timeanddate.com. As expected, the two lists mentioned do not agree. For example, the latter knows two interpretations of ADT, the former only one. The latter list gives many synonym abbreviations and thereby illustrates my point above that each zone can have more than one abbreviation.
#Ole V.V.'s answer already gives great and detailed information, like the fact that the 3-letter abbreviations (like IST or PST) are ambiguous and not standard, and you should prefer the long IANA timezones names (always in the format Continent/City, like Asia/Kolkata or Europe/Berlin).
But there's one tricky detail in that answer: it's taking January and July of 2018 as the base dates for winter and summer (so the abbreviation can be checked against standard and Daylight Saving periods). But it's not guaranteed that it'll take both winter and summer time for all cases, because timezones rules can change - just because a timezone has DST today, it doesn't mean it'll have it forever (the opposite is also true).
So, instead of picking some date/time and hope that all timezones have a DST change between them, the best approach is to get all the changes from the ZoneRules object - it contains all the transition dates (the moment when the offset changes for that timezone - due to DST start/end or because some government decided that their country will now be in another timezone).
It also covers the case where a timezone used an abbreviation in the past, but then changed to another, as I'm checking through all the timezone's changes history.
The code is very similar. The only difference is that, instead of using a fixed date (Jan/Jul 2018), I'm looking at all the transitions of the zone (and if a timezone has no transitions - which means it never had DST or any other changes - I get the current date). I also created a Set of String (as you want just the names, but you could store the ZoneId objects as well):
String ist = "IST";
Set<String> zones = new HashSet<>();
// formatter to get the abbreviation
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("z");
for (String id : ZoneId.getAvailableZoneIds()) {
ZoneId zone = ZoneId.of(id);
ZoneRules rules = zone.getRules();
List<ZoneOffsetTransition> transitions = rules.getTransitions();
if (transitions.isEmpty()) {
// no transitions found, just pick any date
String abbrev = fmt.format(ZonedDateTime.now(zone));
if (ist.equals(abbrev)) {
zones.add(id);
}
} else {
for (ZoneOffsetTransition transition : transitions) {
// get the instant that the transition occurred and convert to this zone
String abbrev = fmt.format(transition.getInstant().atZone(zone));
if (ist.equals(abbrev)) {
zones.add(id);
}
}
}
}
System.out.println(zones);
The output, in this case, will be the same:
[Asia/Calcutta, Eire, Europe/Dublin, Asia/Jerusalem, Asia/Tel_Aviv, Israel, Asia/Kolkata, Asia/Colombo]
Although this code looks more redundant (as it traverses through all the dates when a DST change occurred), it's more guaranteed to get all cases. If you look for a timezone that had DST in the past but it won't have in 2018 (or in any other arbitrary date you get), using this arbitrary date won't work. Only by checking all transitions you can be sure that all cases were covered.
One example: if instead of IST, I'd like to check the abbreviation AEDT (Australian Eastern Daylight Time).
Using #Ole V.V.'s code, I'll get:
[Australia/Sydney, Australia/Melbourne, Australia/Hobart, Australia/Victoria, Australia/ACT, Australia/Canberra, Australia/NSW, Australia/Tasmania, Australia/Currie]
Using my code, I'll get:
[Australia/Sydney, Australia/Brisbane, Australia/Melbourne, Australia/Queensland, Australia/Hobart, Australia/Victoria, Australia/ACT, Australia/Canberra, Australia/NSW, Australia/Tasmania, Australia/Currie, Australia/Lindeman]
Note the differences. One example is Australia/Brisbane, which had DST until the 90's, but now it doesn't (so it won't have it in 2018 as well). So, if you try to get AEDT (summer time) in 2018, this timezone won't be picked by #Ole V.V.'s code, because it won't have DST in 2018.
But I'm checking all the changes it had during history, no matter when it happened. This guarantees that I'm covering all cases.
PS: if you want to get the abbreviations that were valid in a specific date, then you can use #Ole V.V.'s code (just change the dates accordingly).
Another way (not easier) is to download the IANA Time Zone Database file and follow this tutorial to understand how to read the files (not trivial, IMO). Take, for example, the Dublin's entry:
# Zone NAME GMTOFF RULES FORMAT [UNTIL]
Zone Europe/Dublin -0:25:00 - LMT 1880 Aug 2
-0:25:21 - DMT 1916 May 21 2:00 # Dublin MT
-0:25:21 1:00 IST 1916 Oct 1 2:00s
0:00 GB-Eire %s 1921 Dec 6 # independence
0:00 GB-Eire GMT/IST 1940 Feb 25 2:00
0:00 1:00 IST 1946 Oct 6 2:00
... etc
You can see that IST is used for Europe/Dublin. Well, this is not the most straightforward way, but every time IANA updates its database, it takes some time for changes to be included in the JDK (although you can update just the timezone data if you want).
So, if you want the most up-to-date information, you can regularly check for updates in IANA's website.
Refer to Java 8 docs for ZoneDateTime api on oracle docs.
Link of a sample maven project on github implementing this method.
Implementation wise you can use the code below,
ZoneId losAngeles = ZoneId.of("America/Los_Angeles");
ZoneId berlin = ZoneId.of("Europe/Berlin");
// 2014-02-20 12:00
LocalDateTime dateTime = LocalDateTime.of(2014, 02, 20, 12, 0);
// 2014-02-20 12:00, Europe/Berlin (+01:00)
ZonedDateTime berlinDateTime = ZonedDateTime.of(dateTime, berlin);
// 2014-02-20 03:00, America/Los_Angeles (-08:00)
ZonedDateTime losAngelesDateTime = berlinDateTime.withZoneSameInstant(losAngeles);
int offsetInSeconds = losAngelesDateTime.getOffset().getTotalSeconds(); // -28800
// a collection of all available zones
Set<String> allZoneIds = ZoneId.getAvailableZoneIds();

SimpleDateFormat parses a string to wrong time

I run the following code:
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
try{
Date date = sdf.parse("03-28-2003 01:00:00");
System.out.print(date.toString());
}
catch(Exception e){
//do something
}
The result of the parsing is this date: 2003-03-28T02:00:00.000+0300
One hour is added.
When I change the year/day/hour to any other valid number, I get the correct time, no extra hour is added. If I only change the minutes or the seconds I still get the added hour.
Can anyone tell me why this happens?
EDIT:
This is related to when daylight saving time is applied in the timezone my program runs on- UTC+02:00.
In this timezone the clock changed on 2003-03-28. that's why an hour was added, as it was suggested by the comments and answer below.
I used the code suggested in the answer to parse my date and the parsing worked! The date is parsed correctly, the extra hour isn't added.
Finding out exactly what your code does is complicated by the fact that not only SimpleDateFormat.parse() may depend on the default time zone of the computer (and does in this case where the pattern does not include time zone), also Date.toString() depends on the default time zone. However, I understand that you want to interpret the date string in UTC, so I will concentrate on getting the parsing right and not worry so much about what’s printed.
Feek is correct in the comment that setting the time zone of the SimpleDateFormat to UTC will get you what you want, for example:
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
With this line added before try I get this output on my computer:
Fri Mar 28 02:00:00 CET 2003
2 am. CET agrees with 1 UTC, so now the parsing is correct.
Allow me to add that if you can use the Java 8 date and time classes, I find the corresponding code somewhat clearer:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse("03-28-2003 01:00:00", formatter);
OffsetDateTime utcDateTime = dateTime.atOffset(ZoneOffset.UTC);
System.out.println(utcDateTime);
The point is not that it’s shorter, but that you don’t get easily in doubt about what it does and don’t easily get time zone or DST problems. An added benefit is that the output is also as expected:
2003-03-28T01:00Z
Now it’s evident that the time is correct (Z means Z or Zulu or UTC time zone, it’s got more than one name).
If for some reason you absolutely need an oldfashioned java.util.Date object, that is not difficult:
Date date = Date.from(utcDateTime.toInstant());
This gives the same date as we got from sdf.parse() with UTC time zone.

Joda Time subtracting 24 hours from an instance of MutableDateTime, I would like to know why

I do not understand why MutableDateTime.setDate() is setting the time to "yesterday" (see the log timestamp hours - it is 20:28). Is this timezone related? Do I need to set something on the formatter?
I would expect that after calling setDate with "10/27/2010", the date would be the same as the parsed date 00:00 EDT 10/27/10, instead of 20:28 EDT 10/26/10. This is 24 hours ago from "now".
What am I missing here, or how should I edit the code to get the desired result? I am new to Joda Time, and would like to solve this mystery.
DateTimeFormatter dateFormatterJ = DateTimeFormat.forPattern("MM/dd/yyyy");
DateTimeFormatter timestampFormatJ = DateTimeFormat.forPattern("HH:mm zzz MM/dd/yy");
MutableDateTime startDate = new MutableDateTime();
log.info("parsed date " +
timestampFormatJ.print(dateFormatterJ.parseMutableDateTime(startDateString)));
startDate.setDate((dateFormatterJ.parseMutableDateTime(startDateString)));
log.info("startDate: " + timestampFormatJ.print(startDate));
In this case, startDateString is simply "10/27/2010".
here is the log output:
10-27 20:28:55 INFO parsed date: 00:00 EDT 10/27/10
10-27 20:28:55 INFO startDate: 20:28 EDT 10/26/10
Thanks
The simple answer would be, because the javadoc says so.
public void setDate(ReadableInstant
instant)
Set the date from another
instant. The time part of this object
will be unaffected.
Parameters:
instant - an instant to copy the date
from, time part ignored
Throws:
IllegalArgumentException - if the
object is invalidobject is invalid
When Joda says 'Date' it means the human meaning of the word Date. "The year-month-day portion of this value", not the logical equivalent of a java.util.Date. (the whole point of joda being to introduce some natural, sensible, semantics to handling date and time.)
EDIT:
To answer your 'how to fix' question, simply do:
MutableDateTime startDate = new MutableDateTime(dateFormatterJ.parseMutableDateTime(startDateString));
Or else manually zero out the time portions of course.
EDIT 2: Hmm, I apparently did not read carefully enough, this is only half of the answer. Will check.
EDIT 3: well this bugged me so much that I took a minute to look for it.
public void setDate(final ReadableInstant instant) {
long instantMillis = DateTimeUtils.getInstantMillis(instant);
Chronology instantChrono = DateTimeUtils.getInstantChronology(instant);
DateTimeZone zone = instantChrono.getZone();
if (zone != null) {
instantMillis = zone.getMillisKeepLocal(**DateTimeZone.UTC**, instantMillis);
}
setDate(instantMillis);
}
For some reason, it's rolling your absolute time forward into UTC before setting the date. So you give it 10/27/2010 00:00 EDT and it sets the absolute magnitude of time to the number of milliseconds that represent 10/27/2010 00:00 UTC, which of course is only 6 or 7 PM the day before. Then it finds the EDT date value of that to be 10/26.
Couldn't say if that's somehow intended or if it's a bug that's been there for 2 years or what.)
When parsing a string that does not contain a GMT offset or time-zone ID, you must do one of three things:
do nothing, and accept that the string is parsed in the default time zone
specify the time zone to parse in using withZone() on the formatter
use parseLocalDate() instead of parseMutableDateTime()
The last is the preferred solution, as it correctly parses the data that was actually input, which was a date without time, offset or zone.
Using parseLocalDate() in the test code correctly parses the date.

Categories

Resources