In the following code, range equals durationInDays only when range is less than 30. If it is equal to 30 or greater, durationInDays is always range - 1
Calendar c = Calendar.getInstance();
Date now = new Date();
c.setTime(now);
int range = 35;
c.add(Calendar.DATE, range);
Date then = c.getTime();
Duration duration = Duration.between(now.toInstant(), then.toInstant());
int durationInDays = (int)duration.toDays();
When debugging this example, the variables are set as follows:
this = {CalTest#871}
c = {GregorianCalendar#876} "java.util.GregorianCalendar[time=1522449516301,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/Dublin",offset=0,dstSavings=3600000,useDaylight=true,transitions=228,lastRule=java.util.SimpleTimeZone[id=Europe/Dublin,offset=0,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2018,MONTH=2,WEEK_OF_YEAR=13,WEEK_OF_MONTH=5,DAY_OF_MONTH=30,DAY_OF_YEAR=89,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=11,HOUR_OF_DAY=23,MINUTE=38,SECOND=36,MILLISECOND=301,ZONE_OFFSET=0,DST_OFFSET=3600000]"
now = {Date#877} "Fri Feb 23 23:38:36 GMT 2018"
range = 35
then = {Date#878} "Fri Mar 30 23:38:36 IST 2018"
duration = {Duration#879} "PT839H"
durationInDays = 34
Why does then have an IST timezone? This difference is the causing duration to be a little less than 35 days, rounding to 34.
First, then hasn’t got IST timezone. A Date hasn’t got any time zone. Date.toString chooses a time zone, usually the JVM’s time zone setting, for generating the string only.
Looking at how your two Date objects are rendered in the debugger, one might wonder that it would appear they are rendered in two different time zones. They are not. Both are in Europe/Dublin time zone. As you are probably aware, Ireland with most of the EU switches to summer time (DST) on the last Sunday in March. Therefore, your date in February is in standard time, which in Ireland coincides with GMT, and therefore your string is rendered with GMT as “time zone”. On March 30, summer time is in effect, so the time zone is rendered as IST for Irish Summer Time this time. Edit: the transistion to summer time also accounts for the missing hour compared to your expected duration of 35 days (840 hours).
Edit: Since you can use java.time, the modern Java date and time API, I suggest you go all in and forget about the old-fashioned Date and Calendar. The modern API is so much nicer to work with, and you won’t need all the conversions any longer:
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Europe/Dublin"));
int range = 35;
ZonedDateTime then = now.plusDays(range);
Duration duration = Duration.between(now, then);
int durationInDays = (int) duration.toDays();
The result is still PT839H and hence 34 days.
To obtain a duration in days that agrees with the number of days we added:
int durationInDays = (int) ChronoUnit.DAYS.between(now, then);
System.out.println(durationInDays);
This prints
35
Duration is mostly for durations in hours, minutes and seconds. It does support days, but only days at 24 hours each, so gives the surprising result you saw when used across the summer time transition. ChronoUnit.DAYS on the other hand is exactly made for days. It sees that the two ZonedDateTime instances have the same time-of-day and therefore acknowledges a full 35 days between the two.
Related
The following is an example that attempts to calculate the difference in hours between time zones.
import java.util.*;
import java.text.*;
public class ForPosting
{
public static void main (String[] args)
{
try {
String myDateString = "07-13-2021 11:00:00";
SimpleDateFormat localDateTime = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
SimpleDateFormat utcDateTime = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
localDateTime.setTimeZone(TimeZone.getTimeZone("EET"));
System.out.println(localDateTime.parse(myDateString));
utcDateTime.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(utcDateTime.parse(myDateString));
float diff = ((localDateTime.parse(myDateString).getTime() - utcDateTime.parse(myDateString).getTime() ) / 3600000);
System.out.println("EET & UTC Time Difference : " + diff);
localDateTime.setTimeZone(TimeZone.getDefault());
System.out.println(localDateTime.parse(myDateString));
utcDateTime.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(utcDateTime.parse(myDateString));
diff = ((localDateTime.parse(myDateString).getTime() - utcDateTime.parse(myDateString).getTime() ) / 3600000);
System.out.println("Default & UTC Time Difference : " + diff);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The above code generates the following output.
The Default & UTC Time Difference : 4.0 was confirmed by checking the current dates and it confirms there is a 4 hour difference.
Doing the same check for EET & UTC is different.
EET & UTC Time Difference : -3.0
The output shows a 3 hour difference yet the following shows there is only 2.
Why does the EET & UTC actual time show 2 hour difference yet the Java code shows a 3 hour difference?
EET is not a time zone
EET is a common abbreviation of some 20 time zones in Europe and Northern Africa that are at offset +02:00 from UTC during standard time. Many, not all of them are using summer time (daylight saving time) abbreviated EEST for Eastern European Summer Time and hence are at offset +03:00 during the 7 “summer” months. So what you get when asking for EET, I can’t tell. So don’t do that. Use a proper time zone ID like Europe/Uzhgorod, always in region/city format. So that you know what you get.
So to answer your question:
Why does the EET & UTC actual time show 2 hour difference yet the Java
code shows a 3 hour difference?
Because they interpret EET differently. Both interpretations are fully valid and none is authoritative.
This is nothing special for EET. Many two, three, four and five letter time zone abbreviations are in common use. Very many of them are ambiguous, like EET. And very many of them are not time zones. It may be that there are situations where you can use one for informal communication to the user, for example just to distinguish between EET and EEST in a situation where the user knows well enough what each of those means in the context. You certainly cannot rely on them for defining a time zone in your program.
java.time
I recommend that you use java.time, the modern Java date and time API, for your date and time work. To find the difference between some time zone and UTC at a moment in time:
ZoneId zone = ZoneId.of("Europe/Uzhgorod");
Instant when = Instant.now();
ZoneOffset differenceFromUtc = when.atZone(zone).getOffset();
System.out.format("%s & UTC Time Difference : %s%n", zone, differenceFromUtc);
Output when running just now:
Europe/Uzhgorod & UTC Time Difference : +03:00
It’s summer now in Europe/Uzhgorod, and the difference of 3 hours comes from that time zone using summer time. Try with an instant in winter:
Instant when = Instant.parse("2022-01-01T00:00:00Z");
Europe/Uzhgorod & UTC Time Difference : +02:00
As I said, some Eastern European time zones do not use summer time. Try with one of those:
ZoneId zone = ZoneId.of("Europe/Kaliningrad");
Instant when = Instant.now();
Europe/Kaliningrad & UTC Time Difference : +02:00
Minor comments to your code
While UTC works for defining a time zone, the canonical ID is Etc/UTC. Etc is a pseudo-region for time zones that don’t belong in a well-defined geographical region including Etc/UTC, Etc/GMT and also the IDs for mere whole-hour offsets like Etc/GMT-2 (for offset +02:00, the sign is inverted).
Assigning the result of your division to a float does not help you. You are dividing a long values by an int value, and the result is always truncated to an long number. When assigning to a float you will still always have a whole number. If you wanted a fraction as result, you would need to convert at least one of the operands to float (or double) before dividing.
And a funny detail, your code measures the difference in hours between when it’s 11 o’clock in EET and 11 o’clock in UTC. You get a result exactly because there are 3 hours between those two moments. What I am not so pleased about is that you are not measuring the difference at a well-defined point in time. Instead in my code I am choosing one point in time and finding the difference at that point.
Link
Oracle tutorial: Date Time explaining how to use java.time.
Based on Epoch seconds, I convert it to the start of hour and end of hour.
long epochSeconds = 1589374800L;
Instant instant = Instant.ofEpochSecond(epochSeconds);
Calendar now = Calendar.getInstance(TimeZone.getTimeZone(ZoneId.of("Asia/Kolkata")));
now.setTimeInMillis(instant.toEpochMilli());
System.out.println(now.getTime()); // Correct --> Wed May 13 06:00:00 PDT 2020
Calendar endOfHour = (Calendar)now.clone();
endOfHour.set(Calendar.MINUTE, 59);
endOfHour.set(Calendar.SECOND, 59);
endOfHour.set(Calendar.MILLISECOND, 999);
System.out.println(endOfHour.getTime()); // Wrong ---> Wed May 13 06:29:59 PDT 2020
The start of hour seems correct, but the end of hour is not giving it right, instead of upto 59 minute, 59 second, 999 millisecond it is giving only half hour difference.
You are mixing java.time and java.util.Calendar types. Don't do that. For one thing, you're losing the TimeZone you specified when you clone. Basically, Calendar is a mess. But you don't need it here, something like
long epochSeconds = 1589374800L;
LocalDateTime date = Instant.ofEpochSecond(epochSeconds) //
.atZone(ZoneId.of("Asia/Kolkata")) //
.toLocalDateTime();
System.out.println(date);
LocalDateTime endOfHour = date.withMinute(59) //
.withSecond(59) //
.with(ChronoField.MILLI_OF_SECOND, 999);
System.out.println(endOfHour);
Should meet your needs. Here that outputs
2020-05-13T18:30
2020-05-13T18:59:59.999
Two points (and one more at the end):
Just repeating what has already been said: don’t mix old and modern date-time classes. Use the modern ones exclusively. Forget about the old ones. They were always poorly designed anyway.
Your observed result is correct and as should be expected.
You are using Asia/Kolkata time zone for your (outmoded) Calendar object. Asia/Kolkata time zone is special in that it is not offset a whole number of hours from UTC as most time zones are, but +05:30, five and a half hours. Let’s look at your times in this time zone first. Your epoch second value (AKA Unix timestamp) is equal to 2020-05-13T18:30:00+05:30 in Asia/Kolkata. The end of that hour in Asia/Kolkata is 2020-05-13T18:59:59.999. This is the result that you get.
It seems that you are running your program on a JVM in a different time zone (perhaps America/Vancouver or America/Los_Angeles). This time zone is offset a whole number of hours from UTC, so 18:59:59.999 in India equals 06:29:59 PDT (your time zone).
Half-open:
I promised you a third point. Represent the end of the hour as the whole hour, here 19:00 rather than 18:59:59 and some number of 9s. The philosophical argument: The hour doesn’t end one millisecond before the next hour begins, so this is incorrect. The practical argument: It frees you from deciding how many 9s you need. java.time has nanosecond precision, so is able to represent nearly a million points in time between your end of the hour and the beginning of the next hour. You risk hitting such a point and assigning it to the wrong hour. In comparisons just make sure that you are checking whether a point in time is strictly before the whole hour where the hour ends.
If you do need Calendar objects for a legacy API:
ZoneId zone = ZoneId.of("Asia/Kolkata");
long epochSeconds = 1589374800L;
ZonedDateTime now = Instant.ofEpochSecond(epochSeconds).atZone(zone);
Calendar nowAsOldfashionedCalendar = GregorianCalendar.from(now);
ZonedDateTime endOfHour
= now.plusHours(1).truncatedTo(ChronoUnit.HOURS).minusNanos(1);
Calendar endOfHourAsOldfashionedCalendar = GregorianCalendar.from(endOfHour);
I included .minusNanos(1) to get the last nanosecond of the previous hour, but as I said, you should prefer to omit it if you can. The conversion to GregorianCalendar will truncate to milliseconds and give you the same result as in your code in the question.
This is regarding Java Calendar and the effects we encountered after today day light saving change in Toronto.
Below is the code
Date date = new Date(); //Sun Mar 11 00:00:00 EST 2018
Integer time = 349;
Calendar scheduleDateCal = Calendar.getInstance();
scheduleDateCal.setTime(date);
scheduleDateCal.set(Calendar.MINUTE, 0);
scheduleDateCal.set(Calendar.HOUR_OF_DAY, 0);
String strSchAdminTime = String.valueOf(time);
Integer schAdminMinute = time;
if (strSchAdminTime.length() >= 2) {
schAdminMinute = Integer.valueOf(strSchAdminTime.substring(strSchAdminTime.length()-2));
}
if(time>60){
Integer schAdminHour = Integer.valueOf(strSchAdminTime.substring(0,strSchAdminTime.length()-2));
scheduleDateCal.add(Calendar.HOUR_OF_DAY, schAdminHour);
}else{
scheduleDateCal.add(Calendar.HOUR_OF_DAY, 0);
}
scheduleDateCal.add(Calendar.MINUTE, schAdminMinute);
System.out.println(scheduleDateCal.getTime());
I know this code hasn't done with the best practises however I need to maintain it for the current release. In here it uses integer to represent the time portion and later there is a logic to extract the hours and minutes from it.
When I followed the logic, the hour portion is 3. Then there is a logic to add this time to Calendar object with value 'Sun Mar 11 00:00:00 EST 2018' with below statement
scheduleDateCal.add(Calendar.HOUR_OF_DAY, schAdminHour);
Theoretically after this calculation, the calendar object should have value "Sun Mar 11 03:00:00 EDT 2018". However it returns "Sun Mar 11 04:00:00 EDT 2018" I know starting from today the time will go one hour ahead with the daylight saving. Can any one please help me to understand this
Appreciate the help.
At 00:00:00 today (Sunday March 11, 2018) summer time (DST) was not yet in effect, so that time was correctly rendered as Sun Mar 11 00:00:00 EST 2018 (EST for Eastern Standard Time). Date.toString chooses between EST and EDT based on the time contained in the Date object (not based on the time the toString method is called). When you add 3 hours to that time, you cross the time at 2 when the clock was turned forward to 3. So 3 hours after your start time the time is 04:00:00 EDT (EDT for Eastern Daylight Time).
PS Modern code
PS In case you or someone else is interested, here is the modern — both simpler and shorter — version of your code. To set the time to 03:49:
int time = 349;
ZoneId zone = ZoneId.of("America/Toronto");
ZonedDateTime scheduledDateTime = LocalDate.now(zone)
.atTime(time / 100, time % 100)
.atZone(zone);
System.out.println(scheduledDateTime);
Today this printed
2018-03-11T03:49-04:00[America/Toronto]
Still better, of course, if you can get completely rid of representing 03:49 as the integer value 349. To use your JVM’s time zone setting you may set zone to ZoneId.systemDefault(). This is fragile because the setting may be changed at any time by other parts of your program or other programs running in the same JVM.
To set the time to 3 hours 49 minutes after midnight (which with DST transition isn’t the same thing, as you have seen):
ZonedDateTime scheduledDateTime = LocalDate.now(zone)
.atStartOfDay(zone)
.plusHours(time / 100)
.plusMinutes(time % 100);
This time I got
2018-03-11T04:49-04:00[America/Toronto]
EST is -5, EDT is -4, so you get 1 hour when you are calling add().
You can use scheduleDateCal.set(Calendar.HOUR_OF_DAY, schAdminHour) and scheduleDateCal.set(Calendar.MINUTE, schAdminMinute), if you need result in different timezone.
DateFormat df1 = new SimpleDateFormat("kk:mm");
Date d = null;
try {
d = df1.parse("21:00");
} catch (ParseException e) {
e.printStackTrace();
}
I'm getting 61200000 milliseconds but when I convert it online, I get a different value.
SimpleDateFormat is not so simple as the name suggests. In particular its handling of time zone is sometimes obscure. In this case I dare say it uses your JVM’s time zone setting (probably taken over from your computer’s setting), apparently one that is offset 4 hours from UTC. So when the time is 21:00 (9 pm) in your time zone, it’s 17:00 in UTC. The milliseconds value in a Date object is always in UTC, therefore you get 17 * 60 * 60 * 1000 = 61 200 000. If you try it online on a server in a different time zone, you’re likely to get a different result.
java.time.Duration
I think what you really want is Duration.parse("PT21H0M"). This will give you a duration of 21 hours 0 minutes. You can use its toMillis method to get 21 * 60 * 60 * 1000 = 75 600 000.
The Java 8 date and time classes (of which Duration is just one) are generally much more pleasant to work with than the old classes.
Or you can pass a number of hours.
Duration d = Duration.ofHours( 21 );
ISO 8601 duration
The syntax PT21H0M may look a bit odd at first, but its an international standard, ISO 8601, so we’d probably better learn to work with it, and it’s straightforward to learn.
If you do this:
d = df1.parse("21:00");
you get this date
Thu Jan 01 21:00:00 CET 1970
and the epoch is 72000000, to convert that, you need to divide by 1000
verifiable here
I would like to represent 1 day from current date as epoch time. Today's date is Oct 20th. So I would like to get the epoch time for tomorrow. I have the below code -
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.add(Calendar.DAY_OF_YEAR, 1);
int time = (int) ((cal.getTimeInMillis())/1000);
System.out.println("TIME IS: " +time);
So I basically want to add 1 day to current day of the year i.e. Oct 20th, and represent that as epoch time. I am displaying the time in seconds, hence the divide by 1000. The result for the program is 1413958770. 1 day from today would be 86400 seconds. How exactly does epoch time work? I understand it gives time since 1970, but if that is indeed the case, the value can't be that small :-). Am I missing something here?
Let's do some basic math:
a minute has 60 seconds
an hour has 60 minutes
a day has 24 hours
a year has 365 days
1413958770 / (60 * 60 * 365 * 24) = 44,8
(test it)
Add 44,8 years to 1970, and you'll be in 2014. So nothing is wrong.
If "today" is the epoch, then it should "represent" 0, any time measured from or compared to "now" should be subtracted from it, for example...
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
long now = cal.getTimeInMillis();
cal.add(Calendar.DAY_OF_YEAR, 1);
long then = cal.getTimeInMillis();
long secondsFromEpoch = (then - now) / 1000;
System.out.println(secondsFromEpoch);
Unix time or POSIX time or Epoch time is a system for describing instants in time, defined as the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 1970, not counting leap seconds.
In Java, you can simply get it using below statement.
long current_time = System.currentTimeMillis() * 1000000L;
or
long epoch = date.getTime();
There have been a couple dozen epochs used by various computer systems. The beginning of 1970 UTC is common, but certainly not the only epoch in current use. And various systems track time since epoch using whole seconds, milliseconds, microseconds, and nanoseconds. So rather than use the vague term "epoch time", it would be more useful to directly say what you want. In this case it seems to be "whole seconds since beginning of 1970 UTC until tomorrow".
Perhaps you were just curious about the math and numbers. But if you were serious about the date-time work, then I advise using the Joda-Time library or the java.time package in Java 8.
Time zone is crucial when taking about dates and defining the beginning of a day.
Here is example code using Joda-Time 2.5.
DateTimeZone zone = DateTimeZone.forID( "Australia/Melbourne" );
DateTime tomorrowStart = DateTime.now( zone ).plusDays( 1 ).withTimeAtStartOfDay();
long secondsSinceUnixEpoch = ( tomorrowStart.getMillis() / 1000 );