I would like to save some user timezone in a Daylight saving proof format.
My goal is to get the correct GMT offset whenever the code gets executed.
In order to figure out my best option, I wrote the following:
ArrayList<String> list = new ArrayList<String>();
list.add( "EST");
list.add( "EDT");
list.add( "America/New_York");
long now = System.currentTimeMillis();
for( String tzID: list) {
TimeZone tz = TimeZone.getTimeZone( tzID);
System.out.println( tzID + " now=" + tz.getOffset( now) / 3600000 + " / +182=" + tz.getOffset( now + ( 182 * 86400000)) / 3600000);
}
For short, give me the offset now and in 182 days
Executed September 3rd, the output is
EST now=-5 / +182=-5
EDT now=0 / +182=0
America/New_York now=-4 / +182=-4
This is unexpected for several reasons
1) Why is America/New_York not giving -4/-5 ?, Isn't it supposed to be date sensitive?
2) Why does EDT == UTC?
java.time
The question and the accepted answer use the java.util date-time API which was the right thing to do in 2012. In March 2014, the modern Date-Time API was released as part of the Java 8 standard library which supplanted the legacy date-time API and since then it is strongly recommended to switch to java.time, the modern date-time API.
Solution using java.time
You can use ZonedDateTime which automatically adjusts the time zone offset for a given ZoneId.
Demo:
import java.time.ZoneId;
import java.time.ZonedDateTime;
class Main {
public static void main(String[] args) {
ZoneId zone = ZoneId.of("America/New_York");
ZonedDateTime now = ZonedDateTime.now(zone);
ZonedDateTime after182Days = now.plusDays(182);
System.out.println(zone + " now=" + now.getOffset() + " / +182=" + after182Days.getOffset());
}
}
Output as of now:
America/New_York now=-05:00 / +182=-04:00
ONLINE DEMO
Learn more about the modern Date-Time API from Trail: Date Time.
Do not use three-letter timezone ID: Note from the Java 7 Timezone documentation:
Three-letter time zone IDs
For compatibility with JDK 1.1.x, some other three-letter time zone IDs (such as "PST", "CTT", "AST") are
also supported. However, their use is deprecated because the same
abbreviation is often used for multiple time zones (for example, "CST"
could be U.S. "Central Standard Time" and "China Standard Time"), and
the Java platform can then only recognize one of them.
One problem you have is that 182 * 86400000 overflows. If you use
long now = System.currentTimeMillis();
for( String tzID: "EST,EDT,America/New_York".split(",")) {
TimeZone tz = TimeZone.getTimeZone( tzID);
System.out.println( tz.getDisplayName() + " now=" + tz.getOffset( now) / 36e5
+ " / +182=" + tz.getOffset( now + 182 * 86400000L) / 36e5);
}
prints
Eastern Standard Time now=-5.0 / +182=-5.0
Greenwich Mean Time now=0.0 / +182=0.0
Eastern Standard Time now=-4.0 / +182=-5.0
If you look at the javadoc and source for getTimeZone you can see
* #return the specified <code>TimeZone</code>, or the GMT zone if the given ID
* cannot be understood.
public static synchronized TimeZone getTimeZone(String ID) {
return getTimeZone(ID, true);
}
private static TimeZone getTimeZone(String ID, boolean fallback) {
TimeZone tz = ZoneInfo.getTimeZone(ID);
if (tz == null) {
tz = parseCustomTimeZone(ID);
if (tz == null && fallback) {
tz = new ZoneInfo(GMT_ID, 0);
}
}
return tz;
}
In short, EDT is not recognised so it becomes GMT.
I suspect this is the problem:
now + ( 182 * 86400000)
The parenthesized arithmetic expression overflows 32 bits. You probably want:
now + ( 182 * 86400000L)
However, that still assumes that any daylight saving time will be applied for roughly six months, which is certainly not the case in the real world. For example, looking at the Sao Paolo time zone, it switches in October and February - so if you ran your code in September, you'd end up seeing -3 / -3. Even for time zones where DST switches on/off roughly every six months, you're very likely to find 182 consecutive days each year without a switchover (almost by definition, given that that's slightly less than half a year).
It's not clear exactly what you're trying to do, but I suspect you should really just be saving the time zone ID, e.g. "America/New_York". Almost anything else is asking for trouble.
Related
Initialize java.util.Calendar with May, 31 1900. Then add one year to it twenty times.
Here's code:
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
fun main(args : Array<String>) {
val f = SimpleDateFormat("yyyy.dd.MM")
val cal = Calendar.getInstance()
cal.set(1900, Calendar.MAY, 31)
for(i in 1..20) {
println(f.format(cal.time))
cal.add(Calendar.YEAR, 1)
}
}
The output is following:
1900.31.05
1901.31.05
1902.31.05
1903.31.05
1904.31.05
1905.31.05
1906.31.05
1907.31.05
1908.31.05
1909.31.05
1910.31.05
1911.31.05
1912.31.05
1913.31.05
1914.31.05
1915.31.05
1916.31.05
1917.31.05
1918.01.06
1919.01.06
Why I get June, 1 instead of May, 31 since 1918?
UPD: with time information
1917.31.05 23:38:50.611
1918.01.06 01:38:50.611
If this is DST invention, how do I prevent that?
You seem to be running your code in a timezone that changed its offset by two hours in 1917 or 1918. That is, the number of hours ahead or behind UTC changed. I've no idea why your timezone would have done that, but I'm sure there's a good historical reason for it.
If you're only interested in dates, without the Time component, use the java.time.LocalDate class, which effectively represents a day, month and year only. It's not subject to any daylight savings anomalies.
LocalDate today = LocalDate.now();
or
LocalDate moonLanding = LocalDate.of(1969, 7, 20);
I am assuming that you are in Europe/Moscow time zone. Turing85 in a comment correctly spotted the cause of the behaviour you observed: In 1918 summer time (DST) in your time zone began on May 31. The clock was moved forward from 22:00 to 24:00, that is, by two hours. Your Calendar object is aware of this and therefore refuses to give 23:38:50.611 on this date. Instead it picks the time 2 hours later, 1918.01.06 01:38:50.611. Now the month and day-of-month have changed to 1st of June.
Unfortunately this change is kept in the Calendar and carried on to the following year.
If this is DST invention, how do I prevent that?
Thomas Kläger in a comment gave the right solution: If you only need the dates, use LocalDate from java.time:
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("uuuu.dd.MM");
LocalDate date = LocalDate.of(1900, Month.MAY, 31);
for (int i = 1; i <= 20; i++) {
System.out.println(date.format(dateFormatter));
date = date.plusYears(1);
}
Output (abbreviated):
1900.31.05
1901.31.05
…
1917.31.05
1918.31.05
1919.31.05
The “local” in LocalDate means “without timezone” in java.time jargon, so this is guaranteed to keep you free of surprises from time zone anomalies.
If you need a time, you may consider LocalDateTime, but since this is without time zone too, it will give you the non-existing time of 1918.31.05 23:38:50.611, so maybe not.
An alternative thing you may consider is adding the right number of years to your origin of 1900.31.05 23:38:50.611. Then at least you will only have surprises in years where you hit a non-existing time. I am using ZonedDateTime for this demonstration:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu.dd.MM HH:mm:ss.SSS");
ZonedDateTime originalDateTime = ZonedDateTime.of(1900, Month.MAY.getValue(), 31,
23, 30, 50, 611000000, ZoneId.of("Europe/Moscow"));
for (int i = 0; i < 25; i++) {
System.out.println(originalDateTime.plusYears(i).format(formatter));
}
Output:
1900.31.05 23:30:50.611
1901.31.05 23:30:50.611
…
1917.31.05 23:30:50.611
1918.01.06 01:30:50.611
1919.01.06 00:30:50.611
1920.31.05 23:30:50.611
…
1924.31.05 23:30:50.611
Again in 1919 summer time began on May 31. This time the clock was only advanced by 1 hour, from 23 to 24, so you get only 1 hour later than the imaginary time of 23:30:50.611.
I am recommending java.time for date and time work, not least when doing math on dates like you do. The Calendar class is considered long outmoded. java.time was designed acknowledging that Calendar and the other old classes were poorly designed. The modern ones are so much nicer to work with.
How could I be sure it was Moscow?
In no other time zone than Europe/Moscow is the time of 1918.31.05 23:38:50.611 nonexistent. I checked:
LocalDateTime dateTime = LocalDateTime.of(1918, Month.MAY, 31, 23, 38, 50, 611000000);
for (String zid : ZoneId.getAvailableZoneIds()) {
ZonedDateTime zdt = dateTime.atZone(ZoneId.of(zid));
LocalDateTime newDateTime = zdt.toLocalDateTime();
if (! newDateTime.equals(dateTime)) {
System.out.println(zid + ": -> " + zdt + " -> " + newDateTime);
}
}
Output:
Europe/Moscow: -> 1918-06-01T01:38:50.611+04:31:19[Europe/Moscow] -> 1918-06-01T01:38:50.611
W-SU: -> 1918-06-01T01:38:50.611+04:31:19[W-SU] -> 1918-06-01T01:38:50.611
“W-SU” is a deprecated name for the same time zone, it stands for Western Soviet Union.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Time Changes in Moscow Over the Years
List of tz database time zones showing W-SU as deprecated.
An old message on an IANA mailing list stating “But long ago … we based the name on some more-political entity than a city name. For example, we used "W-SU" for the western Soviet Union…”
I am calling an API which takes two dates as input.The API checks if the difference between the two date is greater than 60 min, then it throws an exception.My input dates are startDate=11-06-2016T00:57:01 and endDate=11-06-2016T01:56:01.These two dates are saved in java.util.Date object.
Now the issue is though the two dates have a difference of 59 min which is less than 60 min, still the API throws exception.Looks like this isssue is due to DayLightSaving.On Nov 6,once 2 am is reached , DayLightSaving ends (PDT time zone ends), time is moved backward by 1 hr due to which time again become 1 am but in PST time zone now.This means on Nov 6 , there would be 1-2 am twice one in PDT and one in PST zone.
When this API is called on NOV 7, the time zone would be PST.So when the two dates are passed without the timezone specified, it takes the startDate in PDT zone and enddate in PST zone.Since PDT and PST itself have a difference of 1 hour, this would get added to the 59 min differnce and exception is being thrown.
How to handle this case when the input dates are in the transition period from PDT to PST?
sample code
SimpleDateFormat formatter1 = new SimpleDateFormat("MM-dd-yyyy hh:mm:ss");
String start="11-06-2016 00:57:01";
String end ="11-06-2016 01:56:01";
Date startdate = formatter1.parse(start);
Date enddate = formatter1.parse(end);
System.out.println("startDate is :" + startdate);
System.out.println("endDate is :" +enddate);
long dateRange = enddate.getTime() - startdate.getTime();
//if the difference between the two dates is > than 60 min i.e 3600000 ms, then throw exception.
System.out.println(dateRange);
if (dateRange > (60 * 60 * 1000)){
throw new Exception("Date time range cannot be greater than 60 minutes.(calculated using millisecond difference)");
}
Output
[Date Range is = 7140000
Exception in thread "main" java.lang.Exception: Date time range cannot be greater than 60 minutes.(calculated using millisecond difference).
at Datetest.main(Datetest.java:28)][1]
The above snippet throws exception when called in PST time zone.
Neither SimpleDateFormat nor the underlying Calendar specifies what happens when parsing a datetime string without timezone for a time in the overlapping hour between daylight savings time and standard time.
You have observed that it will return the later time, i.e. it seems to prefer standard over daylight savings time. But, the behavior is undefined, so...
The new java.time classes do however specify exactly what happens, and how to choose the other "hour" of the overlap.
In the new API, since your datetime string is without timezone, you'd likely first parse using LocalDateTime, then apply time zone to get a ZonedDateTime, e.g.
LocalDateTime ldtEnd = LocalDateTime.parse("2016-11-06T01:56:01");
ZonedDateTime zdtEnd = ldtEnd.atZone(ZoneId.of("America/Los_Angeles"));
// zdtEnd is now: 2016-11-06T01:56:01-07:00[America/Los_Angeles]
To see the overlap, you can try adding an hour:
ZonedDateTime zdtEnd2 = zdtEnd.plusHours(1);
// zdtEnd2 is now: 2016-11-06T01:56:01-08:00[America/Los_Angeles]
The behavior is well-defined, see javadoc of atZone():
In most cases, there is only one valid offset for a local date-time. In the case of an overlap, where clocks are set back, there are two valid offsets. This method uses the earlier offset typically corresponding to "summer".
In the case of a gap, where clocks jump forward, there is no valid offset. Instead, the local date-time is adjusted to be later by the length of the gap. For a typical one hour daylight savings change, the local date-time will be moved one hour later into the offset typically corresponding to "summer".
To obtain the later offset during an overlap, call ZonedDateTime.withLaterOffsetAtOverlap() on the result of this method. To throw an exception when there is a gap or overlap, use ZonedDateTime.ofStrict(LocalDateTime, ZoneOffset, ZoneId).
As you can see, it will always return the earlier time in an overlap, which is opposite of the observed behavior of SimpleDateFormat. If you want the later time in an overlap, call withLaterOffsetAtOverlap().
If you don't want to rely on documented default, you can always be explicit:
ZoneId PT = ZoneId.of("America/Los_Angeles");
LocalDateTime ldtStart = LocalDateTime.parse("2016-11-06T00:57:01");
ZonedDateTime zdtStartEarly = ldtStart.atZone(PT).withEarlierOffsetAtOverlap();
ZonedDateTime zdtStartLater = ldtStart.atZone(PT).withLaterOffsetAtOverlap();
System.out.println(zdtStartEarly); // 2016-11-06T00:57:01-07:00[America/Los_Angeles]
System.out.println(zdtStartLater); // 2016-11-06T00:57:01-07:00[America/Los_Angeles]
LocalDateTime ldtEnd = LocalDateTime.parse("2016-11-06T01:56:01");
ZonedDateTime zdtEndEarly = ldtEnd.atZone(PT).withEarlierOffsetAtOverlap();
ZonedDateTime zdtEndLater = ldtEnd.atZone(PT).withLaterOffsetAtOverlap();
System.out.println(zdtEndEarly); // 2016-11-06T01:56:01-07:00[America/Los_Angeles]
System.out.println(zdtEndLater); // 2016-11-06T01:56:01-08:00[America/Los_Angeles]
As you can see, for the 00:57 time, it makes no difference, because that time is not in the overlap hour.
What you can do here get the difference between the 2 dates using timezone offset. something like below
private int getDSTdifferenceDateAdjustment(Date startDate, Date endDate, TimeZone timeZone)
{
if (startDate == null || endDate == null) return 0;
int baseOffset = timeZone.getOffset(startDate.getTime());
int newOffSet = timeZone.getOffset(endDate.getTime());
return (newOffSet - baseOffset);
}
Have something like this in your method
int dstDifference = getDSTdifferenceDateAdjustment(startdate, enddate, TimeZone.getDefault());
// The dstDifference will get in the negative, so we are adding to the dateRange variable
dateRange += dstDifference;
Try this one and even check when the DST starts next year. Mostly this will work in all these cases
[purpose]
How to get int value after dividing 2 values(long-type).
[problem]
I changed the time(todaySeatedEndDateStr's HH:mm:ss part), but it is impossible to obtain an accurate value.
And I'm not sure that value is correct.
The main formula>
c'' = b / (a+b) * c
a, b : long type
c : int type
c'' : int type
Finally I want to get C''
Situation pic
# Test Code
import java.text.SimpleDateFormat;
import java.util.Date;
public class TimeCalculateTest {
public static void main(String[] args) throws Exception {
//2016-09-20 00:00:00 (Today's start point)
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String todayStartDateStr = "2016-09-20 00:00:00";
Date todayStartDate = sdf.parse(todayStartDateStr);
//2016-09-19 23:30:00 (Yesterday's particular point)
String yesterdaySeatedStartDateStr = "2016-09-19 23:30:00";
Date yesterdaySeatedStartDate = sdf.parse(yesterdaySeatedStartDateStr);
//2016-09-20 03:30:00 (Today's particular point)
String todaySeatedEndDateStr = "2016-09-20 21:30:00";
Date todaySeatedEndDate = sdf.parse(todaySeatedEndDateStr);
System.out.println("Today's Start Date String : " + todayStartDateStr);
System.out.println("Today's Start Date Long: " + todayStartDate.getTime());
System.out.println("Yesterday's Start Date String : " + yesterdaySeatedStartDateStr);
System.out.println("Yesterday Start Date Long : " + yesterdaySeatedStartDate.getTime());
System.out.println("Today's End Date String : " + todaySeatedEndDateStr);
System.out.println("Today's End Date Long : " + todaySeatedEndDate.getTime());
int c = 500; // <------ c
System.out.println("c: " + c);
if (yesterdaySeatedStartDate.compareTo(todayStartDate) < 0) {
long a = yesterdaySeatedStartDate.getTime(); // <----- a
long b = todaySeatedEndDate.getTime(); // <------ b
long abSum = a + b; // <------ a+b
System.out.println("Yesterday's long value : " + a);
System.out.println("Today's long value : " + b);
System.out.println("---> Sum : " + abSum);
long result = (long) ((float)b / (float)abSum * c);
System.out.println("---> Result : " + result);
System.out.println("------->to int : " + (int)result );
}
}
}
output >
Today's Start Date String : 2016-09-20 00:00:00
Today's Start Date Long: 1474297200000
Yesterday's Start Date String : 2016-09-19 23:30:00
Yesterday Start Date Long : 1474295400000
Today's End Date String : 2016-09-20 21:30:00
Today's End Date Long : 1474374600000
c: 500
Yesterday's long value : 1474295400000
Today's long value : 1474374600000
---> Sum : 2948670000000
---> Result : 250
------->to int : 250
I changed the 'todaySeatedEndDateStr' variable's HH:mm:ss,
but always get the 250.
How can I fix this problem?
plz help me..
a and b are both measured in milliseconds since January 1970, so they are roughly the same even though a is yesterday and b is today. So b / (a + b) is very close to a half, and half of 500 is 250.
If I interpret your graph correctly, you want to do this instead:
long midnight = todayStartDate.getTime();
long result = (long) ((float) (b - midnight) / (float) (b - a) * c);
Now I am taking the time since midnight in proportion to the time since a yesterday. Please try and see if it works for you.
Midnight of earlier period’s start to end of later period?
Looks like you have over-complicated the original problem. Seems your problem picture wants elapsed time from midnight of the date of the earlier period to a later moment. If not so, please edit your Question to state in plain conversational English what is the problem statement. And why do you use words "yesterday" and "today" if the date-time values are hard-coded?
java.time
You are using troublesome old legacy date-time classes now supplanted by the java.time classes.
We parse as LocalDateTime objects because your inputs lack info about offset-from-UTC or time zone. If you want to account for issues such as Daylight Saving Time (DST), use ZonedDateTime instead.
To parse, we replace the SPACE in the middle with a T to comply with ISO 8601 standard.
LocalDateTime earlierStart = LocalDate.parse( "2016-09-19 23:30:00".replace( " " , "T" ) );
LocalDateTime laterStop = LocalDate.parse( "2016-09-20 21:30:00".replace( " " , "T" ) );
To get the midnight ending of the starting point, we need to go through the LocalDate. We move to the start of the next day because getting the last moment of the day is problematic with an endlessly divisible fractional second.
LocalDate localDateOfStartNextDay = earlierStart.toLocalDate().plusDays( 1 );
LocalDateTime newDayAfterStart = localDateOfStartNextDay.atStartOfDay();
Now capture the elapsed time as a Duration with a resolution of nanoseconds.
Duration duration = Duration.between( newDayAfterStart , laterStop );
This code may not be exactly your solution, given that your Question is confusing. But I think you can see that working with the java.time classes will be less convoluted that trying to do math on count-from-epoch numbers.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
I have a Java method which compares two Dates and returns the number of days between them, but it's off by a day.
Even after I 0 out the hours, min, and sec the calculation is still off.
public long compareDates(Date exp, Date today){
TimeZone tzone = TimeZone.getTimeZone("America/New_York");
Calendar expDate = Calendar.getInstance();
Calendar todayDate = Calendar.getInstance();
expDate.setTime(exp);
todayDate.setTime(today);
expDate.set(Calendar.HOUR_OF_DAY, 0);
expDate.set(Calendar.MINUTE, 0);
expDate.set(Calendar.SECOND, 0);
todayDate.set(Calendar.HOUR_OF_DAY, 0);
todayDate.set(Calendar.MINUTE, 0);
todayDate.set(Calendar.SECOND, 0);
logger.info("Today = " + Long.toString(todayDate.getTimeInMillis()) + " Expiration = " + Long.toString(expDate.getTimeInMillis()));
expDate.setTimeZone(tzone);
todayDate.setTimeZone(tzone);
return (expDate.getTimeInMillis()-todayDate.getTimeInMillis())/86400000;
}
Output
Today = 1453939200030 Expiration = 1454544000000
There's 7 days between 1/28 and 2/4 but this returns 6.
Well, as you can see, you didn't clear the milliseconds, and 1454544000000 - 1453939200030 = 604799970 and dividing by 86400000 gets you 6.99999965277777..., which means 6 when truncated to int.
Now, if you clear the milliseconds too, today becomes 1453939200000, which will lead to you answer 7.
Note: This doesn't mean you're done, because of Daylight Savings Time. With DST, one of the timestamps may be ±1 hour from the other, so you may still get that truncation issue.
This was an answer to your particular issue. Try searching for how to correctly find days between dates in Java.
Today = 1453939200030
The times are given in milliseconds, and it looks like somehow your inputted Date has 30 extra milliseconds on it.
When I subtract the 30 milliseconds, then do the math on a calculator, I get 7 days. With your figures as is, I get 6.9999996527777777777777777777778, and in long math, the decimal figures get truncated to 6.
Zero out the milliseconds also.
expDate.set(Calendar.MILLISECOND, 0);
todayDate.set(Calendar.MILLISECOND, 0);
java.time
The Question and other Answers use outmoded classes. The old date-time classes such as java.util.Date/.Calendar bundled with the earliest versions of Java have proven to be quite troublesome. Those old classes have been supplanted by the java.time framework in Java 8 and later.
As the other Answers point out correctly, the issue is that the start long has 30 on the right side, precluding a whole-day calculation.
Count-Of-Days Definition
Furthermore you must define what you mean by a count-of-days. Do you mean a count by date, so any time on the 3rd of January to any time on the 4th is one day even if the times were a minute before and after midnight? Or do you mean a count of generic 24-hour blocks of time while ignoring the fact that particular days in particular time zones are not always 24-hours long because of Daylight Saving Time (DST) and other anomalies?
Count Days By Date
If you want the former, count by dates, then make use of the LocalDate class (a date-only without time-of-day nor time zone) and the Period class (a span of time defined as a count of years, months, days) found in java.time.
Define your inputs. Use long rather than int. These numbers apparently represent a count of milliseconds since the first moment of 1970 in UTC.
long startMilli = 1_453_939_200_030L;
long stopMilli = 1_454_544_000_000L;
Convert those long numbers into Instant objects, a moment on the timeline in UTC.
Instant startInstant = Instant.ofEpochMilli ( startMilli );
Instant stopInstant = Instant.ofEpochMilli ( stopMilli );
Define the time zone in which you want to consider the calendar dates. Note that time zone is crucial in defining dates. The date is not simultaneously the same around the globe. The date varies by time zone.
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
Apply that time zone to each Instant to produce ZonedDateTime.
ZonedDateTime startZdt = ZonedDateTime.ofInstant ( startInstant , zoneId );
ZonedDateTime stopZdt = ZonedDateTime.ofInstant ( stopInstant , zoneId );
To get a Period, we need “local” dates. By “local” we mean any particular locality, a generic date value. The LocalDate class contains no time zone, but the time zone contained with in the ZonedDateTime is applied when determining a LocalDate.
LocalDate startLocalDate = startZdt.toLocalDate ();;
LocalDate stopLocalDate = stopZdt.toLocalDate ();
Define our span of time as a count of generic days, in Period.
Period period = Period.between ( startLocalDate , stopLocalDate );
Interrogate the Period to ask for the number of generic days contained within.
int days = period.getDays ();
Dump to console.
System.out.println ( "milli: " + startMilli + "/" + stopMilli + " | Instant: " + startInstant + "/" + stopInstant + " | ZonedDateTime: " + startZdt + "/" + stopZdt + " | LocalDate: " + startLocalDate + "/" + stopLocalDate + " | period: " + period + " | days: " + days );
milli: 1453939200030/1454544000000 | Instant: 2016-01-28T00:00:00.030Z/2016-02-04T00:00:00Z | ZonedDateTime: 2016-01-27T19:00:00.030-05:00[America/Montreal]/2016-02-03T19:00-05:00[America/Montreal] | LocalDate: 2016-01-27/2016-02-03 | period: P7D | days: 7
Count Of Whole Days
If you want a count of whole days, use the Days class from ThreeTen-Extra. Notice in the output below that we get a count of six (6) days rather than seven (7) as seen above.
ThreeTen-Extra
The ThreeTen-Extra project extends java.time. Run by the same folks who built java.time.
The behavior of the between method is not documented clearly. Experimenting shows that it seems to based on 24-hour chunks of time, not dates. Replace the 030 with 000, and also try replacing in the stopMilli the last 000 with 030, to see the behavior for yourself.
Days daysObject = Days.between ( startZdt , stopZdt );
int daysObjectCount = daysObject.getAmount ();
Dump to console. The P6D string you see in the output was generated according to the formats defined in the ISO 8601 standard. This standard is used by default in java.time for all parsing and generating of textual representations of date-time values. These standard formats are quite sensible and useful so do glance at that linked Wikipedia page.
System.out.println ( "daysObject: " + daysObject + " | daysObjectCount: " + daysObjectCount );
daysObject: P6D | daysObjectCount: 6
To fix my problems, I have zeroed out the milliseconds as mentioned, as well as casted the longs to doubles in order to maintain accuracy and round when necessary.
expDate.setTime(exp);
todayDate.setTime(today);
expDate.setTimeZone(tzone);
todayDate.setTimeZone(tzone);
expDate.set(Calendar.HOUR_OF_DAY, 0);
expDate.set(Calendar.MINUTE, 0);
expDate.set(Calendar.SECOND, 0);
expDate.set(Calendar.MILLISECOND, 0);
todayDate.set(Calendar.HOUR_OF_DAY, 0);
todayDate.set(Calendar.MINUTE, 0);
todayDate.set(Calendar.SECOND, 0);
todayDate.set(Calendar.MILLISECOND, 0);
double diff = ((double)expDate.getTimeInMillis()-(double)todayDate.getTimeInMillis())/86400000;
return Math.round(diff);
How can I get the year, month, day, hours, minutes, seconds and milliseconds of the current moment in Java? I would like to have them as Strings.
You can use the getters of java.time.LocalDateTime for that.
LocalDateTime now = LocalDateTime.now();
int year = now.getYear();
int month = now.getMonthValue();
int day = now.getDayOfMonth();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int millis = now.get(ChronoField.MILLI_OF_SECOND); // Note: no direct getter available.
System.out.printf("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, millis);
Or, when you're not on Java 8 yet, make use of java.util.Calendar.
Calendar now = Calendar.getInstance();
int year = now.get(Calendar.YEAR);
int month = now.get(Calendar.MONTH) + 1; // Note: zero based!
int day = now.get(Calendar.DAY_OF_MONTH);
int hour = now.get(Calendar.HOUR_OF_DAY);
int minute = now.get(Calendar.MINUTE);
int second = now.get(Calendar.SECOND);
int millis = now.get(Calendar.MILLISECOND);
System.out.printf("%d-%02d-%02d %02d:%02d:%02d.%03d", year, month, day, hour, minute, second, millis);
Either way, this prints as of now:
2010-04-16 15:15:17.816
To convert an int to String, make use of String#valueOf().
If your intent is after all to arrange and display them in a human friendly string format, then better use either Java8's java.time.format.DateTimeFormatter (tutorial here),
LocalDateTime now = LocalDateTime.now();
String format1 = now.format(DateTimeFormatter.ISO_DATE_TIME);
String format2 = now.atZone(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME);
String format3 = now.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss", Locale.ENGLISH));
System.out.println(format1);
System.out.println(format2);
System.out.println(format3);
or when you're not on Java 8 yet, use java.text.SimpleDateFormat:
Date now = new Date(); // java.util.Date, NOT java.sql.Date or java.sql.Timestamp!
String format1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH).format(now);
String format2 = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH).format(now);
String format3 = new SimpleDateFormat("yyyyMMddHHmmss", Locale.ENGLISH).format(now);
System.out.println(format1);
System.out.println(format2);
System.out.println(format3);
Either way, this yields:
2010-04-16T15:15:17.816
Fri, 16 Apr 2010 15:15:17 GMT
20100416151517
See also:
Java string to date conversion
Switch to joda-time and you can do this in three lines
DateTime jodaTime = new DateTime();
DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss.SSS");
System.out.println("jodaTime = " + formatter.print(jodaTime));
You also have direct access to the individual fields of the date without using a Calendar.
System.out.println("year = " + jodaTime.getYear());
System.out.println("month = " + jodaTime.getMonthOfYear());
System.out.println("day = " + jodaTime.getDayOfMonth());
System.out.println("hour = " + jodaTime.getHourOfDay());
System.out.println("minute = " + jodaTime.getMinuteOfHour());
System.out.println("second = " + jodaTime.getSecondOfMinute());
System.out.println("millis = " + jodaTime.getMillisOfSecond());
Output is as follows:
jodaTime = 2010-04-16 18:09:26.060
year = 2010
month = 4
day = 16
hour = 18
minute = 9
second = 26
millis = 60
According to http://www.joda.org/joda-time/
Joda-Time is the de facto standard date and time library for Java.
From Java SE 8 onwards, users are asked to migrate to java.time
(JSR-310).
// Java 8
System.out.println(LocalDateTime.now().getYear()); // 2015
System.out.println(LocalDateTime.now().getMonth()); // SEPTEMBER
System.out.println(LocalDateTime.now().getDayOfMonth()); // 29
System.out.println(LocalDateTime.now().getHour()); // 7
System.out.println(LocalDateTime.now().getMinute()); // 36
System.out.println(LocalDateTime.now().getSecond()); // 51
System.out.println(LocalDateTime.now().get(ChronoField.MILLI_OF_SECOND)); // 100
// Calendar
System.out.println(Calendar.getInstance().get(Calendar.YEAR)); // 2015
System.out.println(Calendar.getInstance().get(Calendar.MONTH ) + 1); // 9
System.out.println(Calendar.getInstance().get(Calendar.DAY_OF_MONTH)); // 29
System.out.println(Calendar.getInstance().get(Calendar.HOUR_OF_DAY)); // 7
System.out.println(Calendar.getInstance().get(Calendar.MINUTE)); // 35
System.out.println(Calendar.getInstance().get(Calendar.SECOND)); // 32
System.out.println(Calendar.getInstance().get(Calendar.MILLISECOND)); // 481
// Joda Time
System.out.println(new DateTime().getYear()); // 2015
System.out.println(new DateTime().getMonthOfYear()); // 9
System.out.println(new DateTime().getDayOfMonth()); // 29
System.out.println(new DateTime().getHourOfDay()); // 7
System.out.println(new DateTime().getMinuteOfHour()); // 19
System.out.println(new DateTime().getSecondOfMinute()); // 16
System.out.println(new DateTime().getMillisOfSecond()); // 174
// Formatted
// 2015-09-28 17:50:25.756
System.out.println(new Timestamp(System.currentTimeMillis()));
// 2015-09-28T17:50:25.772
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH).format(new Date()));
// Java 8
// 2015-09-28T17:50:25.810
System.out.println(LocalDateTime.now());
// joda time
// 2015-09-28 17:50:25.839
System.out.println(DateTimeFormat.forPattern("YYYY-MM-dd HH:mm:ss.SSS").print(new org.joda.time.DateTime()));
tl;dr
ZonedDateTime.now( // Capture current moment as seen in the wall-clock time used by the people of a particular region (a time zone).
ZoneId.of( "America/Montreal" ) // Specify desired/expected time zone. Or pass `ZoneId.systemDefault` for the JVM’s current default time zone.
) // Returns a `ZonedDateTime` object.
.getMinute() // Extract the minute of the hour of the time-of-day from the `ZonedDateTime` object.
42
ZonedDateTime
To capture the current moment as seen in the wall-clock time used by the people of a particular region (a time zone), use ZonedDateTime.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment during runtime(!), so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Call any of the many getters to pull out pieces of the date-time.
int year = zdt.getYear() ;
int monthNumber = zdt.getMonthValue() ;
String monthName = zdt.getMonth().getDisplayName( TextStyle.FULL , Locale.JAPAN ) ; // Locale determines human language and cultural norms used in localizing. Note that `Locale` has *nothing* to do with time zone.
int dayOfMonth = zdt.getDayOfMonth() ;
String dayOfWeek = zdt.getDayOfWeek().getDisplayName( TextStyle.FULL , Locale.CANADA_FRENCH ) ;
int hour = zdt.getHour() ; // Extract the hour from the time-of-day.
int minute = zdt.getMinute() ;
int second = zdt.getSecond() ;
int nano = zdt.getNano() ;
The java.time classes resolve to nanoseconds. Your Question asked for the fraction of a second in milliseconds. Obviously, you can divide by a million to truncate nanoseconds to milliseconds, at the cost of possible data loss. Or use the TimeUnit enum for such conversion.
long millis = TimeUnit.NANOSECONDS.toMillis( zdt.getNano() ) ;
DateTimeFormatter
To produce a String to combine pieces of text, use DateTimeFormatter class. Search Stack Overflow for more info on this.
Instant
Usually best to track moments in UTC. To adjust from a zoned date-time to UTC, extract a Instant.
Instant instant = zdt.toInstant() ;
And go back again.
ZonedDateTime zdt = instant.atZone( ZoneId.of( "Africa/Tunis" ) ) ;
LocalDateTime
A couple of other Answers use the LocalDateTime class. That class in not appropriate to the purpose of tracking actual moments, specific moments on the timeline, as it intentionally lacks any concept of time zone or offset-from-UTC.
So what is LocalDateTime good for? Use LocalDateTime when you intend to apply a date & time to any locality or all localities, rather than one specific locality.
For example, Christmas this year starts at the LocalDateTime.parse( "2018-12-25T00:00:00" ). That value has no meaning until you apply a time zone (a ZoneId) to get a ZonedDateTime. Christmas happens first in Kiribati, then later in New Zealand and far east Asia. Hours later Christmas starts in India. More hour later in Africa & Europe. And still not Xmas in the Americas until several hours later. Christmas starting in any one place should be represented with ZonedDateTime. Christmas everywhere is represented with a LocalDateTime.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
With Java 8 and later, use the java.time package.
ZonedDateTime.now().getYear();
ZonedDateTime.now().getMonthValue();
ZonedDateTime.now().getDayOfMonth();
ZonedDateTime.now().getHour();
ZonedDateTime.now().getMinute();
ZonedDateTime.now().getSecond();
ZonedDateTime.now() is a static method returning the current date-time from the system clock in the default time-zone. All the get methods return an int value.
Or use java.sql.Timestamp. Calendar is kinda heavy,I would recommend against using it
in production code. Joda is better.
import java.sql.Timestamp;
public class DateTest {
/**
* #param args
*/
public static void main(String[] args) {
System.out.println(new Timestamp(System.currentTimeMillis()));
}
}
in java 7 Calendar one line
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").format(Calendar.getInstance().getTime())
Use the formatting pattern 'dd-MM-yyyy HH:mm:ss aa' to get date as 21-10-2020 20:53:42 pm
Look at the API documentation for the java.util.Calendar class and its derivatives (you may be specifically interested in the GregorianCalendar class).
Calendar now = new Calendar() // or new GregorianCalendar(), or whatever flavor you need
now.MONTH
now.HOUR
etc.