I'm trying to do a simple subtraction of dates and getting odd results. For some reason when I format it with SimpleDateFormat, there are 7 extra hours difference.
package timedemo;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Timedemo {
public static void main(String[] args) {
Date start = new Date(); // time right now
Date stop = new Date();
long startTime = start.getTime()-1000; // introduce a second of skew
long stopTime = stop.getTime();
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
// First one shows up prior to 1970 epoch. Okay, except for 5 hour variance.
// Probably a timezone thing, as I'm in EST (-5).
System.out.println("New date is "+new Date(stopTime - startTime));
System.out.println("Raw Start is "+startTime); // fine
System.out.println("Raw Stop is "+stopTime); // fine
System.out.println("Raw Difference is "+(stopTime-startTime));
System.out.println("Formatted Start is "+sdf.format(startTime));
System.out.println("Formatted Stop is "+sdf.format(stopTime));
System.out.println("Formatted Difference is "+sdf.format(stopTime-startTime));
}
}
And the results are:
New date is Wed Dec 31 19:00:01 EST 1969
Raw Start is 1418397344360
Raw Stop is 1418397345360
Raw Difference is 1000
Formatted Start is 10:15:44
Formatted Stop is 10:15:45
Formatted Difference is 07:00:01
I had thought it was a timezone thing, but I'm in EST (-5), not MST (-7).
I would suspect Daylight Savings, but it's 7 hours, not 1.
12/24 hour difference? 12-7=5 which is my timezone offset... not sure what to make of it though.
Kind of out of ideas at this point.
Why the seven hour shift on the last line?
The "12-7 = 5" is definitely related to the problem... or more accurately, it's "12-5=7", i.e. 5 hours before midnight is 7pm. You'll see that if you format it as a full date/time:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Heck, you can see that in your first line: "Wed Dec 31 19:00:01 EST 1969" - that 19:00 is 7pm, which you're formatting using hh as 07.
Fundamentally, the problem is that you're trying to treat a difference in time as if it's a point in time. I would very strongly urge you not to do that. If you absolutely want to do that (and the difference will always be non-negative but less than 24 hours), then you should set the time zone on SimpleDateFormat to UTC, and use HH instead of hh. But it would be better to use Joda Time or java.time from Java 8 which can represent a Duration, i.e. the difference between two points in time. Date is simply not an appropriate data type for that.
Related
I have some strings in Java that come in the format: Day Month Year Hour:Minute:Second
7 Jan 2010 23:00:00.000
4 Feb 2010 17:40:00.000
What is the easiest way to parse this string and convert the values to their resulting Julian Dates? I am reading in these strings from Excel so they are not objects with any sort of conversion/formatting utilities (just raw strings). Is there an easy library or function to call to convert these, or would I have to manually write a parser? Thanks
java.time
Sure, Java has got a parser for date and time built-in, the DateTimeFormatter class (named so because it can also format date and time back to strings). And a number of classes that can utilize it for producing objects of themselves. In your case you need the LocalDateTime class. A LocalDateTime is a date and time of day without time zone or offset from UTC, so appropriate for holding the data from your string.
This formatter s good for your string:
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("d MMM uuuu HH:mm:ss.SSS", Locale.ENGLISH);
Edit: You wrote in a comment:
Plugging in Jan 7 2010 hour 23 into this calculator:
aavso.org/jd-calculator gives
back 2455204.45833. Would this be the exact Julian Date? I believe
your solution was giving the Day instead of Date decimal value
Yes, that’s exactly true. The modified code to get the julian date including the fraction is:
String source = "7 Jan 2010 23:00:00.000";
LocalDateTime ldt = LocalDateTime.parse(source, FORMATTER);
// Subtract half a day to compensate for the
// fact that the Julian day begins at noon
LocalDateTime dateToUseForJulianDay = ldt.minusHours(12);
long julianDayNumber = dateToUseForJulianDay.getLong(JulianFields.JULIAN_DAY);
double juianDateFraction = (double) dateToUseForJulianDay.getLong(ChronoField.NANO_OF_DAY)
/ (double) Duration.ofDays(1).toNanos();
double julianDate = julianDayNumber + juianDateFraction;
System.out.println("Julian date: " + julianDate);
And the output is in this example:
Julian date: 2455204.4583333335
It agrees very nicely with thee result you quote from the online calculator.
The Julian day number is the day number since January 1, 4713 BC. The Julian day starts at noon, which Java does not take into account, so as a hack I have subtracted 12 hours to compensate and get the correct day for all times of day. Since the getLong() method only gets the Julian day number as a whole number, I need to find the fraction separately. It’s a matter of dividing the nanosecond of the day by the total number of nanoseconds in a day. From the original date and time we would have needed the number of nanos since 12 noon; but since I have already subtracted 12 hours, the nanosecond of the day, since 0:00 midnight, is the number we need.
Further link: Julian day on Wikipedia
My library Time4J supports Julian Dates out of the box.
ChronoFormatter<PlainTimestamp> f =
ChronoFormatter.ofTimestampPattern(
"d MMM uuuu HH:mm:ss.SSS", PatternType.CLDR, Locale.ENGLISH);
Moment j2000 = f.parse("7 Jan 2010 23:00:00.000").atUTC(); // are your timestamps really UTC?
// eventually also: ".in(Timezone.ofSystem());"
System.out.println(JulianDay.ofSimplifiedTime(j2000)); // programmer's standard
// JD(POSIX)2455204.4583333335
System.out.println(JulianDay.ofEphemerisTime(j2000)); // astronomical definition
// JD(TT)2455204.459099352
Advantages:
No complex calculation of your owns.
Support for the astronomical definition on the time scale TT.
Explicit display of time zone dependencies (whatever you choose).
I'm trying to convert 5007 to HH:mm:ss:SSS, I'm using Netbeans IDE v8.2:
Date date = new Date(5007);
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
String dateFormatted = formatter.format(date);
But this gives me an incorrect result:
01:00:05:007
^^-------------------------why does it print to me 01 in the hours?
But it should get:
00:00:05:007
^^-------------------------correct result
When I use ideone for example it gives me a correct result:
Any explication about this problem?
I testes with both versions: Java 7 and Java 8.
When you ask for the explanation of your problem I will be honest with you: You are misusing SimpleDateFormat for formatting a duration. It was never meant for this use and is not designed to give you the expected result if you try it anyway. The problem you got is not the only problem you will have. If your duration grows to more than 24 hours, the whole days will not be formatted, so your result will be wrong in a different way.
Apart from that, these days I recommend not using the SimpleDateFormat class at all. Its replacement is java.time.format.DateTimeFormatter, it came out in 2014 and comes with quite fewer negative surprises. It is not a good choice for formatting a duration either, though.
So what is a good choice? I will give you the Java 9 answer first because it is easy:
Duration dur = Duration.ofMillis(5007);
String hmss = String.format("%02d:%02d:%02d:%03d",
dur.toHours(),
dur.toMinutesPart(),
dur.toSecondsPart(),
dur.toMillisPart());
The main advantage isn’t even the ease, it’s the clarity. For example, with this code no one needs to ask the question I asked in a comment, is it a point it time or a duration? The code very clearly states that it is a duration.
For Java 6, 7 and 8, you may still use the Duration class (in Java 6 and 7 through the ThreeTen Backport), but it doesn’t lend itself that easily to formatting, so I think I would resort to using the TimeUnit enum as in the question I had linked to (the link you asked me to remove again):
long millis = 5007;
long hours = TimeUnit.MILLISECONDS.toHours(millis);
millis -= TimeUnit.HOURS.toMillis(hours);
long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
millis -= TimeUnit.MINUTES.toMillis(minutes);
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
millis -= TimeUnit.SECONDS.toMillis(seconds);
String hmss = String.format("%02d:%02d:%02d:%03d", hours, minutes, seconds, millis);
The result is the desired
00:00:05:007
To illustrate the point I was trying to make about what happens when the duration is more than 24 hours, let’s try to give it 100 000 000 milliseconds (there are 86 400 000 millis in a day):
27:46:40:000
Date date = new Date(5007);
creates a Date which is 5007 milliseconds after January 1, 1970, 00:00:00 GMT.
When you format it with SimpleDateFormat, it is converted to your machine's timezone (which I'm assuming is GMT+1).
When I run your code I get
02:00:05:007
And my timezone is GMT+2.
If you set the timezone of the DateFormat instance to GMT, you'll get the expected output:
Date date = new Date(5007);
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS");
formatter.setTimeZone (TimeZone.getTimeZone ("GMT"));
String dateFormatted = formatter.format(date);
System.out.println (dateFormatted);
This prints
00:00:05:007
It is definitely a Locale issue. When you do not specify one it will use the (your system) default one. To override this;
try either adding this
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
or passing a Locale when constructing the SimpleDateFormat instance. For example:
DateFormat formatter = new SimpleDateFormat("HH:mm:ss:SSS", Locale.ENGLISH);
I assume Locale.ENGLISH has UTC timezone btw, but I hope you get the idea.
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.
String qtm = "00:02:00";
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
java.util.Date d = null;
try
{
d = formatter.parse(qtm);}
catch (java.text.ParseException e) {
System.err.println("Error: " + e.getMessage());
}
System.out.println(d);
this code gives me:-
Thu Jan 01 00:02:00 IST 1970
in my program I want to keep a time quantum of 2 minutes for roundrobin algo, how can I do that?? please help me.
when I give
long curr= d.getTime();
system.out.println(d);
it gives the output:-
-19500000
please tell me how to give just 2 minutes as an interval and to assign it to a variable....
The problem at the moment is that your SimpleDateFormat is in your local time zone, whereas Date.getTime() gives you the milliseconds since the Unix epoch in UTC.
You can fix this as:
SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
formatter.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
... and then use getTime() as before.
Ideally, it would be better to use Joda Time or the Java 8 java.time package, both of which have a Duration type to represent this sort of value. Joda Time has a PeriodFormatter which could be used to parse this as a Period and convert that into a Duration, which is a bit long-winded, admittedly. I don't see a way of parsing straight to a Duration for either of them...
If you just want two minutes then why not go for the milliseconds equivalent (120000L) ?
If you are planning to use it within a scheduled thread pool executor, you could use (2, TimeUnit.Seconds).
IMHO, the solution really depends on what purpose you want to use it for.
I'm trying to do simple date/time calculations and no matter what I do I'm getting confused with what appears to be time zones etc.
I need to add (and subtract)different date/times together so I figured the easiest way would be to convert them to seconds, perform the calculations and then format back to a string. But no matter what I do, I'm one hour out - which sounds like timezone/DST etc.
What's wrong with this:
long dateInMillis = 3600000L;
DateFormat formatter = new SimpleDateFormat("HH:mm");
Date dt = new Date();
dt.setTime(dateInMillis);
System.out.println( formatter.format(dt.getTime()));
The answer I get is 2:00. Even if I use:
long dateInMillis = 1;
I still get 1:00
Help please ;-)
TIA
Martin
I agree that using Joda is likely to make life easier. However what you are seeing is the effect of a TimeZone sensitive formatter.
You could try adding:
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Save yourself a lot of headache, use an existing library for this - I like Joda Time myself.
For example the DateTime class supports various calculations (to add or subtract specific periods etc.) If you want an example, post your exact requirement, and I'll add it to the answer.
You should not try and format a long as a date. Just use:
long dateInMillis = 3600000L;
DateFormat formatter = new SimpleDateFormat("HH:mm");
Date dt = new Date();
dt.setTime(dateInMillis);
System.out.println( formatter.format(dt));
However, I would use a 3rd party lib for handling time-calculations like JodaTime as #Nim suggested, if your calculations are in anyway complicated.
I also suggest to use a framework as suggested in the comments.
However, Date is based on the milliseconds after Jan. 1st 1970. So 3600000L is just one hour from there, so 1:00 is correct.
According to people here, you could use Joda Time to resolve this calculations. If you want to stay with pure Java code, you can look for Calendar class to do your operations with dates. Here is a sample:
Calendar c = Calendar.getInstance();
DateFormat df = new SimpleDateFormat("HH:mm");
Date d = c.getTime();
System.out.println(df.format(d)); //actual hour:minute
c.add(Calendar.HOUR, -4);
d = c.getTime();
System.out.println(df.format(d)); //actual hour:minute minus 4 hours
Still, I won't rely too much in this kind of code. You'll have less problems using Joda Time.
When you're calling new Date(), current date in current timezone is created. When you're calling 'dt.setTime(dateInMillis);' you set offset in milliseconds from 1 January 1970 in GMT timezone. Let me explain, here in Msk, first call will give me Thu Mar 01 17:02:22 MSK 2012, the second call will give me 1 hour offset, thus in Msk it will be original offset (+3) plus 1, so it'll be Thu Jan 01 04:00:00 MSK 1970. Then when you're calling formatter.format(? extends Number) it calls new Date(((Number)obj).longValue()) inside, so it creates back you date which is Thu Jan 01 04:00:00 MSK 1970 and prints hours and minutes. So, you're right, it prints out time in your current timezone.
For posterity, here's example code using the Joda-Time 2.3 library as mentioned by the other answers and comments.
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;
long dateTimeInMillis = 3600000L; // 1,000 * 60 * 60 = 3600000L = 1 hour.
// Time Zone list… http://joda-time.sourceforge.net/timezones.html (not quite up-to-date, read page for details)
DateTime dateTimeInUtc = new DateTime( dateTimeInMillis, DateTimeZone.UTC );
// Paris happens to be one hour ahead of UTC/GMT.
DateTime dateTimeInParis = dateTimeInUtc.toDateTime( DateTimeZone.forID( "Europe/Paris" ) );
Dump to console…
System.out.println( "dateTimeInUtc: " + dateTimeInUtc );
System.out.println( "dateTimeInParis: " + dateTimeInParis );
When run…
dateTimeInUtc: 1970-01-01T01:00:00.000Z
dateTimeInParis: 1970-01-01T02:00:00.000+01:00