Strange behaviour when comparing DateTime objects with JODA - java

Let me depict my scenario:
I have a running app in a server that is located in Dallas, Texas (I think it internally uses EDT timezone). In this server I need to get the time server, convert it to Europe/Madrid timezone and then check if the obtained date is within a Date Interval.
The weird thing is that I'm getting a response that suggests that the current server time once it is converted to Europe/Madrid timezone in BEFORE the lower date interval, which is very weird.
Here is how I'm doing this, getting the server time and convert it to Europe/Madrid timezone:
DateTimeZone timeZoneMadrid = DateTimeZone.forID( "Europe/Madrid" );
DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm");
DateTime nowServer = new DateTime();
log.debug("Current server time is " + nowServer.toString(formatter));
DateTime nowServerSpanishTimeZone = nowServer.withZone(timeZoneMadrid);
log.debug("Current server time converted to Madrid Zone is " + nowServerSpanishTimeZone.toString(formatter));
Output:
Current server is 2014-10-06 06:12
Current server time converted to Madrid Zone is 2014-10-06 12:12
Now, I create the DateTime for the interval, start and end, based on the converted DateTime:
int year = serverTimeConverted.getYear();
int month = serverTimeConverted.getMonthOfYear();
int day = serverTimeConverted.getDayOfMonth();
this.setStartDate(new DateTime(year, month, day, 8, 0, 0, 0));
this.setEndDate(new DateTime(year, month, day, 21, 0, 0, 0));
As you can see, my interval goes from 08:00:00 to 21:00:00
Then I check if the server time converted is within the date range, this is very verbose because I added a lot of checking and output because of the strange behaviour...:
private boolean withinTimeRange(DateTime now, DateTime start, DateTime end){
DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYYMMdd-HH:mm");
String currentDate = now.toString(formatter);
long nowTimeStamp = now.getMillis() / 1000;
long startTimeStamp = start.getMillis() / 1000;
long endTimeStamp = end.getMillis() / 1000;
log.debug("Checking if date " + currentDate + " is in the interval dates " + start.toString(formatter) + " and " + end.toString(formatter));
log.debug("Checking if UNIX timestamp " + nowTimeStamp + " is in the interval dates " + startTimeStamp + " and " + endTimeStamp);
if (!now.isBefore(start)){
log.debug("Current time " + currentDate + " is not before " + start.toString(formatter));
if (!now.isAfter(end)){
log.debug("Current time " + currentDate + " is not after " + end.toString(formatter));
return true;
}
else{
log.debug("Current time " + currentDate + " is after " + end.toString(formatter));
return false;
}
}
else{
log.debug("Current time " + currentDate + " is before " + start.toString(formatter));
return false;
}
}
Just call the method with the time server converted and the start and end dates and, for the previous output, where server time converted is 2014-10-06 12:12, I get this output from previous method:
Checking if date 20141006-12:12 is in the interval dates 20141006-08:00 and 20141006-21:00
Checking if UNIX timestamp 1412590332 is in the interval dates 1412596800 and 1412643600
Current time 20141006-12:12 is before 20141006-08:00
Current timeserver converted to Madrid TimeZone is not within time range, skipping iteration
As you can see the timestamp from the server time converted is before the start datetime.....how is this possible?
I think I'm doing something wrong when creating the DateTime start and end, I've tried creating them with .withTimeZone("Europe/Madrid"), but then I get even strangest behaviour...any clues?
Thanks!
UPDATE: based on previous SO question here, I modified my previous code and now it works:
DateTime now = new DateTime();
LocalDate today = now.toLocalDate();
LocalDate tomorrow = today.plusDays(1);
DateTimeZone timeZoneMadrid = DateTimeZone.forID( "Europe/Madrid" );
DateTime start = today.toDateTimeAtStartOfDay(timeZoneMadrid);
DateTime end = tomorrow.toDateTimeAtStartOfDay(timeZoneMadrid);
start = start.plusHours(8);
end = end.minusHours(4);
Interval interval = new Interval(start, end);
DateTimeFormatter formatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm");
String currentDate = now.toString(formatter);
if (interval.contains(now)){
return true;
}
else{
return false;
}

Method DateTime::getMillis returns milliseconds independent of zone. This value is a constant.
Once created DateTime instance will return the same millis in any time zone.
final DateTime now = DateTime.now();
now.withZone(texas).getMillis() == now.withZone(madrid).getMillis(); // true
DateTime::isAfter and DateTime::isBefore methods compares millis returned by DateTime::getMillis method.
So this value is also zone independent.
But printing DateTime with DateTimeFormatter is zone dependent. It will print different hours/minutes in in different time zones.
So, if you want compare dates zone independently, then your result is correct.
Example:
- Precondition:
DateTimeZone usZone = DateTimeZone.forID("US/Eastern");
DateTimeZone spZone = DateTimeZone.forID("Europe/Madrid");
DateTimeZone.setDefault(usZone);
Your code works in next way:
DateTime serverDate = new DateTime(2014, 10, 6, 6, 12); // US zone
DateTime dateInMadrid = serverDate.withZone(spZone); // zone is Madrid, but .getMillis() will return the same value
DateTime startDate = new DateTime(2014, 10, 6, 8, 0); // US zone
// startDate = startDate.withZone(spZone) - this will not change the result
DateTime endDate = new DateTime(2014, 10, 6, 21, 0); // US zone
// endDate = endDate.withZone(spZone) - this will also not change the result
System.out.println(dateInMadrid .isAfter(startDate) && dateInMadrid .isBefore(endDate));
// false - it is correct. because all dates were created in US zone
Correct way: you should create start and end dates in Madrid zone
DateTime serverDate = new DateTime(2014, 10, 6, 6, 12);
DateTime startDateMadrid = new DateTime(2014, 10, 6, 8, 0, spZone); // Madrid zone is used in constructor!
DateTime endDateMadrid = new DateTime(2014, 10, 6, 21, 0, spZone); // Madrid zone is used in constructor!
System.out.println(serverDate.isAfter(startDateMadrid) && serverDate.isBefore(endDateMadrid));
// true

Related

Calculate last third of two Dates in Java

I am searching for a method to get the last third of two dates.
first example:
Date 1 = 22:00
Date 2 = 01:00 (next day)
calculateLastThird(); (output: 00:00)
second example:
Date 1 = 22:25
Date 2 = 01:45 (next day)
calculateLastThird(); (output: 00:38)
I already know how to get the midpoint between those two dates:
Date midpoint = new Date((date1.getTime() + date2.getTime()) / 2);
java.time
I recommend that you use java.time, the modern Java date and time API, for your date and time work.
ZoneId zone = ZoneId.of("America/Boise");
ZonedDateTime zdt1 = ZonedDateTime.of(2020, 12, 26, 22, 0, 0, 0, zone);
ZonedDateTime zdt2 = ZonedDateTime.of(2020, 12, 27, 1, 0, 0, 0, zone);
Duration fullElapsedTime = Duration.between(zdt1, zdt2);
Duration twoThirds = fullElapsedTime.multipliedBy(2).dividedBy(3);
ZonedDateTime lastThird = zdt1.plus(twoThirds);
System.out.println(lastThird);
Output from this snippet is:
2020-12-27T00:00-07:00[America/Boise]
Three things I like about this code are:
It pretty well mimics the way one would do the calculation by hand and how you would explain to someone else which calculation you want at all.
It takes any transistion to or from summer time (DST) into account.
It leaves the actual calculation to the library methods. It involves no low-level addition or division in your own code.
Link
Oracle tutorial: Date Time explaining how to use java.time.
One way to do this is the following:
Date midpoint = new Date((date1.getTime() + 2 * date2.getTime()) / 3);
If you insist using Date.
It should be a simple math:
First, need to calculate the difference between the two dates (in milliseconds).
Then get the 2/3 rd of that value and add it to the first date.
private static Date calculateLastThird(Date d1, Date d2) {
// Calculate time difference in milliseconds
long differenceMillis = d2.getTime() - d1.getTime();
double millisToAdd = (2.0/3.0) * differenceMillis;
Date twoThird = new Date(d1.getTime() + (long) millisToAdd);
return twoThird;
}
public static void main(String args[]) {
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
try {
Date date1= sdf1.parse("2020/12/26 22:00:00");
Date date2= sdf1.parse("2020/12/27 01:00:00");
SimpleDateFormat sdf2 = new SimpleDateFormat("HH:mm");
System.out.println("Last third: " + sdf2.format(calculateLastThird(date1, date2)));
Date date3= sdf1.parse("2020/12/26 22:25:00");
Date date4= sdf1.parse("2020/12/27 01:45:00");
System.out.println("Last third: " + sdf2.format(calculateLastThird(date3, date4)));
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Sample output of above code:
Last third: 00:00
Last third: 00:38

Java Joda time isAfter date solution

I want to compare two dates.
If the current date time is greater or after the
specific date , then it will return 'True'.
So far I have tried this.
String deadline = "25/11/2017 11:00:00";
DateTime utc = new DateTime(DateTimeZone.UTC);
DateTimeZone timeZone = DateTimeZone.forID("Asia/Dhaka");
DateTime dhakaTime = utc.toDateTime(timeZone);
//Dead Line Time
DateTimeFormatter format = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss");
DateTime deadlineTime = format.parseDateTime(deadline.trim());
//Comapare
return deadlineTime.isAfter(dhakaTime.plusDays(2));
As today is 23 and dhakaTime.plusDays(2) will be 25 so it should return
"true".
But I am getting "false".
Output value :
dhakaTime.plusDays(2) = 2017-11-25T14:10:27.762+06:00
deadlineTime = 2017-11-25T11:00:00.000+06:00
Am i missing something or doing something wrong?
It gives false correctly.
You're comparing
deadLineTime = 2017-11-25T11:00:00.000+06:00 and
dhakaTime(+2) = 2017-11-25T14:10:27.762+06:00
They both are at same date, but with time, deadLineTime is at 11AM but dhakaTime(+2) is at 2PM. So,
(Nov 25, 2017 11AM)isAfter(Nov 25, 2017 2PM) is false.
EDIT: Testcases
As you mentioned you're testing, The following used different test cases for comparing deadLine with dhakaTime (+1, and +2 days). I hope this gives you an idea about how this works.
public static void main(String[] args) {
String deadline = "25/11/2017 11:00:00";
DateTime utc = new DateTime(DateTimeZone.UTC);
DateTimeZone timeZone = DateTimeZone.forID("Asia/Dhaka");
DateTime dhakaTime = utc.toDateTime(timeZone);
//Dead Line Time
DateTimeFormatter format = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss");
DateTime deadlineTime = format.parseDateTime(deadline.trim());
System.out.println("Deadline : " + deadline);
System.out.println("Current datetim : " + dhakaTime);
System.out.println("current datetime + 1 day : " + dhakaTime.plusDays(1));
System.out.println("current datetime + 2 day : " + dhakaTime.plusDays(2));
System.out.println("Is deadline after current datetime:" + deadlineTime.isAfter(dhakaTime));
System.out.println("Is deadline after current datetime + 1 day:" + deadlineTime.isAfter(dhakaTime.plusDays(1)));
System.out.println("Is deadline after current datetime + 2 day:" + deadlineTime.isAfter(dhakaTime.plusDays(2)));
}
Try using the method DateTimeFormatter withZone(DateTimeZone zone) and use that to create the DateTime

UTC-N to Local Time

Using joda, how do you format a UTC+/-n time, to "wall time" to be displayed to the user:
From (UTC+/-n):
2015-05-15T03:28:49.523-04:00
To (EST) Wall:
2015-05-14 23:22:44
Update (1)
Please consider the following code. We need to use timestamp
for writes to and from the DB in UTC. With that in mind:
DateTimeZone.setDefault(DateTimeZone.UTC);
LocalDateTime utcDate = new LocalDateTime();
DateTimeZone utcTZ = DateTimeZone.forTimeZone(TimeZone.getTimeZone("ETC/UTC"));
DateTimeZone localTZ = DateTimeZone.forTimeZone(TimeZone.getTimeZone("America/Montreal"));
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
fmt.withZone(localTZ);
DateTime localDateTime = utcDate.toDateTime(localTZ);
DateTime utcDateTime = localDateTime.toDateTime(utcTZ);
Timestamp u = new Timestamp(utcDateTime.getMillis());
System.out.println("UTC Time: " + u);
LocalDateTime date = new LocalDateTime(u);
DateTime srcDateTime = date.toDateTime(utcTZ);
DateTime dstDateTime = srcDateTime.toDateTime(localTZ);
System.out.println("UTC+/- Time: " + dstDateTime.toString());
DateTime dateTimeInTargetTimezone = dstDateTime.withZone(localTZ);
System.out.println("Wall Time: " + dateTimeInTargetTimezone.toString("yyy-MM-dd HH:mm:ss"));
Now, when extracting the UTC time from the DB in a Timestamp object, we need
to display the time to the end user in a "Wall/Funeral Time", whatever you want to call it, in their TZ.
Output
UTC Time: 2015-05-15 20:03:47.561 "Good"
UTC+/- Time: 2015-05-15T20:03:47.561-04:00 "Good"
Wall Time: 2015-05-15 20:03:47 "No! No! No! Danger! We'll be late!"
What in the name! Do I have to do to get dstDateTime to equal the time I see on my wall (ie, 2015-05-15 4:03:47).
Update (2)
Got rid of Timestamp:
DateTimeZone utcTZ = DateTimeZone.forTimeZone(TimeZone.getTimeZone("ETC/UTC"));
DateTimeZone localTZ = DateTimeZone.forTimeZone(TimeZone.getTimeZone("America/Montreal"));
DateTimeFormatter fmt = DateTimeFormat.forPattern("yyy-MM-dd HH:mm:ss");
LocalDateTime utcDate = new LocalDateTime(utcTZ);
DateTime utcDateTime = utcDate.toDateTime(utcTZ);
System.out.println("UTC Time: " + utcDateTime);
DateTime dstDateTime = utcDateTime.toDateTime(localTZ);
System.out.println("Unformated Wall Time: " + dstDateTime);
System.out.println("Wall Time: " + dstDateTime.toString(fmt));
Output
UTC Time: 2015-05-20T14:09:28.469Z
Unformated Wall Time: 2015-05-20T10:09:28.469-04:00
Wall Time: 2015-05-20 10:09:28
Everything looks perfect however, when I try to right the UTZ date to the DB,
I need to convert to Timestamp (ie, new Timestamp(o.getOrderDate().getMillis())), and it obviously rights the local time to the DB, and not the UTC Zulu time that I need.
Thanks in Advance,
Nick.
#Nick
I'm not sure if I understood your question correctly but you can try this:
//sample input
String timestamp = "2015-05-15T03:28:49.523-04:00";
//format to parse
DateTimeFormatter dateTimeF = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
//parse to local date time
LocalDateTime dateTime = dateTimeF.parseLocalDateTime(timestamp).minusHours(4);
//output here minus 4 hours
System.out.println(dateTime);
You can improve your implementation from my sample code above and make the offset hours more dynamic but so far this code provides the result as you mentioned in your example.
Goodluck!
Just use the DateTime.withZone method to change the timezone:
#Test
public void change_timezone() {
String input = "2015-05-15T03:28:49.523-04:00";
DateTimeZone targetTimeZone = DateTimeZone.forID("Europe/London");
DateTime dateTime = DateTime.parse(input);
DateTime dateTimeInTargetTimezone = dateTime.withZone(targetTimeZone);
assertThat(dateTimeInTargetTimezone.toString("yyy-MM-dd HH:mm:ss"),
equalTo("2015-05-15 08:28:49"));
}

How to find the days between dates using my code?

Below is my code which checks the date which is stored in database with the current system date and calculates the days and if that days is lesser than the 180 days it will print something else print nothing,this code works great in an normal java program(with out using swings concept) if it is used with the swing program i changed the sql query to check get the date from the database based on the department and staff names which is entered in the text fields,i coded this code inside an jbutton,in the output it just prints the current system date but not calculates the days between the selected date and the current system dates,friends this is the problem am facing kindly need your help friends....thanks in advance..
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/leave", "root", "");
Statement stm = conn.createStatement();
ResultSet rs = stm.executeQuery("select * from staff where depmt='" + txt1 + "' AND staffs='" + txt2 + "'");
Calendar javaCalendar = null;
String currentDate = "";
javaCalendar = Calendar.getInstance();
currentDate = javaCalendar.get(Calendar.DATE) + "/" + (javaCalendar.get(Calendar.MONTH) + 1) + "/" + javaCalendar.get(Calendar.YEAR);
int cdate = javaCalendar.get(Calendar.DATE);
int cmonth = (javaCalendar.get(Calendar.MONTH) + 1);
int cyear = javaCalendar.get(Calendar.YEAR);
int z = 0;
int date = 0, month = 0, year = 0;
System.out.println("Current Date\t" + currentDate);
System.out.println("\n");
while (rs.next()) {
date = rs.getInt(3);
month = rs.getInt(4);
year = rs.getInt(5);
System.out.println("Random Date\t" + date + "/" + month + "/" + year + "\n");
int d = (date - cdate);
int m = month - cmonth;
int y = year - cyear;
int d1 = java.lang.Math.abs(d);
int d2 = java.lang.Math.abs(m);
int d3 = java.lang.Math.abs(y);
z = d1 + (d2 * 30) + (d3 * 365);
if (z >= 180) {
System.out.println("something");
0
} else {
System.out.println("nothing");
}
}
} catch (Exception e) {
System.out.println(e.getMessage());
//e.printStackTrace();
}
// TODO add your handling code here:
}
You should really use prepared statements cause this way your query is prone to sql injections.
Date formatter insted of concating string for currentdate
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
String formattedDate = formatter.format(todaysDate);
Also it seems like your not closeing the connection that may be another issue.
Is there any reason for storeing the date in 3 separate columns?
Your algorithm to calculate the day difference between two dates is broken. It does not take in account different month lengths or leap years.
Unfortunately Java Calendar does not offer this feature at all. So either you apply your own homegrown algorithm (not easy, but in web there are some sources how to map a gregorian date to epoch days) or you use JodaTime like this way:
LocalDate db = new LocalDate(year, month, date);
int days = Days.daysBetween(db, LocalDate.now()).getDays();
Note that the result will be negative if db date is in the future. After all you can greatly shorten your code and abandon all Calendar stuff which is very bad for calculations of durations.
try this:
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
Date date1 = df.parse("14/02/2014");
Date date2 = df.parse("08/03/2014");
int days = Days.daysBetween(date1, date2).getDays();
Try this, by changing return value from millisecond to day.
public static int daysBetween(Date dateFrom, Date dateTo){
return (int)( (dateTo.getTime() - dateFrom.getTime()) / (1000 * 60 * 60 * 24));
}
Start Of Day
If you start with a mid-afternoon date-time object, go back 180 days by calculating seconds * minutes * hours * 180, you'll end up excluding the date-times earlier in that day 180 ago whereas I suppose you would want to include them. You should pay attention to when the day begins.
Time Zone
Both the question and other answers ignore the issue of time zone. Time zone defines the beginning of a day. Given the point about start of day (above), time zone is a related component.
Avoid java.util.Date & .Calendar
The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome. Avoid them. Instead use either Joda-Time or the new java.time package in Java 8.
Joda-Time
Here is some example code using Joda-Time 2.3.
Note that while a Joda-Time DateTime object is similar to a java.util.Date, a DateTime does truly know its own assigned time zone.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime dateTimeInQuestion = new DateTime( 2013, 6, 5, 4, 3, 2, timeZone );
DateTime now = new DateTime( timeZone );
DateTime hundredEightyDaysAgo = now.minusDays( 180 ).withTimeAtStartOfDay();
boolean isExpired = dateTimeInQuestion.isBefore( hundredEightyDaysAgo );
Dump to console…
System.out.println( "dateTimeInQuestion: " + dateTimeInQuestion );
System.out.println( "now: " + now );
System.out.println( "hundredEightyDaysAgo: " + hundredEightyDaysAgo );
System.out.println( "isExpired: " + isExpired );
When run…
dateTimeInQuestion: 2013-06-05T04:03:02.000+02:00
now: 2014-03-10T07:34:26.937+01:00
hundredEightyDaysAgo: 2013-09-11T00:00:00.000+02:00
isExpired: true

JODA is acting crazy?

I'm trying to use JODA to simply convert a numeric timestamp (a long representing Unix epoch time), to a Month Day, Year string.
Here's code I just ran a few seconds ago:
long lTimestamp = 1315600867; // Current timestamp is approx 9/9/11 3:41 PM EST
DateTime oTimestamp = new DateTime(lTimestamp);
String strMon, strDay, strYear;
strMon = oTimestamp.monthOfYear().getAsText(Locale.ENGLISH);
strDay = oTimestamp.dayOfMonth().getAsText(Locale.ENGLISH);
strYear = oTimestamp.year().getAsText(Locale.ENGLISH);
String strDate = strMon + " " + strDay + ", " + strYear;
System.out.println("Converted timestamp is : " + strDate);
The output to this is January 16, 1970!!!
Does this make any sense to anyone?!?!
The long you pass into the DateTime constructor is meant to be in milliseconds, not seconds - so use 1315600867000L instead and it's all fine.
Documentation states:
Constructs an instance set to the milliseconds from 1970-01-01T00:00:00Z using ISOChronology in the default time zone.
If you're getting a value which is already in seconds, you just need to multiply by 1000:
long timestampInSeconds = getValueFromDatabase();
long timestampInMillis = timestampInSeconds * 1000L;
DateTime dt = new DateTime(timestampInMillis);
I'd actually advise you to use Instant in this case rather than DateTime - you don't really have a time zone to consider. If you are going to use DateTime, you should specify the time zone explicitly, e.g.
DateTime dt = new DateTime(timestampInMillis, DateTimeZone.UTC);

Categories

Resources