I'm receiving a datetime from a SOAP webservice without timzone information. Hence, the Axis deserializer assumes UTC. However, the datetime really is in Sydney time. I've solved the problem by substracting the timezone offset:
Calendar trade_date = trade.getTradeDateTime();
TimeZone est_tz = TimeZone.getTimeZone("Australia/Sydney");
long millis = trade_date.getTimeInMillis() - est_tz.getRawOffset();
trade_date.setTimeZone( est_tz );
trade_date.setTimeInMillis( millis );
However, I'm not sure if this solution also takes daylight saving into account. I think it should, because all operations are on UTC time. Any experiences with manipulating time in Java? Better ideas on how to solve this problem?
I pity the fool who has to do dates in Java.
What you have done will almost certainly go wrong around the daylight savings transitions. The best way to to it is probably to create a new Calendar object, set the Timezone on it, and then set all of the fields individually, so year, month, day, hour, minute, second, getting the values from the Date object.
Edit:
To keep the everyone happy, you should probably do this:
Calendar utcTime = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Calendar sydneyTime = Calendar.getInstance(TimeZone.getTimeZone("Australia/Sydney");
utcTime.setTime(trade_date);
for (int i = 0; i < Calendar.FIELD_COUNT; i++) {
sydneyTime.set(i, utcTime.get(i));
}
Then you won't be using any deprecated methods.
I want to thank the person for responce 6. This was a great start for me and an approach I did not consider. There are some addtional steps required to bring it to production code level. In particular observe the steps required for DST_OFFSET and ZONE_OFFSET. I want to share the solution I came up with.
This takes the time from the input Calendar object, copies it to the output time, sets the new time zone to the output. This is used when taking time literally from the database and setting the Time Zone without changing the time.
public static Calendar setNewTimeZoneCopyOldTime( Calendar inputTime,
TimeZone timeZone ) {
if( (inputTime == null) || (timeZone == null) ) { return( null ); }
Calendar outputTime = Calendar.getInstance( timeZone );
for( int i = 0; i < Calendar.FIELD_COUNT; i++ ) {
if( (i != Calendar.ZONE_OFFSET) && (i != Calendar.DST_OFFSET) ) {
outputTime.set(i, inputTime.get(i));
}
}
return( (Calendar) outputTime.clone() );
}
However, I'm not sure if this solution
also takes daylight saving into
account. I think it should, because
all operations are on UTC time.
Yes, you should take the daylight saving into account, since it affects the offset to UTC.
Any experiences with manipulating time in Java? Better ideas on how to solve this problem?
Joda-Time is a better time API. Maybe the following snippet could be of help :
DateTimeZone zone; // TODO : get zone
DateTime fixedTimestamp = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond, zone);
JodaTime types are immutable which is also a benefit.
I normally do it this way
Calendar trade_date_utc = trade.getTradeDateTime();
TimeZone est_tz = TimeZone.getTimeZone("Australia/Sydney");
Calendar trade_date = Calendar.GetInstance(est_tz);
trade_date.setTimeInMillis( millis );
Are you getting an ISO 8601 style string from that messed-up Web Service? If so, the Joda-Time 2.3 library makes this very easy.
If you are getting an ISO 8601 string without any time zone offset, you pass a time zone object to the DateTime constructor.
DateTimeZone timeZone = DateTimeZone.forID( "Australia/Sydney" );
String input = "2014-01-02T03:00:00"; // Note the lack of time zone offset at end.
DateTime dateTime = new DateTime( input, timeZone );
Dump to console…
System.out.println( "dateTime: " + dateTime );
When run…
dateTime: 2014-01-02T03:00:00.000+11:00
#Test
public void tzTest() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS Z");
TimeZone tz1 = TimeZone.getTimeZone("Europe/Moscow");
Calendar cal1 = Calendar.getInstance(tz1);
long l1 = cal1.getTimeInMillis();
df.setTimeZone(tz1);
System.out.println(df.format(cal1.getTime()));
System.out.println(l1);
TimeZone tz2 = TimeZone.getTimeZone("Africa/Douala");
Calendar cal2 = Calendar.getInstance(tz2);
long l2 = l1 + tz1.getRawOffset() - tz2.getRawOffset();
cal2.setTimeInMillis(l2);
df.setTimeZone(tz2);
System.out.println(df.format(cal2.getTime()));
System.out.println(l2);
assertNotEquals(l2, l1);
}
Running CalendarTest
2016-06-30 19:09:16.522 +0300
1467302956522
2016-06-30 19:09:16.522 +0100
1467310156522
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.137 sec
I've decided to reparse the datetime string received with the correct time zone set. This should also consider daylight saving:
public class DateTest {
private static SimpleDateFormat soapdatetime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
/**
* #param args
*/
public static void main(String[] args) {
TimeZone oztz = TimeZone.getTimeZone("Australia/Sydney");
TimeZone gmtz = TimeZone.getTimeZone("GMT");
Calendar datetime = Calendar.getInstance( gmtz );
soapdatetime.setTimeZone( gmtz );
String soap_datetime = soapdatetime.format( datetime.getTime() );
System.out.println( soap_datetime );
soapdatetime.setTimeZone( oztz );
datetime.setTimeZone( oztz );
try {
datetime.setTime(
soapdatetime.parse( soap_datetime )
);
} catch (ParseException e) {
e.printStackTrace();
}
soapdatetime.setTimeZone( gmtz );
soap_datetime = soapdatetime.format( datetime.getTime() );
System.out.println( soap_datetime );
}
}
Related
I'm trying to create a simple Alarm Clock, but I stumbled upon a problem that I can't seem to fix. I'm trying to parse a string to a date so I can get the difference between the current time and the time to set off the alarm.
Here's my code to parse the time:
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
sdf.setTimeZone(getTimezone());
Date date = sdf.parse(args[0]);
Here's my getTimezone() method:
public static TimeZone getTimezone() {
Calendar cal = Calendar.getInstance();
long milliDiff = cal.get(Calendar.ZONE_OFFSET);
String [] ids = TimeZone.getAvailableIDs();
String name = null;
for (String id : ids) {
TimeZone tz = TimeZone.getTimeZone(id);
if (tz.getRawOffset() == milliDiff) {
// Found a match.
name = id;
break;
}
}
return TimeZone.getTimeZone(name);
}
And here's my code for figuring out the difference:
long diff = date.getTime() - System.currentTimeMillis();
So my problem is that the date.getTime() returns 79680000, while System.currentTimeMillis() returns 1473538047978 (This is of course different every time, but for some odd reason, date.getTime() is not).
Which means that I get a negative number when trying to figure out the difference, and therefore I cannot use it.
EDIT: After a little bit of debugging, I realised that it has to do with the year, month and day not being set, however I do not know how to get those.
You did notice that date.getTime() returns 79680000 which is 22 hours and 20 minutes after 1 January 1970. The problem is (as you noticed) that you did not parse year, month and day.
You can do it by:
SimpleDateFormat sdf = new SimpleDateFormat("DD/MM/YYYY hh:mm:ss");
Example input 20/04/2016 20:20:0 returns time as Mon Jan 04 20:20:00 CET 2016 (don't look at the timezone). It is 1451935200000 miliseconds after 1 January 1970.
Note: change string to match your format requirements (the syntax is self-explanatory).
The accepted answer by Ronin is correct. You are trying to put a time-of-day value into a date-time type.
java.time
Also, you are using troublesome old legacy date-time classes. Now supplanted by the java.time classes.
For a time-of-day value without a date and without a time zone, use LocalTime.
LocalTime alarmTime = LocalTime.parse( "12:34" );
Getting current time-of-day requires a time zone.
ZoneId z = ZoneId.of( "America/Montreal" );
LocalTime now = LocalTime.now( z );
But since we are setting an alarm, we care about the date too.
ZonedDateTime now = ZonedDateTime.now( z );
ZonedDateTime alarm = null;
if ( now.toLocalTime().isBefore( alarmTime ) ) {
alarm = ZonedDateTime.of( now.toLocalDate() , alarmTime , z );
} else {. // Else too late for today, so set alarm for tomorrow.
alarm = ZonedDateTime.of( now.toLocalDate().plusDays( 1 ) , alarmTime , z );
}
To calculate the elapsed time until the alarm, use the Duration class.
Duration untilAlarm = Duration.between( now , alarm );
You can interrogate the duration for a total number of milliseconds. But know that java.time classes are capable of handling nanoseconds.
long millis = untilAlarm.toMillis();
Updated.
You are using only time without a date with you date object in code (parses only time). If you add there date to you time, your date should be comparable to your System.getCurrentTimeMillis() call. And if you subtracting current millis from date in the past, you will have negative numbers. I prefer this convertion (date2 is after date1):
long diffInMillies = date2.getTime() - date1.getTime();
return TimeUnit.convert(diffInMillies, TimeUnit.MILLISECONDS);
I have some time Strings such as "09:00" and "17:30" and I need to check if the current time is between that range.
I thought I could make this comparison:
SimpleDateFormat format = new SimpleDateFormat("HH:mm");
Date now = new Date();
Date begin;
Date end;
begin = format.parse(begin_string);
end = format.parse(end_string);
if (now.compareTo(begin) > 0 && end.compareTo(now) > 0)
return true;
else
return false;
Turns out that when I parse the strings, the times are parsed correctly, but the date is set to Jan 1st 1970. This way, the code will always return false.
I'd like to know how can I set begin and end to the current date, but with the times from their strings.
You could also just reuse your format object for current time like this way:
SimpleDateFormat format = new SimpleDateFormat("HH:mm");
Date now = new Date();
String time = format.format(now); // format to wall time loosing current date
System.out.println(time);
now = format.parse(time); // reparse wall time
System.out.println(now);
So you transform now to 1970 using implicitly the standard time zone of your system and can then use it for direct comparisons with begin and end.
Date begin = format.parse("09:00");
Date end = format.parse("21:30");
return (begin.before(now) && end.after(now)); // open-bounded interval
Get current time, Calendar.getInstance();
Get another 2 instance of current time, and set time fields based on your input
For example:
Calendar.set(Calendar.HOUR_OF_DAY, 1);
and invoke compare() on the boundry of time
You should really use a Calendar. Then you can individually set the hours and minutes from values parsed from the string. Then get the time in milliseconds and compare those.
Date now = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(now);
cal.set(Calendar.HOUR_OF_DAY, hours);
cal.set(Calendar.MINUTE, minutes);
long time = cal.getTimeInMillis();
You could also use the wonderful Joda library. In my opinion Joda is a much better way to work with Dates and Times.
The bundled java.util.Date & .Calendar classes are notoriously troublesome. Avoid them. Use either Joda-Time library or the new java.time package found in Java 8 (inspired by Joda-Time, defined by JSR 310).
If you truly do not care about time zone or date, use either the Joda-Time LocalTime class or the java.time LocalTime class.
Caution: Naïve programmers often think they need only local time and can therefore ignore time zones, but then live to regret that position.
Joda-Time
If your times are in proper ISO 8601 format (24-hours, correct number of digits), then you can directly pass the string inputs to the constructor of LocalTime without bothering to parse. That class has a built-in ISO 8601 style parser.
String inputStart = "09:00";
String inputStop = "17:30";
LocalTime start = new LocalTime( inputStart );
LocalTime stop = new LocalTime( inputStop );
LocalTime now = LocalTime.now();
// Comparing using Half-Open logic, where beginning is inclusive and ending is exclusive.
boolean isNowContainedWithinInterval = ( ( now.isEqual( start ) ) || ( now.isAfter( start ) ) ) && ( now.isBefore( stop ) );
Dump to console…
System.out.println( "start: " + start );
System.out.println( "stop: " + stop );
System.out.println( "now: " + now );
System.out.println( "isNowContainedWithinInterval: " + isNowContainedWithinInterval );
When run…
start: 09:00:00.000
stop: 17:30:00.000
now: 12:42:06.567
isNowContainedWithinInterval: true
In the real-world, I would add an assertion test proving the stop time is later than the start time, to validate inputs.
I just wanted to find date parameter is current date (yyyy-MM-dd) without using simpledateformater or any date to string convertion and then find is equals.
specifiedDate=2012-12-20
currentDate=2012-12-21
specifiedDate == currentDate
to be simple i dont want time (i.e HH:mm:S) not to be included while validating
i have tried something like
public boolean isCurrentDate(Calendar date){
Calendar currentDate = Calendar.getInstance().getTime();
if (currentDate.getDate()==(date.getTime().getDate())
&& currentDate.getMonth()==(date.getTime().getMonth())
&& currentDate.getYear()==(date.getTime().getYear()) )
{
return true;
}
return false;
}
please suggest a better way or if any libraries already available for this !!
What about setting time fields to 0 before comparing
currentDate.set(Calendar.HOUR_OF_DAY, 0);
currentDate.set(Calendar.MINUTE, 0);
currentDate.set(Calendar.SECOND, 0);
currentDate.set(Calendar.MILLISECOND, 0);
Try this if you want to do only
1) Using strings
String s1 = new String("2012-01-27");
String s2 = new String("2011-01-28");
System.out.println(s1.compareTo(s2));
The result will be TRUE if s1 is "bigger" than s2 in lexicographical way and it's what you need. To get more info read javadoc for compareTo() method.
2) Using Joda Time
Using Joda Time lib you can acheive as below
DateTime first = ...;
DateTime second = ...;
LocalDate firstDate = first.toLocalDate();
LocalDate secondDate = second.toLocalDate();
return firstDate.compareTo(secondDate);
I prefer second option
If you are using calendar
public static boolean isSameDay(Calendar cal1, Calendar cal2) {
if (cal1 == null || cal2 == null) {
throw new IllegalArgumentException("The dates must not be null");
}
return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) &&
cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR));
}
public static boolean isToday(Calendar cal) {
return isSameDay(cal, Calendar.getInstance());
}
If you are using Date
public static boolean isSameDay(Date date1, Date date2) {
if (date1 == null || date2 == null) {
throw new IllegalArgumentException("The dates must not be null");
}
Calendar cal1 = Calendar.getInstance();
cal1.setTime(date1);
Calendar cal2 = Calendar.getInstance();
cal2.setTime(date2);
return isSameDay(cal1, cal2);
}
public static boolean isToday(Date date) {
return isSameDay(date, Calendar.getInstance().getTime());
}
Your last line && currentDate.getYear()==(date.getMonth()) ) appears to be comparing the year and month not the year and year. Could this be your issue?
Try this:
currentDate.set(Calendar.DATE, 0);
Time Zone
The example code in your question ignores the crucial issue of time zone. The date, that is the beginning and ending points of a day, is defined by a time zone.
Both java.util.Calendar and java.util.Date have no time zone assigned. They represent a date and a time in UTC/GMT.
So you need to apply a desired time zone, relevant to the context of your app & data. That means you need a decent date-time library. Something other than java.util.Date/Calendar, java.text.SimpleDateFormat and their sibling classes, as they are notoriously troublesome. Use either Joda-Time or the new java.time.* package bundled with Java 8 (inspired by Joda-Time, defined by JSR 310).
Note the use of the method withTimeAtStartOfDay. That gets the first moment of the day. That is usually the time 00:00:00 but not always. Daylight Saving Time (DST) or other anomalies may produce a different time. That method smartly handles such issues.
Today = Span of Time
Technically, when working with date-time values, a particular "date" is actually a span of time. The most common and generally useful way to define that span is "half-open" where the beginning is inclusive and the ending is exclusive. That means, for current date, we want the first moment of today (inclusive) to the first moment of tomorrow (exclusive). Then we ask if the target date-time falls within that span.
There are other ways to get this job done. I'm showing this approach because it is applies to situations beyond the question of "today".
Joda-Time
Joda-Time offers three classes for defining a span of time: Interval, Period, and Duration.
Example Code
Setup our input data, a Calendar object.
// Create a Calendar object to simulate input.
java.util.Date date = DateTime.now().minusDays( 3 ).toDate();
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.setTime( date );
Define "today" as a span of time, and see if target date-time falls within that span.
DateTimeZone timeZone = DateTimeZone.forID( "Asia/Kolkata" );
DateTime dateTimeInQuestion = new DateTime( cal.getTimeInMillis(), timeZone );
DateTime now = new DateTime( timeZone );
Interval today = new Interval( now.withTimeAtStartOfDay(), now.plusDays( 1 ).withTimeAtStartOfDay() );
boolean isDateTimeInQuestionInInterval = today.contains( dateTimeInQuestion );
Dump to console…
System.out.println( "cal: " + cal.getTime() );
System.out.println( "dateTimeInQuestion: " + dateTimeInQuestion );
System.out.println( "now: " + now );
System.out.println( "today: " + today );
System.out.println( "isDateTimeInQuestionInInterval: " + isDateTimeInQuestionInInterval );
When run…
cal: Wed Feb 12 22:46:04 PST 2014
dateTimeInQuestion: 2014-02-13T12:16:04.369+05:30
now: 2014-02-16T12:16:04.497+05:30
today: 2014-02-16T00:00:00.000+05:30/2014-02-17T00:00:00.000+05:30
isDateTimeInQuestionInInterval: false
I am trying to set the year of a java.util.Date.
he time stamp I need to parse does not include the year so I did this:
private static final SimpleDateFormat logTimeStampFormat =
new SimpleDateFormat("MMM dd HH:mm:ss.SSS");
boolean isAfterRefDate (String line, Date refDate) {
try {
Date logTimeStamp = logTimeStampFormat.parse(line);
logTimeStamp.setYear(2012); // But this is deprecated!
return logTimeStamp.after(refDate);
} catch (ParseException e) {
// Handle exception
}
}
To avoid using a deprecated method, I do like this:
private static final SimpleDateFormat logTimeStampFormat =
new SimpleDateFormat("MMM dd HH:mm:ss.SSS");
private static Calendar cal = Calendar.getInstance();
boolean isAfterRefDate (String line, Date refDate) {
try {
Date logTimeStamp = logTimeStampFormat.parse(line);
cal.setTime(logTimeStamp);
cal.set(Calendar.YEAR, 2012);
logTimeStamp = cal.getTime();
return logTimeStamp.after(refDate);
} catch (ParseException e) {
// Handle exception
}
}
I just don't think that this is the best way to solve this problem. I have to first set the calendar object properly and then get the date object back from it while earlier I could just modify the date object directly.
Can someone suggest a better approach?
Can someone suggest a better approach.
Sure - try to avoid using Date and Calendar in the first place. Use Joda Time instead, which is much better.
Setting the year on a Date is an inherently ambiguous operation - what time zone is this year meant to be? What would you expect to happen if you're setting a year of 2013 on a previous date of February 29th 2012?
Using Joda Time will make your code much clearer in terms of what kind of data you're really expecting. You can always convert to/from Date and Calendar at API boundaries if you really need to.
java.time
Can someone suggest a better approach?
Yes, use the java.time classes.
The Answer by Jon Skeet was correct but is now outdated, as the Joda-Time team advises migration to the java.time classes.
MonthDay & LocalTime
We can parse that input string as two separate objects, a MonthDay and a LocalTime. The first is obviously a month and a day-of-month but without any year so it is not a complete date. The second represents a time-of-day but without any date and without any time zone.
String input = "Sep 21 12:34:56.123";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "MMM dd HH:mm:ss.SSS" );
MonthDay md = MonthDay.parse ( input , f );
LocalTime lt = LocalTime.parse ( input , f );
System.out.println ( "md: " + md + " | lt: " + lt );
md: --09-21 | lt: 12:34:56.123
LocalDateTime
We can add in a year, stir altogether, and get a LocalDateTime.
LocalDateTime ldt = LocalDateTime.of( ld , lt );
ZonedDateTime
This is still not an actual moment, not a point on the timeline. Without the context of an offset-from-UTC or a time zone, a LocalDateTime has no meaning.
Apply a ZoneId to get a ZonedDateTime. Now we have a meaningful moment, a point on the timeline.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( z );
In java, how can I find out if a specific date is within 1 year of today's date.
I have the following but not sure if this is the best approach.
String date = "01/19/2005";
DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
Date lastExamTakenDate = null;
Calendar todaysDateMinus1Year = Calendar.getInstance();
todaysDateMinus1Year.add(Calendar.YEAR, -1);
if (date!=null)
{
try {
lastExamTakenDate = df.parse(date);
if (lastExamTakenDate.before(todaysDateMinus1Year.getTime()))
hasToTakeExam = true;
} catch (ParseException ex) {
//exception
}
}
The java.util.Date & .Calendar classes bundled with Java are notoriously troublesome. Avoid them. Use either Joda-Time or the new java.time package bundled with Java 8 (and inspired by Joda-Time).
Joda-Time
If you are certain you want date only without time or time zone, use the LocalDate class (found in both Joda-Time and java.time).
Note the use of a time zone when asking for the current date. The date varies depending on where you are at on earth at the moment. If you fail to specify a date, the JVM’s default time zone will be used. Generally better to specify.
Here is some example code using Joda-Time 2.3.
String input = "01/19/2005";
DateTimeFormatter formatter = DateTimeFormat.forPattern( "MM/dd/yyyy" );
LocalDate localDate = LocalDate.parse( input, formatter );
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
LocalDate now = LocalDate.now( timeZone );
LocalDate yearAgo = now.minusYears( 1 );
boolean withinYearAgo = ( ( localDate.isAfter( yearAgo ) ) & ( localDate.isBefore( now ) ) );
Dump to console…
System.out.println( "input: " + input );
System.out.println( "localDate: " + localDate );
System.out.println( "now: " + now );
System.out.println( "yearAgo: " + yearAgo );
System.out.println( "withinYearAgo: " + withinYearAgo );
When run…
input: 01/19/2005
localDate: 2005-01-19
now: 2014-04-10
yearAgo: 2013-04-10
withinYearAgo: false
You might want to add a test for "or is equal to" depending on your definition of "within last year".
If you call getTime() on a date object it will return a long with milliseconds since epoch (jan 1. 1970). Checking if a date is within the last year is then a simple matter of creating one date object with a date one year ago and doing comparison on the long values (someDate > aYearAgo). Alternatively you can use the after() method on a calendar object. To create a calendar/date object with a value one year ago you can use calObj.add(Calendar.YEAR, -1).
I believe something like this will get you the start of the calendar day so that time of day is not a factor.
GregorianCalendar calToday = new GregorianCalendar();
GregorianCalendar oneYearAgoTodayAtMidnight = new GregorianCalendar(calToday.get(Calendar.YEAR) - 1, calToday.get(Calendar.MONTH), calToday.get(Calendar.DATE));
This approach ignores leap-years (and other calendar-caused oddities), but is very straightforward:
public boolean isWithinAYear(Date inputDate) {
Date d = new Date() // Get "now".
long dLong = d.getTime();
// You could multiply this next line out and use a single constant,
// I didn't do that for clarity (and the compiler will optimize it
// out for us anyhow.)
long oneYearAgo = dLong - (365 * 24 * 60 * 60 * 1000);
return inputDate.getTime() > oneYearAgo;
}
Your solution using GregorianCalendar is technically more correct.
This woks perfectly.
public class X {
public static Date date ;
public static Date date1 ;
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, 0);
boolean time;
date = cal.getTime();
System.out.println(date);
time = withinYear(date);
System.out.println(time);
}
private static boolean result;
public static boolean withinYear(Date inputDate)
{
Calendar todaysDateMinus1Year = Calendar.getInstance();
todaysDateMinus1Year.add(Calendar.YEAR, -1);
date1 = todaysDateMinus1Year.getTime();
if (inputDate.before(date1))
result= true;
return result;
}