java util to convert cron expression from one timezone to another [duplicate] - java

I am looking for a way to convert cron expression from one timezone to another one timezone.
For example, my web-client user is GMT+1200 and my server-side is GMT+0800, while user setting 02:10 to execute task every Tuesday and Thursday, the cron expression will be 0 10 2 3,5 * ?, and I have used code as below, it can get current fire time for user's timezone
CronExpression expr = new CronExpression("0 10 2 3,5 * ?");
System.out.println(expr.getNextValidTimeAfter(new Date()));
System.out.println(expr.getTimeZone());
System.out.println(expr.getExpressionSummary());
System.out.println("=======");
TimeZone tz = TimeZone.getTimeZone("GMT+1200");
expr.setTimeZone(tz);
System.out.println(expr.getNextValidTimeAfter(new Date()));
System.out.println(expr.getTimeZone());
System.out.println(expr.getExpressionSummary());
The getNextValidTimeAfter will print Mon Feb 02 22:10:00 CST 2015, which after setTimeZone(tz);, however the getExpreesionSummary or even getCronExpression() will still be 0 10 2 3,5 * ?, where I want to get string will be 0 10 22 2,4 * ? and then I can save into DB for next time fire and also another time-zone user to query setting (of course this will need to convert 0 10 22 2,4 * ? to this user's timezone)
Any help is appreciated

If you are willing to retain same cron expression, but give contextual calculations based on date timezone (so that user and serverside get next execution based on their timezones for same expression), you may use cron-utils, which provides such functionality. All next/previous execution calculations are contextual to timezone, since release 3.1.1.
They provide an example at the docs:
//Get date for last execution
DateTime now = DateTime.now();
ExecutionTime executionTime = ExecutionTime.forCron(parser.parse("* * * * * * *"));
DateTime lastExecution = executionTime.lastExecution(now));
//Get date for next execution
DateTime nextExecution = executionTime.nextExecution(now));
nextExecution value will be calculated for same timezone as reference date (now).

Related

Changing Java Timestamp format causes a change of the timestamp

I'm a bit puzzled regarding the Java timestamp formatting functions as they seem to change the timestamp by a few minutes.
My Pivotal CloudFoundry app writes a new entry into a PostgreSQL 9.4.5 database. Doing so, I let PostgreSQL generate and store a timestamp for that entry (within the SQL statement I use 'now()' for that particular column).
All that works fine so far. Problems arise when I read the entry with my app because I have to format the timestamp to ISO 8601 (company policy) and this seems to change the timestamp. I've used this code:
public String parseTimestamp(String oldDate) {
System.out.println("String oldDate: " + oldDate);
TimeZone timeZone = TimeZone.getTimeZone("Europe/Berlin");
SimpleDateFormat oldDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSS");
SimpleDateFormat newDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
newDateFormat.setTimeZone(timeZone);
Date tempDate = null;
try {
tempDate = oldDateFormat.parse(oldDate);
} catch (ParseException e) {
//TODO
}
System.out.println("tempDate before format(): " + tempDate);
String newDate = newDateFormat.format(tempDate);
System.out.println("tempDate after format(): " + tempDate);
System.out.println("String newDate: " + newDate);
return newDate;
}
Output:
String oldDate: 2018-06-18 13:07:27.624828+02
tempDate before format(): Mon Jun 18 13:17:51 CEST 2018
tempDate after format(): Mon Jun 18 13:17:51 CEST 2018
String newDate: 2018-06-18T13:17:51Z
As you can see, the timestamp changed from 13:07 to 13:17. All the other queried entries also show a difference, varying between ~2 to 10 minutes. However, when I re-run the query, each difference stays the same. OPs told me that all involved systems have synchronised time and I'm not sure if system time should play a role here at all.
Does someone know what is going on?
First, I was really puzzled and thought, you might have found some kind of bug in SimpleDateFormat. Your code can be reduced to the following lines to show the same behaviour:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSS");
String str = "2018-06-18 13:07:27.624828+02";
System.out.println(str);
System.out.println(format.parse(str));
Result:
2018-06-18 13:07:27.624828+02
Mon Jun 18 13:17:51 CEST 2018
The parsed Date object got ten additional minutes (and some seconds).
Taking a closer look reveals the problem - it is NOT a bug in JDK:
Your millisecond-part is 624828 - these are six(!) digits (not three). 624828 milliseconds are about 624 seconds, which are 10 minutes and 24 seconds.
The SimpleDateFormat correctly summed up this additional seconds and minutes.
What's wrong too: Your date format contains five digits for the milliseconds part.
Solution:
The (new) Java Time API can handle larger fractions of a second - and also convert easily between different time zones:
String str = "2018-06-18 13:07:27.624828+02";
DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSx");
OffsetDateTime date = OffsetDateTime.parse(str, pattern);
System.out.println(date);
System.out.println(date.withOffsetSameInstant(ZoneOffset.UTC));
Result:
2018-06-18T13:07:27.624828+02:00
2018-06-18T11:07:27.624828Z
Your input date contains micro seconds but SimpleDateFormat supports only milli seconds (up to 3 digits). When parsing the value with the flag lenient enabled the complete 6 digits are evaluated as micro seconds. So at the end you have 624 seconds that are added to the date additionally.
You can try to use the SSSSSS pattern with DateTimeFormatter that has been introduced in Java 8.
But you may have further issues. Your code parses the input date without the time zone information +02 that is included at the end. You should add one letter X for the ISO8601 time zone format. When formatting the date for outputting you use letter 'Z' that indicates time zone UTC. But you set time zone CET (Berlin). So the output date is not in UTC. The correct ouput in UTC for the specified input date is:
2018-06-18 11:07:27Z

Java XMLGregorianCalendar format

I am currently stuck with XMLGregorianCalendar formatting problem and would like to seek help from you java gurus. With a function call from other system, I got a data object displayed on web page with "SUBMIT_DATE":1516032000000 and "SUBMIT_TIME":36895000 (both with returned type XMLGregorianCalendar). How can I know the correct human readable date and time in this case?
Thank you for your time and help.
Update after clarification
// We first need to check that the fields we need are defined
if (submitDate.getTimezone() == DatatypeConstants.FIELD_UNDEFINED) {
throw new IllegalStateException("No time zone defined in submit date " + submitDate);
}
if (submitDate.getYear() == DatatypeConstants.FIELD_UNDEFINED
|| submitDate.getMonth() == DatatypeConstants.FIELD_UNDEFINED
|| submitDate.getDay() == DatatypeConstants.FIELD_UNDEFINED) {
throw new IllegalStateException("Date not defined in submit date " + submitDate);
}
if (submitTime.getHour() == DatatypeConstants.FIELD_UNDEFINED
|| submitTime.getMinute() == DatatypeConstants.FIELD_UNDEFINED
|| submitTime.getSecond() == DatatypeConstants.FIELD_UNDEFINED) {
throw new IllegalStateException("Time of day not defined in submit time " + submitTime);
}
if (submitTime.getTimezone() != DatatypeConstants.FIELD_UNDEFINED
&& submitTime.getTimezone() != submitDate.getTimezone()) {
throw new IllegalStateException("Conflicting offsets " + submitDate.getTimezone()
+ " and " + submitTime.getTimezone() + " minutes");
}
// then format into a human readable string
final ZoneId userZone = ZoneId.of("Asia/Taipei");
final Locale userLocale = Locale.forLanguageTag("zh-TW");
DateTimeFormatter localizedFormatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(userLocale);
ZonedDateTime dateTime = submitDate.toGregorianCalendar()
.toZonedDateTime()
.with(LocalTime.of(submitTime.getHour(), submitTime.getMinute(), submitTime.getSecond()))
.withZoneSameInstant(userZone);
String humanReadableDateTime = dateTime.format(localizedFormatter);
System.out.println(humanReadableDateTime);
This prints:
2018年1月16日 上午10時14分55秒
I am assuming that submitDate and submitTime are XMLGregorianCalendar objects that you have got from the complex object that you have received from a remote system. I am further assuming that you can require the date to contain a UTC offset. Though the method is called getTimezone, what it really returns is not a time zone, but an offset in minutes from UTC (or GMT). The extensive checks in the four if statements are necessary because XMLGregorianCalendar is very flexible with which fields are defined and which not.
To display the date and time in a format suitable for a user audience, you need to know that audience’s time zone and locale. Once you know those, please fill them in in the above snippet. If you trust the JVM’s settings, you may use ZoneId.systemDefault() and/or Locale.getDefault(Locale.Category.FORMAT) You may also choose between format styles FULL, LONG, MEDIUM and SHORT.
If you don’t receive an offset, you will need to rely on the date and time already being at the user’s offset. On one hand it’s simpler, on the other hand it is more fragile since if the date and time are given at another offset than the user expects, s/he will receive incorrect information, which is worse than receiving no information at all. First check that there is indeed no offset:
if (submitDate.getTimezone() != DatatypeConstants.FIELD_UNDEFINED
|| submitTime.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
throw new IllegalStateException("Unexpected offset");
}
Also check that required fields are defined, this is the same as before. Then create a LocalDateTime object and format it:
LocalDateTime dateTime = LocalDateTime.of(
submitDate.getYear(), submitDate.getMonth(), submitDate.getDay(),
submitTime.getHour(), submitTime.getMinute(), submitTime.getSecond());
String humanReadableDateTime = dateTime.format(localizedFormatter);
I got the same result as above.
Original answer
final ZoneId userZone = ZoneId.of("Asia/Taipei");
final Locale userLocale = Locale.forLanguageTag("zh-TW");
ZonedDateTime submitDateTime
= Instant.ofEpochMilli(submitDate + submitTime).atZone(userZone);
DateTimeFormatter localizedFormatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(userLocale);
String humanReadableDateTime = submitDateTime.format(localizedFormatter);
System.out.println(humanReadableDateTime);
This prints
2018年1月16日 上午10時14分55秒
To display the date and time in a format suitable for a user audience, you need to know that audience’s time zone and locale. Once you know those, please fill them in in the first two lines of the above snippet. If you trust the computer’s settings, you may use ZoneId.systemDefault() and/or Locale.getDefault(Locale.Category.FORMAT) You may also choose between format styles FULL, LONG, MEDIUM and SHORT. For this purpose I think you can ignore the information that the returned type is XMLGregorianCalendar.
As #user unknown in another answer I am assuming that you can just add the two numeric values. The first almost certainly denotes milliseconds since the epoch, the sum probably too. So why were they passed as two values and not just one? My best guess is that they pass the date separately for any client that just needs the date and not the time of day. The date value falls at midnight in time zones at offset +08:00, this would agree with China, Philippines, Malaysia and a dozen other time zones.
If instead of the numbers you have got two XMLGregorianCalendar objects, getting the date and time is a different story, but you may still use the same way of formatting them.
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(date);
return DatatypeFactory.newInstance().newXMLGregorianCalendar(
calendar);
This should work..
Pass your millisecs
Your inputs look like they're just the date without time in milliseconds and the time without date in milliseconds.
If you divide both values by 1000:
date -d #1516032000
Mo 15. Jan 17:00:00 CET 2018
date -d #36895
Do 1. Jan 11:14:55 CET 1970
Well - but why 17:00:00? Maybe a time zone issue.
Here is the aggregate:
date -d #$((1516032000+36895))
Di 16. Jan 03:14:55 CET 2018
The various date/time formats for Java have methods, which take a long parameter for seconds since epoch (1.1.1970) to set the time.

insert and extract hour an minute from oracle

I work with oracle
I want to insert data which contains hour and minute
I have this column : DATE_ARCH
the type of this column is date
I have this java code which should insert date in this column
transfers.setDateArch(new java.sql.Date(
System.currentTimeMillis()));
but when I try to extract hour and minute from DATE_ARCH
using this sql code :
select to_char(transfers.DATE_ARCH , 'HH:MM') from transfers where id_transfer='TR-300'
I have all time this value :
12:05
Updated :
I try with this code :
Timestamp t = new Timestamp(new Date().getTime());
transfers.setDateArch(t);
I try to extract hour and minute using this code :
select to_char(transfers.DATE_ARCH , 'HH24:MI') from transfers where id_transfer='TR-258'
but I have in all case this value :
00:00
as I already said the type of DATE_ARCH is date
When I try in sql with :
UPDATE transfers SET DATE_ARCH = SYSDATE
I have the correct value using
select to_char(transfers.DATE_ARCH , 'HH24:MI') from transfers where id_transfer='TR-258'
now I want to know how can I insert date with hour and minute using java code
You are correct. I do not know enough about the java interface to Oracle to know the right solution. But, your solution is inserting the date with no time. The expression:
to_char(transfers.DATE_ARCH , 'HH:MM')
is returning "12" because that is midnight and "05" because it is May. The correct expression for minutes is:
to_char(transfers.DATE_ARCH , 'HH:MI')
and for a 24-hour clock:
to_char(transfers.DATE_ARCH , 'HH24:MI')
I do not, alas, know how to fix the java code. But perhaps there is a DateTime method that you can use.

Modify date without modifying time

With JodaTime, without using the 'plus' or 'minus' functions and using the least lines of code, how can I set a new date without modifying the time?
My first attempt was to store the 'time' parts of the DateTime in separate ints using getHoursOfDay() and getMinutesOfHour() etc - then create a new DateTime with the required date and set the hours, minutes, and seconds back again. But this method is pretty clunky, and I was wondering if there was a less verbose method for doing this - ideally with just one line of code.
For example:
22/05/2013 13:40:02 >>>> 30/08/2014 13:40:02
Is JodaTime a must? Basic way to do this is
1. extract just time from timestamp.
2. add this to just date
long timestamp = System.currentTimeMillis(); //OK we have some timestamp
long justTime = timestamp % 1000 * 60 * 60 * 24;// just tiem contains just time part
long newTimestamp = getDateFromSomeSource();//now we have date from some source
justNewDate = newTimestamp - (newTimestamp % 1000 * 60 * 60 * 24);//extract just date
result = justNewDate + justTime;
Something like this.
Previously accepted answer were removed by moderator, as it contains only link to javadoc.
Here is edited version.
You could do it like this
DateTime myDate = ...
myDate.withDate(desiredYear, desiredMonth, desiredDayOfMonth);
JavaDoc is here: DateTime.withDate(int year, int month, int dayOfMonth)
use withFields like this:
new DateTime().withFields(new LocalDate(2000,1,1))
This will set all date time fields of the DateTime to those that are contained in the LocalDate - year, month and day in this case. This will work with any ReadablePartial implementation like YearMonth for example.

Update/ Retrieve /Inserting date field

I am having difficulties while updating a date field into the Database. The field type in the DB is Date/Time.
Now, I am trying to update the field name "R_Date".
Currently, I am using the SQL Expression in my jsp"
UPDATE request SET request_date ='"+Request_Date+"'"; , But it is not accepting.
In the select statement I am using a normal select, I tried to use to_char or to_date, but it is not accepting the format of "DD-MMM-YYYY"
So, can you please help me to retrive/Update/Insert date field in the format of "DD-MMM-YYYY" the date field?
The normal practice to store a timestamp in the DB (thus, java.util.Date in Java side and java.sql.Timestamp in JDBC side) is to use PreparedStatement#setTimestamp().
Date requestDate = getItSomehow();
Timestamp timestamp = new Timestamp(requestDate.getTime());
preparedStatement = connection.prepareStatement("UPDATE request SET request_date = ?");
preparedStatement.setTimestamp(1, timestamp);
The normal practice to obtain a timestamp from the DB is to use ResultSet#getTimestamp().
Timestamp timestamp = resultSet.getTimestamp("request_date");
Date requestDate = timestamp; // You can just upcast.
To convert between java.util.Date and java.lang.String you normally use SimpleDateFormat:
// Convert from String to Date.
String requestDateAsString = "09-Aug-2010";
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH);
Date requestDate = sdf.parse(requestDateAsString);
// Convert from Date to String.
String anotherDateAsString = sdf.format(someDate);
See also:
PreparedStatement tutorial
How to avoid Java code in JSP file (!!!)
I think you should use MON instead of MMM.
Have you tried something like:
UPDATE request
SET request_date = to_date('" + Request_Date + "', 'DD-MON-YYYY')
Hope you realize that as your statement stands (if it worked), it would update every row in the request table (not sure if that's your intention or not but I thought I'd point it out).
You need to check what date format you are trying to insert, and try using to_date method with appropriate format.
Following is referenced from : http://infolab.stanford.edu/~ullman/fcdb/oracle/or-time.html
Oracle's default format for DATE is "DD-MON-YY".
If you want to retrieve date in particular format you need to use :
TO_CHAR(<date>, '<format>')
Similarly if you need to insert/update date with input of date other than in standard format, you need to use :
TO_DATE(<string>, '<format>')
where the <format> string can be formed from over 40 options. Some of the more popular ones include:
MM Numeric month (e.g., 07)
MON Abbreviated month name (e.g., JUL)
MONTH Full month name (e.g., JULY)
DD Day of month (e.g., 24)
DY Abbreviated name of day (e.g., FRI)
YYYY 4-digit year (e.g., 1998)
YY Last 2 digits of the year (e.g., 98)
RR Like YY, but the two digits are ``rounded'' to a year in the range 1950 to 2049. Thus, 06 is considered 2006 instead of 1906
AM (or PM) Meridian indicator
HH Hour of day (1-12)
HH24 Hour of day (0-23)
MI Minute (0-59)
SS Second (0-59)

Categories