Java Timezone: why Offset is needed - java

My requirment is like this:
I am saving time in millisecond in database and the timezone.For example the time in milisecond is 1223123123232 in long and timezone is Asia/Calcutta. I have to convert it to Africa/Asmara timezone.
long l = 1223123123232l;
TimeZone tz = TimeZone.getTimeZone("Asia/Calcutta");
long tzOff = tz.getOffset(l);
java.util.Date d = new Date(l-tzOff); // WHY THIS??
DateFormat df = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("Africa/Asmara"));// required timezone
String s = df.format(d);
System.out.println(s);
To check i am refering this: link
My question is:
If the timezone is just the representation of time in different formats(geographical areas offset from GMT),why do i need to subtract the offset time form the actual time (l-tzOff)?
Why can't i ignore the timezone which is saved in Database, and only consider the timezome in which i want to convert the date?
Something like:
long l = 1223123123232l;
java.util.Date d = new Date(l);
DateFormat df = new SimpleDateFormat("dd MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("Africa/Asmara"));// required timezone
String s = df.format(d);
System.out.println(s);
My system timezone is Asia/Calcutta, i want to convert a Date in Africa/Bujumbura timezone to Europe/Vatican timezone.The above code is not working in this case? Why this is so?

Java dates do not know about time zones. Therefore, if I save 14H00 in New-York, it will not be the same 14H00 as in Paris, although the millisecond value is the same. You need to use a unique reference to save dates. People often chose GMT+0.
If you need to check local time across timezones, you can use a tool I developed here.
To answer your questions:
Timezone is not a representation of time, it is a localisation of time. The reason you would substract an offset is to make sure all time is defined according to the same reference.
Because you would get false results and false dates.

new Date(long) takes the number of milliseconds since the GMT epoch. Since in your case l is the number of milliseconds since some other point in time (namely 1 Jan 1970, 00:00:00 in Asia/Calcutta), you have to convert it to GMT first. This is done by subtracting the relevant timezone offset.

Related

Java - SimpleDateFormat formatter to return epoch time with milliseconds [duplicate]

This question already has answers here:
Convert a date format in epoch
(6 answers)
Closed 5 years ago.
I am very new to Java and coding in general - I have some code which returns a timestamp in the following format yyyy.MM.dd HH:mm:ss:ms which is shown below:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:sss");
This returns:
2017.07.19 11:42:30:423
Is there a way to edit the "SimpleDateFormat formatter" code above to return the date/time as an epoch timestamp that includes milliseconds so that the value returned is formatted as per the below?
1500464550423
I'm hoping that I can amend the ("yyyy.MM.dd HH:mm:ss:sss") part of the SimpleDateFormat formatter code to do this.
Any help or advice is much appreciated.
Thanks
You have a simple error in the use of case in your format pattern string (these are case sensitive). And worse, you are using the old and troublesome SimpleDateFormat class. One of the many problems with it is it’s not telling you what the problem is.
So I recommend you use the modern Java date and time API instead (I am deliberately using your format pattern string verbatim):
String receivedTimetamp = "2017.07.19 11:42:30:423";
DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:sss");
LocalDateTime dateTime = LocalDateTime.parse(receivedTimetamp, parseFormatter);
System.out.println(dateTime);
This code throws an IllegalArgumentException: Too many pattern letters: s. I hope this calls your awareness to the fact that you are using two s’s for seconds and three s’s for fraction of second. If it still isn’t clear, the documentation will tell you that lowercase s is correct for seconds, while you need uppercase S for the fraction. Let’s repair:
DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
Now the code prints 2017-07-19T11:42:30.423, so we have managed to parse the string correctly.
To convert to milliseconds we are still missing a crucial piece of information: in what time zone should the timestamp be interpreted? I think the two obvious guesses are UTC and your local time zone (which I don’t know). Try UTC:
System.out.println(dateTime.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli());
This produces 1500464550423, which is the number you asked for. I suppose we’re done.
If you wanted your JVM’s time zone setting instead, use .atZone(ZoneId.systemDefault()) instead of .atOffset(ZoneOffset.UTC), but beware that the setting may be altered by other software running in the same JVM, so this is fragile.
First of all, check the documentation of SimpleDateFormat. The pattern that corresponds to milliseconds is an uppercase S, while the lowercase s corresponds to seconds. The problem is that SimpleDateFormat usually doesn't complain and try to parse 423 as seconds, adding this amount to your end date (giving an incorrect result).
Anyway, SimpleDateFormat just parses a String to a java.util.Date or formats the Date to a String. If you want the epoch millis value, you must get it from the Date object:
// input string
String s = "2017.07.19 11:42:30:423";
// use correct format ('S' for milliseconds)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS");
// parse to a date
Date date = formatter.parse(s);
// get epoch millis
long millis = date.getTime();
System.out.println(millis); // 1500475350423
The problem is that SimpleDateFormat uses the system's default timezone, so the final value above (1500475350423) will be equivalent to the specificed date and time in my system's timezone (which can be different from yours - just for the record, my system's default timezone is America/Sao_Paulo). If you want to specify in what timezone this date is, you need to set in the formatter (before calling parse):
// set a timezone to the formatter (using UTC as example)
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
With this, the result for millis will be 1500464550423 (the equivalent to the specificed date and time in UTC).
To do the opposite (create a date from the millis value), you must create a Date object and then pass it to the formatter (also taking care of setting a timezone to the formatter):
// create date from millis
Date date = new Date(1500464550423L);
// use correct format ('S' for milliseconds)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
// format date
String formatted = formatter.format(date);
Java new date/time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
As the input String has no timezone information (only date and time), first I parsed it to a LocalDateTime (a class that represents a date and time without timezone). Then I convert this date/time to a specific timezone and get the millis value from it:
// input string
String s = "2017.07.19 11:42:30:423";
// use correct format ('S' for milliseconds)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
// as the input string has no timezone information, parse it to a LocalDateTime
LocalDateTime dt = LocalDateTime.parse(s, formatter);
// convert the LocalDateTime to a timezone
ZonedDateTime zdt = dt.atZone(ZoneId.of("Europe/London"));
// get the millis value
long millis = zdt.toInstant().toEpochMilli(); // 1500460950423
The value is now 1500460950423, equivalent to the specified date and time in London timezone.
Note that the 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 ZoneOffset.UTC constant if you want to use UTC.
To do the opposite, you can get the millis value to create an Instant, convert it to a timezone and pass it to the formatter:
// create Instant from millis value
Instant instant = Instant.ofEpochMilli(1500460950423L);
// use correct format ('S' for milliseconds)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
// convert to timezone
ZonedDateTime z = instant.atZone(ZoneId.of("Europe/London"));
// format
String formatted = z.format(formatter);
First advice is to move to java8 java.time API instead of learning the broken java.date API
then do:
Instant i = Instant.now();
System.out.println(i.toEpochMilli());
in your case you can do:
LocalDateTime myldt = LocalDateTime.parse("2017-06-14 14:29:04",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(myldt.toInstant(ZoneOffset.UTC).toEpochMilli());
note that as soon as you play more with the api you will find more ways to achieve the same thing, at the end you will end invoking toEpochMilli
String strDate = "Jun 13 2003 23:11:52.454 UTC";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm:ss.SSS zzz");
ZonedDateTime zdt = ZonedDateTime.parse(strDate,dtf);
System.out.println(zdt.toInstant().toEpochMilli()); // 1055545912454
You can try
long time = System.currentTimeMillis();
If you have a java.util.Date then invoking getTime() will return the number of millis since the epoch. For example:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:sss");
Date dateToBeFormatted = new Date();
// this will print a datetime literal on the above format
System.out.println(formatter.format(dateToBeFormatted));
// this will print the number of millis since the Java epoch
System.out.println(dateToBeFormatted.getTime());
The key point here is that in order to get the number of millis since the epoch you do not need a SimpleDateFormatter because the number of millis since the epoch is a property of the Date.

Converting a Joda DateTime to another timezone without changing the time

I have tried all the ways in all the other questions on SO, and I can't get it to work. It is making me want to kill myself.
I have a set of times which are something like "04:00 AM AEST", except the AEST is a glitch, they should be GMT. What I want to do is change them to "04:00 GMT", and then convert them up to the correct AEST times (which in this example would be "14:00 AEST"). I have tried everything, and nothing works. The closest was to manually make a new DateTime using each individual value from the original date, e.g.
DateTime dt = new DateTime(origdate.year, origdate.month, origdate.day, origdate.hour, origdate.minute, origdate.second, timezone.GMT)
But for some reason the results came out four and a half minutes over, which is weird because timezones differ on hours and half hours.
1st Method By following lines you will get GMT time in specified format :
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Date currentLocalTime = cal.getTime();
DateFormat date = new SimpleDateFormat("dd-MM-yyy HH:mm:ss z");
date.setTimeZone(TimeZone.getTimeZone("GMT"));
String gmtTime = date.format(currentLocalTime);
Hence, from GMT you can derive the time of any place.
2nd Method You can get system time of current place in milliseconds by:
Long current_time = System.currentTimeMillis() / 1000L;
Hope it helps.

java Date conversion confusion regarding timezones

I'm using Java 6. Our servers are on Eastern Standard Time (GMT-5), and I'm trying to store a value that should be interpreted as such but I'm confused about how to convert it. I have
String dateStr = "1368921600000"; // This is 5/19/2013 00:00:00
final Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("GMT-5"));
cal.setTimeInMillis(Long.parseLong(dateStr));
final java.util.Date dateObj = cal.getTime();
System.out.println(dateObj.toString());
but right now this prints out "Sat May 18 19:00:00 CDT 2013" (since my local machine is on CDT) but I want the result to be "Sat May 18 24:00:00 CDT 2013". How can i interpret the date string "1368921600000" as an EST date? Thanks, - Dave
1368921600000 is an instant in time, the same instant, everywhere in the world. To convert this to a date and time, you have to specify where in the world you want to know the date/time at that instant. It just so happens that the timestamp is relative to UTC, and is Sun, 19 May 2013 00:00:00 GMT.
If you want the time of this instant (the same instant) somewhere else in the world, you can use the Calendar as you did and extract the individual field values (e.g. HOUR_OF_DAY). If all you care about is getting a text string, you use a DateFormat instance such as SimpleDateFormat:
DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z");
df.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String timeOnUSEastCoast = df.format(new Date(1368921600000L));
// will be GMT-5 or GMT-4 depending on DST
In this example, the output would be GMT-4: Sat, 18 May 2013 20:00:00 EDT (not only has the hour changed, but it's still the previous day on the US east coast).
If you want to output the UTC time but just want to pretend it's EST then it's just easier to tell the DateFormat to leave off the time zone field (remove the "z") in the text output and call it whatever you want, but understand that the timestamp value is always UTC.
It is often not necessary to use the Joda Time library to get historically accurate time-zone- and daylight-savings-aware local time mappings, although this is the common go to response for many.
If you have a database of timestamps that require local time conversion, then here are some principles that have served me well:
Store date/times (Instants, in the parlance of Joda; Dates, in the parlance of the Java Calendar API) in UTC time. UTC does not care about DST. It does not care about time zones. UTC simply represents a universally representable moment in time. This step alone can save a lot of date/time headaches.
Database records should contain TimeZone and/or Locale fields so that mapping from UTC can be performed. Think about your data. Every timestamp in your database does not need time zone information associated with it. Instead, associate the time zone data with a part of your data model that provides appropriate granularity. If your app will only ever be localized in one time zone, then you needn't store this infor at all. In my recent project, I created a Locale table that contains the TZ ID for timestamps in my Encounters table. All other timestamps are subordinate to these records, so it made sense to associate it there.
Use the Java API GregorianCalendar to map UTC Dates to local time. And that's all I ever use it for. I almost never use GregorianCalendars to do date arithmetic or other date operations. Here is the paradigm that I've been working with:
public static void main(String[] args) {
m_GregorianCalendar = new GregorianCalendar(TimeZone.getTimeZone(
"America/Chicago"));
Date d = new Date();
String fmt = "dd-MMM-yyyy # HH:mm";
:
:
String myDate = mapToLocalTime(d, fmt, gc);
:
:
}
public String mapToLocalTime(Date utc, String format, GregorianCalendar gc) {
gc.setTime(utc); // this calendar is already timezone aware (constructed
// with time zone id (DST too))
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.setCalendar(gc); // formatter uses conventions of calendar
sdf.applyPattern(fmt); // pattern for formatter
return sdf.format(utc);
}
Consider representing timestamps internally in a numeric format (longs, doubles). This greatly simplifies date comparisons and date arithmetic. The only downside is that conversions must be done to format the data into a human recognizable form, but if you use functions for these conversions it need not be a big deal at all.
Use either a new java.util.SimpleDateFormat(format) or java.util.DateFormat.getDateTimeInstance(int,int), then use #setTimeZone(timezone).
When you're printing Date.toString(), the Calendar's time zone is not taken into consideration at all. After you do a cal.getTime(), whatever was set in Calendar is no longer relevant.
What is is TimeZone's default time zone.
So right before printing the date, set the default time zone to the one you want to print in, e.g.:
String dateStr = "1368921600000"; // This is 5/19/2013 00:00:00
final Calendar cal = Calendar.getInstance();
TimeZone gmtZero = TimeZone.getTimeZone("GMT");
cal.setTimeInMillis(Long.parseLong(dateStr));
final java.util.Date dateObj = cal.getTime();
TimeZone.setDefault(gmtZero);
System.out.println(dateObj.toString());
This will print the date in GMT regardless of your system time zone.
Just remember to bring back the original default TimeZone afterwards!

Java How to create a UTC and add few ms to the same UTC object

static String createUTCTime()
{
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.add(Calendar.SECOND, 10);
SimpleDateFormat f = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
f.setCalendar(cal);
return f.format(new Date());
}
essentially what I would like to do below
Calendar objectX = createUTCTime();
//2012-Nov-27 18:35:40
x.addMS(10);
//2012-Nov-27 18:35:50
How do you handle UTC times to manipulate them?
Date is just a point in time, it doesn't hold any time zone information. If you want to work with time zones and dates, you need Calendar:
Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
cal.add(Calendar.SECONDS, 10);
Now the tricky part, you cannot just convert it to Date (cal.getTime()) as it will loose time zone (but will still represent the same point-in-time). What you should do is create a SimpleDateFormat` configured to use UTC.
On the other hand if you only need seconds or milliseconds, Date is fine. Just remember to format it in correct time zone. But you shouldn't use Date for hours and everything above (assumption that hour is 3600000 milliseconds is sometimes wrong due to leap seconds, DST, etc.)
It's really unclear what you're trying to do - but if you're trying to create an object to work with further, you shouldn't be converting it into a string representation, which is what SimpleDateFormat.format does.
The Java API for working with dates and times is pretty broken, to be honest - you'd be much better off with Joda Time. For example:
DateTime x = new DateTime(DateTimeZone.UTC);
x = x.plusMillis(10);
Joda Time provides plenty of immutable types for dates and times, which are significantly better for readability than the mutable Calendar and Date types. Additionally, it supports a lot more types to represent different kinds of values.
Really, avoid java.util.Date/Calendar like the plague :)
You can use following code.
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.add(Calendar.MILLISECOND, 10);
DateFormat df = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
SimpleDateFormat f = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
f.setTimeZone(cal.getTimeZone());
System.out.println(f.format(cal.getTime()));
getTime provide Date object which do not have any information about timeZone. By Date object can display the date in GMT(toGMTString()) and local time zone(toLocalString()).
This code will provide you time in UTC, in the required format.

Date difference includes Timezone offset, what's wrong?

I have this code:
Date now = new Date();
// the string is in UTC format, so a UTC date must be constructed, I don't know if that happens in this format
Date measure = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(utcDateTime);
long diff = now.getTime() - measure.getTime();
if (diff < 1000* 60 * 15) {
// measure is less then 15 minutes recent
do some work
}
When I get the diff, it includes the timezone. I know the Date object internally is UTC.
So what's wrong here?
While a Date object is indeed in UTC, your SimpleDateFormat may not be. I suspect it default's to the system time zone - that's certainly what experimentation would suggest. You can change this using DateFormat.setTimeZone. So if your text represents a UTC date/time, you should set the time zone of the formatter to UTC as well.
Or you could use Joda Time, which is a generally better date and time API :)

Categories

Resources