Consider the following program
Date date1 = new Date(-124304227239000L);
Date date2 = new Date(0);
System.out.println(date1 + " vs. " + date2);
The result (at least with Java 8 on my computer, and with Java 11 on a different computer):
Sun Jan 01 16:59:21 CET 1970 vs. Thu Jan 01 01:00:00 CET 1970
This seems strange because following the documentation (https://docs.oracle.com/javase/8/docs/api/java/util/Date.html#Date-long-) , negative values as parameter for Date indicate dates before 1970. Instead, I get a Sunday instead of Thursday, but still 1970.
Can anybody explain this to me?
The value you've provided is around 1969/1970 BC, depending on whether you do a Gregorian/Julian cutover or not. Date.toString(), aside from all its other problems, doesn't bother to mention the era.
If you use Instant with the same value, it's clearer:
Instant instant = Instant.ofEpochMilli(-124304227239000L);
System.out.println(instant);
Output:
-1970-12-15T15:59:21Z
I'd draw the following conclusions from this:
When using values in the far past, there are lots of considerations to bear in mind, including textual representation and calendar system
Avoid java.util.Date as far as you can
Related
I'm trying to convert java Date to java LocalTime and my code looks like this
Date memberBirthdayDate = club.getMembers().get(i).getDob();
System.out.println(memberBirthdayDate);
LocalDate memberBirthday = memberBirthdayDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
When I print out the date before and after converstion it looks like this:
Before: Wed May 21 00:00:00 GMT 94
After: 0094-05-18
It looks like it's converting backwards but I can't work out how to do it!
you can do that
LocalDateTime memberBirthday = LocalDateTime.ofInstant(memberBirthdayDate.toInstant(),
ZoneId.systemDefault());
Date out = Date.from(memberBirthday.atZone(ZoneId.systemDefault()).toInstant()); System.out.println(out);
There seems to be a bug in your years. You gave the answer yourself in the comment:
I'd imported from a csv through Excel and it had automatically
formatted the date to remove the '19'
The conversion you made works nicely for a date in 1994:
System.out.println("Before: " + memberBirthdayDate);
LocalDate memberBirthday = memberBirthdayDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
System.out.println("After: " + memberBirthday);
Output:
Before: Sat May 21 00:00:00 IST 1994
After: 1994-05-21
I used Europe/Dublin time to reproduce your exact result, so IST is for Irish Summer Time. Your Date seems to denote the start of day at some GMT offset, so you need to use a time zone that agrees with this offset. I expect the conversion to work as expected at least for all dates after year 1900, and likely earlier too.
However, when the year gets truncated from 1994 to 94, funny things start to happen. Dates that far back are not so well defined. LocalDate uses the proleptic Gregorian calendar, which is practical and well-defined, but doesn’t agree with dates used in real life before the introduction of the Gregorian calendar from 1582 and on. I’m not sure what Date uses. For dates back in year 94 AD we shouldn’t be surprised about May 21 coming through as May 18.
Before: Wed May 21 00:00:00 GMT 94
After: 0094-05-18
Link: Wikipedia article Gregorian calendar
Our server is running in netherlands but users uses the application in UK .
For UK 2017-03-26 02:30:00 is valid date-time but not in Netherlands.
I am using the code to convert the time by setting the timezone . But its not giving me right output.
String toDate ="2017-03-26 02:30:00";//Valid Time in UK
Date date = new Date();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// Use London's time zone to format the date in
df.setTimeZone(TimeZone.getTimeZone("London"));
System.out.println("Date and time in London: " + df.parse(toDate));
Output from Program : Date and time in London: Sun Mar 26 04:30:00 CEST 2017
Output Required : Date and time in London: Sun Mar 26 02:30:00 CEST 2017
Java version used : 1.7 . Can not use joda time for some dependency.
First a detail, TimeZone.getTimeZone() is dangerous, if it doesn’t recognize the ID string, it will tacitly give you GMT. Exactly in London this is so close to the correct you may get fooled for a while. TimeZone.getTimeZone("London") gives you UTC (or GMT). As Stefan Freitag pointed out, it has to be TimeZone.getTimeZone("Europe/London") for the correct British time zone.
Next a very common misunderstanding: A Date object does not have a time zone in it. It’s a point in time only. So how come it is printed as Sun Mar 26 04:30:00 CEST 2017, where CEST obviously refers to a time zone (Central European Summer Time, used in the Netherlands)? When printing the date, you are implicitly invoking Date.toString(). This methods unconditionally prints the time according the default time zone for the JVM. Therefore, setting the time zone for the DateFormat you used for parsing has no effect here. And therefore changing the JVM’s time zone setting, as Hugo did in a comment, works. The other way of getting correct output for British users is to format the Date back into a string using a DateFormat with British time zone.
If you use Hugo’s trick in the beginning, before creating the DateFormat, it too will have the British time zone, and you need not call df.setTimeZone() (but you may if you think it makes the code clearer, of course).
Look forward to using Java 8 or later some day. The new date and time classes in java.time generally don’t come with the surprises experienced with the old ones.
As part of some logic, it is necessary in my program to turn a long Java timestamp (including year, month, etc.) to a 'short' Java time. This should correspond to exactly the same hours, minutes and seconds of the original time, but within 1 day of Jan 1 1970 (i.e. a value between 0 (00:00:00) and 86400000 (23:59:59)). An example is the conversion in the question.
In order the perform this, I thought the below code would work:
public int convertToTime(long fullTimeStamp) {
Calendar c = Calendar.getInstance();
c.setTimeInMillis(date);
c.set(Calendar.DATE, 1);
c.set(Calendar.MONTH, 0);
c.set(Calendar.YEAR, 1970);
return (int) c.getTimeInMillis();
}
The issue I am having is to do with timezones. In the UK we are currently in BST. After setting all the values with the function, the time remains the same numbers (e.g. 8.00am) but changes the timezone to GMT! 8.00am GMT is of course not the same as 8.00am BST, and is instead equal to 9.00am BST.
Adding some console output to the function demonstrates this issue:
public int convertToTime(long fullTimeStamp) {
System.out.println(new Date(fullTimeStamp)); // correct
Calendar c = Calendar.getInstance();
c.setTimeInMillis(fullTimeStamp);
System.out.println(c.getTime()); // correct
c.set(Calendar.DATE, 1);
c.set(Calendar.MONTH, 0);
c.set(Calendar.YEAR, 1970);
System.out.println(c.getTime()); // incorrect!
return (int) c.getTimeInMillis();
}
Program output:
Wed Jun 19 12:15:00 BST 2013 // ok
Wed Jun 19 12:15:00 BST 2013 // this makes sense
Thu Jan 01 12:15:00 GMT 1970 // Calendar, stahp!
The desired behaviour is for the last part to read:
Thu Jan 01 11:15:00 GMT 1970
or
Thu Jan 01 12:15:00 BST 1970
Is this expected behaviour of the calendar? My understanding was that it keeps all the 'digits' the same that aren't modified, so if the value of HOUR_OF_DAY is 8, it should stay at 8, even if the timezone is modified.
I have tried setting the timezone on the calendar (before any values are set) to BST and GMT and exactly the same behaviour occurs. I also cannot manually add or remove milliseconds to delete all years after 1970 as I will have to handle leap years.
Aside from 'use Joda time (or some other time package)' does anyone have any other suggestions to perform this operation? I kind of need to get a quick fix in before experimenting with other packages if possible.
Thanks!
I think you're running foul of a little-known fact about the UK time zone: at the Unix epoch, we were actually in UTC+1. Java is getting the time of day right (within the UK time zone), but the name wrong - it shouldn't be specifying GMT, but BST. This isn't BST as in British Summer Time; it's BST as in British Standard Time. Yes, it's that mad.
From the relevant wikipedia article:
An inquiry during the winter of 1959–60, in which 180 national organisations were consulted, revealed a slight preference for a change to all-year GMT+1, but the length of summer time was extended as a trial rather than the domestic use of Greenwich Mean Time abolished.[8] A further inquiry during 1966–67 led the government of Harold Wilson to introduce the British Standard Time experiment, with Britain remaining on GMT+1 throughout the year. This took place between 27 October 1968 and 31 October 1971, when there was a reversion to the previous arrangement.
It's worth bearing in mind that your original problem statement is somewhat ambiguous: you're taking in a long, which is just the millis since the Unix epoch - but then you're trying to interpret it in terms of the hour of day, which immediately begs the question of which time zone you need to interpret it in. Have you made that decision? If so, you should document it very carefully, and make sure your code complies with it.
Ultimately, my recommendations are:
If you can possibly use Joda Time, do so. It will save you hours and hours of heartache.
If you're trying to calendar calculations like this, consider changing the time zone of the calendar to UTC before doing anything else; it will save you some heartache
Avoid using Date.toString() where possible - you could use a DateFormatter with the time zone set to UTC, and then you would see the expected results
As user2340612's answer states, to get just the "millisecond of UTC day" you can use simple arithmetic - but not quite with the values given. I would use:
long timeOfDay = millisecondsSinceUnixEpoch % TimeUnit.DAYS.toMillis(1);
... but this only works if you're interested in the UTC time of day for the given instant. (It will also give a negative result for negative input, but you may not care about that.)
If you need a timestamp between 0 and 86399999 (which is 23:59:59.999) you can get the current timestamp and calculate the remainder of the division between it and 86400000:
desired_time = cur_time % 86400000
But you'll miss the summer time, if present.
I have a variable containing the days since the epoch reference date of 1970-01-01 for a certain date.
Does someone know the way to convert this variable to a java.util.Calendar?
Use the java.time classes in Java 8 and later. In one line:
LocalDate date = LocalDate.ofEpochDay(1000);
Calling ofEpochDay(long epochDay) obtains an instance of LocalDate from the epoch day count.
The following should work:
Calendar c = new GregorianCalendar();
c.setTime(new Date(0));
c.add(Calendar.DAY_OF_YEAR, 1000);
System.err.println(c.getTime());
A note regarding time zones:
A new GregorianCalendar instance is created using the default time zone of the system the program is running on. Since Epoch is relative to UTC (GMT in Java) any time zone different from UTC must be handled with care. The following program illustrates the problem:
TimeZone.setDefault(TimeZone.getTimeZone("GMT-1"));
Calendar c = new GregorianCalendar();
c.setTimeInMillis(0);
System.err.println(c.getTime());
System.err.println(c.get(Calendar.DAY_OF_YEAR));
c.add(Calendar.DAY_OF_YEAR, 1);
System.err.println(c.getTime());
System.err.println(c.get(Calendar.DAY_OF_YEAR));
This prints
Wed Dec 31 23:00:00 GMT-01:00 1969
365
Thu Jan 01 23:00:00 GMT-01:00 1970
1
This demonstrates that it is not enough to use e.g. c.get(Calendar.DAY_OF_YEAR). In this case one must always take into account what time of day it is. This can be avoided by using GMT explicitly when creating the GregorianCalendar: new GregorianCalendar(TimeZone.getTimeZone("GMT")). If the calendar is created such, the output is:
Wed Dec 31 23:00:00 GMT-01:00 1969
1
Thu Jan 01 23:00:00 GMT-01:00 1970
2
Now the calendar returns useful values. The reason why the Date returned by c.getTime() is still "off" is that the toString() method uses the default TimeZone to build the string. At the top we set this to GMT-1 so everything is normal.
Calendar cal = new GregorianCalendar();
cal.setTimeInMillis(0);
cal.add(Calendar.DAY_OF_MONTH, daysSinceEpoch);
I'm grabbing some data from a database that has a stored date value, and I'm letting the user pick date ranges they would like to view data for. All my code for getting these date ranges works except for the method to get the date range covering all time, which would be a start value of the earliest possible data Java handles, to the end value of the max possible date.
Is there something wrong with my code, because I can't see a problem:
public static DateRange getAllTime() {
/**
* Get earliest possible
*/
Calendar c = Calendar.getInstance();
c.set(
c.getActualMinimum(Calendar.YEAR),
c.getActualMinimum(Calendar.MONTH),
c.getActualMinimum(Calendar.DAY_OF_MONTH),
c.getActualMinimum(Calendar.HOUR),
c.getActualMinimum(Calendar.MINUTE),
c.getActualMinimum(Calendar.SECOND)
);
c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
Date start = c.getTime();
/**
* Get latest possible date
*/
c.set(
c.getActualMaximum(Calendar.YEAR),
c.getActualMaximum(Calendar.MONTH),
c.getActualMaximum(Calendar.DAY_OF_MONTH),
c.getActualMaximum(Calendar.HOUR),
c.getActualMaximum(Calendar.MINUTE),
c.getActualMaximum(Calendar.SECOND)
);
c.set(Calendar.MILLISECOND, c.getActualMaximum(Calendar.MILLISECOND));
Date end = c.getTime();
DateRange range = new DateRange();
range.Start = start;
range.End = end;
return range;
}
Why not use
new Date(Long.MIN_VALUE) (in YEAR 292269055 BC)
new Date(Long.MAX_VALUE) (in YEAR 292278994 AD)?
Since froginvasion challenged the answer, I thought I'd double check
long day=1000*60*60*24;
System.out.println(new Date(Long.MAX_VALUE-day));
System.out.println(new Date(Long.MAX_VALUE));
System.out.println(new Date(0));
System.out.println(new Date(-day));
System.out.println(new Date(Long.MIN_VALUE));
System.out.println(new Date(Long.MIN_VALUE+day));
gave me
Sat Aug 16 07:12:55 GMT 292278994
Sun Aug 17 07:12:55 GMT 292278994
Thu Jan 01 00:00:00 GMT 1970
Wed Dec 31 00:00:00 GMT 1969
Sun Dec 02 16:47:04 GMT 292269055
Mon Dec 03 16:47:04 GMT 292269055
I think it is right. I assume the AD/BC are just being suppressed. The suggestion to use new Date(0) as the minimum is clearly wrong because new Date(-day) is clearly smaller.
Why make life so complicated? If you don't have a start date, don't query for a start date. If you don't have an end date, don't query for an end date. And if you have neither, don't query for dates at all.
That code works me, maybe you're not expecting the values it returns?
Start: Sat Jan 01 00:00:00 PST 1
End: Wed Apr 17 21:34:08 PST 292269054
(It would be easier to help if you included the stack trace)
I suspect may get an overflow by setting the year and then setting maximum values for all the other fields separately. That would make your end time somewhere around your start time and cause all records to be rejected. You might try just printing out the calendar times to see what's happening.
As seanizer points out, you're really making this more complicated than it should be - the correct way to deal with this is to leave the date clause off entirely in the query. That may be difficult sometimes because the sql statement isn't generated dynamically. But note that even if you can't modify the sql at run time, the condition (in Oracle)
start_date >= nvl(?, start_date)
will always be satisfied if the supplied value is null and start_date is populated.
Min and max supported by your database?
You may actually need the minimum and maximum value supported by your database rather than the minimum and maximum supported by Java. Some databases have datetime types that only support, say years 0001 through 9999. I haven’t tried nor studied the documentation, but I might suspect that either your JDBC driver or your query may fail if you pass dates outside the interval supported by the database.
For example the MariaDB documentation says:
MariaDB stores values that use the DATETIME data type in a format
that supports values between 1000-01-01 00:00:00.000000 and
9999-12-31 23:59:59.999999.
It’s not very nice to have Java code that depends on a specific brand of database engine (DBMS), though. If you nevertheless need to, just hardcode the values:
public static final LocalDateTime MIN_DATABASE_DATETIME
= LocalDateTime.of(1000, Month.JANUARY, 1, 0, 0);
public static final LocalDateTime MAX_DATABASE_DATETIME
= LocalDateTime.of(9999, Month.DECEMBER, 31, 23, 59, 59, 999_999_000);
I am using and recommending java.time, the modern Java date and time API.
Min and max supported by Java?
The minimum and maximum values supported by Java are built in as constants. java.time supports a wider range than the old-fashioned Date class did. Let’s see what the minimum and maximum are:
System.out.println("Min Java LocalDateTime: " + LocalDateTime.MIN);
System.out.println("Max Java LocalDateTime: " + LocalDateTime.MAX);
Output:
Min Java LocalDateTime: -999999999-01-01T00:00
Max Java LocalDateTime: +999999999-12-31T23:59:59.999999999
What went wrong in your code?
Is there something wrong with my code, because I can't see a problem:
You are correct, while your code gives a minimum value that you may be able to use as minimum, the max value that it gives is about as far off as can be. When I ran it just now, I got:
Sat Jan 01 00:00:00 CST 1 - Wed Apr 17 21:34:08 CST 292269054
Calendar and Date did support dates before the common era (“before Christ”), so giving year 1 as minimum is not correct. This happens because you have not changed the era of your Calendar, so it remains in CE (“AD”). The YEAR field number denotes year of era, so the minimum value is 1.
The maximum value I got looks like it lies 9940 years before the maximum possible Date but probably still way above any date actually present in your data. This is not so! The value you got is in year 292269054 before the common era (“before Christ“) and just 4 and a half months after the minimum value of a Date, pretty much the opposite of what you had tried to find. This is probably due to undetected arithmetic overflow in the computation. I suppose that Calendar never meant to guard against overflow and underflow.
getActualMinimum() and getActualMaximum() give you the min and max values possible given the actual value of the Calendar. So c.getActualMinimum(Calendar.MONTH) would give you 28 in February in a non-leap year and 31 in July. This is not what you wanted. Using Calendar.HOUR is a further bug since HOUR denotes the hour within AM or PM from 0 through 11.
The actual min and max values of Date and Calendar do not fall on midnight nor at New Year, but your code will try to find values at or very close to midnight in the night of New Year, which is the flaw by your technique. Trying to find December 31 in the year where the maximum value fell in August probably accounts for the overflow I mentioned. The 4 and half months you overflowed by roughly agrees with the 4 and a half months above the minimum value where we ended up.
Links
DATETIME in the MariaDB knowledge base
Oracle tutorial: Date Time explaining how to use java.time.