I'm trying to find the difference between 2 times using Joda Time. I've tried using some of the examples on here and can not get a value to return. Here is what I've tried: Where time is in a format like 17:23 and the to and from variables are time zones like America/Los_Angeles or Europe/London
DateTime dtFrom = DateTime.now()
DateTime dtTo = new DateTime(dtFrom.withZone(DateTimeZone.forID(to)));
//calculate time difference
Period period = new Period(dtTo, dtFrom) ;
Minutes minutes = Minutes.minutesBetween(dtFrom, dtTo);
long diffInMillis = dtFrom.getMillis() - dtTo.getMillis();
I've tried using different methods to compare these two and always get no value returned...period returns PT0S, minutes returns PT0M, and diffInMillis returns 0. What am I doing wrong here? Thanks!
You are using the method withZone() which is defined to preserve the (POSIX) instant in milliseconds since 1970-01-01T00:00Z. So dtFrom and dtTo have still the same instant meaning they describe the same global time although their local time representations are different due to different time zones.
Result: The difference between two same instants is exactly equal to zero regardless which time unit you use.
However, if you are interested into the local time difference instead please determine the zone offset difference, for example using the method getOffset().
Related
Background
I'm using threetenbp backport for Android (here), to handle various time related data operations.
One of them is to convert a time to a different timezone (current to UTC and back).
I know this is possible if you use something like that:
LocalDateTime now = LocalDateTime.now();
LocalDateTime nowInUtc = now.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("UTC")).toLocalDateTime();
This works just fine, and it's also quite easy to do the opposite.
The problem
I'm trying to avoid initialization of the library, which loads quite a large file of zones into it. I've already figured out how to handle various date/time related operations without this, except this case of converting to UTC and back.
What I got has an error of a whole 1 hour off from the correct conversion.
What I've tried
This is what I've found and tried:
// getting the current time, using current time zone:
Calendar cal = Calendar.getInstance();
LocalDateTime now = LocalDateTime.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND) * 1000000);
//the conversion itself, which is wrong by 1 hour in my tests:
LocalDateTime alternativeNowInUtc = now.atZone(ZoneOffset.ofTotalSeconds(TimeZone.getDefault().getRawOffset() / 1000)).withZoneSameInstant(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0))).toLocalDateTime();
The question
What's wrong exactly with what I wrote? How can I get an alternative code for converting the time without initialization of the library?
Given an instance of LocalDateTime as input, how can I convert it from current timezone to UTC, and from UTC to current timezone ?
This is probably happening because your JVM's default timezone is in Daylight Saving Time (DST).
To get the correct offset, you should check if the timezone is in DST and add this to the offset:
Calendar cal = Calendar.getInstance();
TimeZone zone = TimeZone.getDefault();
// if in DST, add the offset, otherwise add zero
int dst = zone.inDaylightTime(cal.getTime()) ? zone.getDSTSavings() : 0;
int offset = (zone.getRawOffset() + dst) / 1000;
LocalDateTime alternativeNowInUtc = now.atZone(ZoneOffset.ofTotalSeconds(offset))
.withZoneSameInstant(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0)))
.toLocalDateTime();
Another way to create the nowInUtc as a LocalDateTime is to create an Instant from the Calendar:
LocalDateTime nowInUtc = Instant.ofEpochMilli(cal.getTimeInMillis())
.atOffset(ZoneOffset.ofHours(0)).toLocalDateTime();
Actually, you don't need the Calendar at all, just use Instant.now() to get the current instant:
LocalDateTime nowInUtc = Instant.now().atOffset(ZoneOffset.ofHours(0)).toLocalDateTime();
Or, even shorter, use an OffsetDateTime directly:
LocalDateTime nowInUtc = OffsetDateTime.now(ZoneOffset.ofHours(0)).toLocalDateTime();
Not sure if any of those loads timezone data, it's up to you to test.
And I think that the constant ZoneOffset.UTC can be used instead of ZoneOffset.ofHours(0), because it won't load tz data as well (but I haven't tested it).
Final solution
Assuming the default timezone is in Israel (TimeZone.getDefault() is Asia/Jerusalem):
// April 11th 2018, 3 PM (current date/time in Israel)
LocalDateTime now = LocalDateTime.of(2018, 4, 11, 15, 0, 0);
TimeZone zone = TimeZone.getDefault();
// translate DayOfWeek values to Calendar's
int dayOfWeek;
switch (now.getDayOfWeek().getValue()) {
case 7:
dayOfWeek = 1;
break;
default:
dayOfWeek = now.getDayOfWeek().getValue() + 1;
}
// get the offset used in the timezone, at the specified date
int offset = zone.getOffset(1, now.getYear(), now.getMonthValue() - 1,
now.getDayOfMonth(), dayOfWeek, now.getNano() / 1000000);
ZoneOffset tzOffset = ZoneOffset.ofTotalSeconds(offset / 1000);
// convert to UTC
LocalDateTime nowInUtc = now
// conver to timezone's offset
.atOffset(tzOffset)
// convert to UTC
.withOffsetSameInstant(ZoneOffset.UTC)
// get LocalDateTime
.toLocalDateTime();
// convert back to timezone
LocalDateTime localTime = nowInUtc
// first convert to UTC
.atOffset(ZoneOffset.UTC)
// then convert to your timezone's offset
.withOffsetSameInstant(tzOffset)
// then convert to LocalDateTime
.toLocalDateTime();
The answer of carlBjqsd is okay, just awkward and should maybe a little bit clearer.
Why one hour difference
See the final solution of #carlBjqsd: It uses the expression
int offset = zone.getOffset(1, now.getYear(), now.getMonthValue() - 1, now.getDayOfMonth(), dayOfWeek, now.getNano() / 1000000);
instead of
getRawOffset().
That has caused the difference of one hour you observed. Applications have normally no need only to calculate with the raw offset which leaves out the dst-offset for some periods of the year. It is only the total offset which matters in any conversion from local timestamp to UTC and back. The main purpose of the fine-granular differentiation of partial offsets like raw offsets or dst offsets is just proper naming of the zone (shall we call it standard time or not?).
Misleading title of question: "without loading zones"
No, you can never avoid loading zones if you want to convert between local timestamps and UTC using zones. Your real question is rather: How to avoid loading the zones of ThreetenABP and to use/load the zones of the Android platform instead. And your motivation seems to be:
I'm trying to avoid initialization of the library, which loads quite a
large file of zones into it
Well, I have not measured which zone data have more impact on performance. I can only say based on my studies and knowledge of the source code of involved libraries that java.time and ThreetenBP load the whole file TZDB.dat into a binary array cache in memory (as first step) and then pick out the relevant part for a single zone (i.e. interprete a part of the binary data array via deserialization into a set of zone rules and finally a single ZoneId). Old Java platforms instead work with a set of different zi-files (one for each zone), and I suspect that Android zones behave in a similar way (but please correct me if you know that detail better).
If only ONE zone shall be used at all then the traditional approach of using separate zone files might be better but once you want to iterate over all available zones then it is better to have only one zone file at all.
Personally, I think that the performance aspect is neglectable. If you use the Android zones you will also have some loading times, inevitably. In case you really want to speed up the initialization time of ThreetenABP, you should consider to load it in a background thread.
Are Android zones and ThreetenABP zones equivalent?
Generally not. Both timezone repositories might give the same offset for a concrete zone. And often they do so but sometimes there will be differences which are not under your control. Although both timezone repositories use the data of iana.org/tz in final consequence, differences are mainly caused by possible different versions of tzdb-data. And you cannot control which version of zone data exists on the Android platform because this is up to the user of mobile phone how often he/she updates the Android OS. And this is also true for the data of ThreetenABP. You can offer the latest version of your app including the latest version of ThreetenABP but you cannot control if the mobile device user really updates the app.
Other reasons why to care about choosing the proper tz repository?
Beyond performance and initialization times, there is indeed one special scenario which might be interesting for the choice. If the Android OS is somehow old and uses an outdated version of zone rules then some mobile phone users do not update their operating system but manipulate the device clock in order to compensate the wrong timezone data. This way, they still get the correct local times on the mobile phone (in ALL apps).
In this scenario, ThreetenABP does not offer a good solution because combining their correct zone data with wrong device clock will result in wrong local timestamps (annoying the user). This has been a problem for example in Turkey which changed the dst-rules not a long time ago.
Using just the old calendar and timezone API of Android (in the package java.util) can take into account the problem so correct local timestamps are created. However, if an app communicates UTC-times (for example as count of millisecs since 1970-01-01T00Z) to other hosts (for example servers) then the wrong device clock is still a problem.
We could say why bother because the user has done "nonsense" with the device configuration but we also live in real world and should think about how to make even such users happy. So when thinking about a solution I had introduced at least in my calendar library Time4A methods like SystemClock.inPlatformView() which uses the (probably) most actual zone data and obtains the correct UTC clock based on the assumption that the user will at least observe correct local device time (whatever he/she had done to achieve this goal, either by updating the OS or by clock/zone configuration). I am quite happy with avoiding the old calendar and zone API altogether this way. My API even allows to simultaneously use both zone repositories:
Timezone.of("java.util.TimeZone~Asia/Jerusalem") // uses Android data
Timezone.of("Asia/Jerusalem") // uses Time4A data
Maybe you can profit from these ideas when to find/develop suitable helper classes for your usage of ThreetenABP. Time4A is open source.
I am trying to parse date string with timezone using this code for tests:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZZZZZ", Locale.US);
Calendar calendar = Calendar.getInstance();
calendar.setTime(sdf.parse("2017-07-26T06:00-06:00"));
int offset = calendar.getTimeZone().getRawOffset();
I am trying to change timezone from -06 to +09, but offset always contains 10800000.
How to parse date with timezone correctly (I need time and timezone both)?
Note: -06:00 is an offset, not a timezone - those 2 concepts are related, but they are different things (more on that below).
The problem with SimpleDateFormat and Calendar is that they use the system's default timezone, so even though you parse a date with a different offset (like -06:00), the resulting Calendar will have the default timezone (you can check what zone is by calling TimeZone.getDefault()).
That's just one of the many problems and design issues of this old API.
Fortunately, there's a better alternative, if you don't mind adding a dependency to your project (in this case, I think it's totally worth it). In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP to make it work (more on how to use it here).
To work with offsets, you can use the org.threeten.bp.OffsetDateTime class:
// parse the String
OffsetDateTime odt = OffsetDateTime.parse("2017-07-26T06:00-06:00");
This will parse all the fields correctly (date/time and offset). To get the offset value, similar to calendar.getTimeZone().getRawOffset(), you can do:
// get offset in milliseconds
int totalSeconds = odt.getOffset().getTotalSeconds() * 1000;
I had to multiply by 1000 because calendar returns the value in milliseconds, but ZoneOffset returns in seconds.
To convert this to another offset (+09:00), it's straightforward:
// convert to +09:00 offset
OffsetDateTime other = odt.withOffsetSameInstant(ZoneOffset.ofHours(9));
As I said, timezone and offset are different things:
offset is the difference from UTC: -06:00 means "6 hours behind UTC" and +09:00 means "9 hours ahead UTC"
timezone is a set of all the different offsets that a region had, has and will have during its history (and also when those changes occur). The most common cases are Daylight Saving Time shifts, when clocks change 1 hour back or forward in a certain region. All these rules about when to change (and what's the offset before and after the change) are encapsulated by the timezone concept.
So, the code above works fine if you're working with offsets and wants to convert to a different one. But if you want to work with a timezone, you must convert the OffsetDateTime to a ZonedDateTime:
// convert to a timezone
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("Asia/Tokyo"));
// get the offset
totalSeconds = zdt.getOffset().getTotalSeconds() * 1000;
The getOffset() method above will check the history of the specified timezone and get the offset that was active in that corresponding instant (so, if you take a date during DST, for example, the offset (and also date and time) will be adjusted accordingly).
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 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.
I have timer for a task. And all the sessions will be added up to each other.
Let's say today user spent 5 minutes
Other day he spent 1 hour, here this one hour will be added to the 5 minutes
and so on..
So it will be the total time in one value..
How can I do this ? Is it by Milliseconds or Date object ?
Date is more useful when you are dealing with actual calendar dates.
If you just want to keep track of time intervals/durations, just have a long variable and keep on adding the durations to this.
EDIT : Long.MAX_VALUE is 9,223,372,036,854,775,807. So, you don't really need to worry about the overflow either.
You can keep track of all the milliseconds using Date().getTime(), which returns the time since Epoch. So whenever you need to add/remove, just take your Date object, invoke .getTime(), and add/remove from the total. Then when you're done, you can convert the milliseconds to whatever format you need.
What I need is a time difference between specific timezone ("Russia/Moscow") and local time of the user, difference should be in hours.
I run into problem that the Difference of Time Zones is sometimes false calculated. I calculate difference (in hours) between local offset to UTC of android device and remote offset to UTC. For most user it is fine.. but many user are complaining about the problem.. I am not able to reproduce it at my phone or emulators.
The "wrong" displayed time difference is all ways 1 hour less.
In Moscow it is 15:00, in Europe 12:00. But the user see the offset of 2 hours
here is my code.
String tz="Europe/Moscow"
Calendar mCalendar = new GregorianCalendar();
mCalendar.setTimeZone( TimeZone.getTimeZone(tz));
TimeZone mTimeZone = mCalendar.getTimeZone();
int remote = mTimeZone.getRawOffset()/1000/60/60;
Calendar mCalendar2 = new GregorianCalendar();
TimeZone mTimeZone2 = mCalendar2.getTimeZone();
int local = mTimeZone2.getRawOffset()/1000/60/60;
return local - remote;
You are making the common mistake of equating a Time Zone with a Time Zone Offset. They are two different things. Please read the timezone tag wiki.
When you call getRawOffset, that returns the standard offset for that time zone. To get the offset that's in effect at a particular point in time, you can use getOffset, which takes a parameter of the timestamp for the point in time you are talking about.
Consider the following code, which returns the difference that is currently in effect:
long now = System.currentTimeMillis();
String tz = "Europe/Moscow";
TimeZone mTimeZone = TimeZone.getTimeZone(tz);
int remote = mTimeZone.getOffset(now);
TimeZone mTimeZone2 = TimeZone.getDefault();
int local = mTimeZone2.getOffset(now);
double differenceInHours = (local - remote) / 3600000.0;
return differenceInHours;
Note a couple of things:
I did not need a Calendar class.
The offsets are both for the same "now". You will get different results depending on when you run it.
Not all offsets use a whole number of hours, so this function should return double, not int. For example, try Asia/Kolkata, which uses UTC+5:30 the whole year, or Australia/Adelaide, which alternates between UTC+9:30 and UTC+10:30.
Consider also using Joda-Time, which is a much more robust way of working with time in Java.
I'm trying to setup a RPG that will keep track of a virtual time. After some reading GregorianCalendar seems to be the best way to do this but I have some questions and hoping someone with experience could give me some insight.
Here is what I'm trying to do. The game will start in the year 1675 and run for about 30 years. At that point the character will retire if they have survived that long. The character will be able to choose from actions I've preset for them through the coarse of the game. Some actions will be short and take a hour, others may take a week or a month. The real question comes from me using SQLite heavily. I want to save the current time as well as how long an action will take in my database. My first thought when setting this up was, if I want to start the game in Aug 15, 1675 to have my data base with 3 rows, set those fields to 8, 15, 1675. Then also have 2 more columns for the time. Pull these int via cursor and set them with something like
GregorianCalendar time = new GregorianCalendar();
time.set(year, month, date, hour, minute);
I figured I would pull how long an action takes in a similar fashion. Have an int X, and then a string to tell whether this time is in minutes, hours, days etc. Then either add this int to the int pulled from database to add to the calendar directly.
Q: If I add it directly to the calendar how would I pull int values from the calendar to store in database to load at a later time (when the player loads their game)?
Q: If I add it to the int stored in the database and set the calendar with this new int what will happen when I add enough to make the int out of scope for the calendar (Date is set to 31 but I add another day)?
You can convert from the GregorianCalendar object to/from UNIX time for example using getTimeInMillis() setTimeInMillis(). GregorianCalendar also has a roll() method:
Adds the specified amount to the specified field and wraps the value
of the field when it goes beyond the maximum or minimum value for the
current date. Other fields will be adjusted as required to maintain a
consistent date.
I would recommend using Joda Time as a substitute for the (somewhat lacking) standard java date and time utilities. It's much more flexible.
It has functions to do date math, it supports several different calendars (ISO8601, Buddhist, Coptic, Ethiopic, Gregorian, GregorianJulian, Islamic, Julian), has support for intervals, durations and periods. It has built in formatters that let you make your output look like just about anything you wish.
If it were me, I would use Joda and store the date in the native format presented by Joda (I don;t remember what that is right offhand) and then pull it out again and use Joda to do all the date math, as well as having it convert it to whatever calendar you wish to use for display to the user.
Otherwise, it seems to me you'd be re-inventing the wheel.