Adding time to SimpleDateFormat in Groovy - date

I am trying to add time to groovy parameter which have DateTime stored in SimpleDateFormat.
import groovy.time.TimeCategory
import java.text.SimpleDateFormat
def testCase = messageExchange.modelItem.testCase;
def startdatetime = testCase.testSuite.project.getPropertyValue("StartDateTime").toString();
log.info startdatetime
aaa = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(startdatetime)
use(TimeCategory)
{
def enddatetime = aaa + 5.minutes
log.info enddatetime
}
startdatetime : Wed Nov 08 19:57:50 IST 2017:INFO:2017-11-08T15:00:00.000Z
Error popup displayed with message
'Unparseable date: "2017-11-08T15:00:00.000Z"'

If the date string is Wed Nov 08 19:57:50 IST 2017 and you want to convert it to date object, then you could do:
def dateString = "Wed Nov 08 19:57:50 IST 2017"
def dateFormat = "EEE MMM dd HH:mm:ss Z yyyy"
def date = Date.parse(dateFormat, dateString)
Looks you wanted to add 5 minutes to it which can be done as did already
def endDate
use(TimeCategory) { endDate = date + 5.minutes }
log.info "End date : $endDate"
If you want the date object to formatted, then do:
def outputDateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
log.info "Formatted date: ${date.format(outputDateFormat)}"
Another suggestion after looking at your code to get the project property value, use below one-liner.
Change From:
def testCase = messageExchange.modelItem.testCase;
def startdatetime = testCase.testSuite.project.getPropertyValue("StartDateTime").toString();
To:
def startDateTime = context.expand('${#Project#StartDateTime}')

Instead of "yyyy-MM-dd'T'HH:mm:ss'Z'" you probably want "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" since your input string includes milliseconds.

I have got no experience with Groovy, but I assume that since you can use Java classes, you can also use the modern Java date and time API. I much recommend that over the long outdated SimpleDateFormat class. Your format, 2017-11-08T15:00:00.000Z, is in no way tied to SimpleDateFormat, on the contrary, it’s ISO 8601, the format that the modern date and time classes (as opposed to the old ones) “understand” natively without the need for an explicit formatter for parsing.
So I suggest you try (by no means tested):
import java.time.Instant
import java.time.temporal,ChronoUnit
and
aaa = Instant.parse(startdatetime)
and maybe (if you still need or want to use the Java classes)
enddatetime = aaa.plus(5, ChronoUnit.MINUTES)

Related

I have a string date format 01/01/2017 6:54 PM and want to convert it to 2017-01-01T00:00:05.383+0100 ISOFormat in scala

def cleantz( time : String ) : String = {
var sign_builder= new StringBuilder ++= time
println(sign_builder)
var clean_sign = ""
if (sign_builder.charAt(23).toString == "-"){
clean_sign= sign_builder.replace(23,24,"-").toString()
}else{
clean_sign = sign_builder.replace(23,24,"+").toString()
}
var time_builder= new StringBuilder ++= clean_sign
if (time_builder.charAt(26).toString == ":"){
val cleanz = time_builder.deleteCharAt(26)
cleanz.toString()
}else{
time_builder.toString()
}
}
val start = ISO8601Format.parse(cleantz(01/01/2017 6:54 PM))
I get this error:
java.lang.StringIndexOutOfBoundsException: String index out of range: 23
java.time
For the sake of completeness I should like to contribute the modern answer. It’s quite simple and straightforward.
I am sorry that I can neither write Scala code nor test it on my computer. I have to trust you to translate from Java.
private static DateTimeFormatter inputFormatter
= DateTimeFormatter.ofPattern("MM/dd/yyyy h:mm a", Locale.US);
public static String cleantz(String time) {
return LocalDateTime.parse(time, inputFormatter)
.atOffset(ZoneOffset.ofHours(1))
.toString();
}
Now cleantz("01/01/2017 6:54 PM") returns 2017-01-01T18:54+01:00, which is in ISO 8601 format. I would immediately suppose that you’re set. If for some reason you want or need the seconds too, replace .toString(); with:
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
Now the result is 2017-01-01T18:54:00+01:00. In both cases the milliseconds would have been printed if there were any.
Since AM and PM are hardly used in other languages than English, I suggest you give an English-speaking locale to DateTimeFormatter.ofPattern() (in my example I used Locale.US). Failing to provide a locale will cause the code to fail on many computers with non-English language settings.
Why java.time?
SimpleDateFormat and friends are long outdated and notoriously troublesome. I cannot count the questions asked on Stack Overflow because SimpleDateFormat behaved differently from what every sane programmer would have expected, or offered no help to debug the simple errors we all make from time to time.
Joda-Time was good for a long time. Today the Joda-Time homepage says:
Note that Joda-Time is considered to be a largely “finished” project.
No major enhancements are planned. If using Java SE 8, please migrate
to java.time (JSR-310).
java.time is the modern Java date & time API built using the experience from Joda-Time and under the same lead developer, Stephen Colebourne. It is built into Java 8 and later, and a backport exists for Java 6 and 7, so you can use the same classes there too.
Assuming that your input string is 01/01/2017 6:54 PM: it has 18 characters. When you call charAt(23), it tries to get the character at position 23, which doesn't exist: the string has positions from zero (the first 0) to 17 (the M). If you try to get a position greater than that, it throws a StringIndexOutOfBoundsException.
But you don't need to do all this string manipulation. If you have a string that represents a date in some format, and want to convert it to another format, all you need is:
parse the original string to a date
format this date to another format
So you need 2 different Joda formatter's (one for each step). But there's one additional detail.
The input has a date (01/01/2017) and a time (6:54 PM), and the output has a date (2017-01-01), a time (18:54:00.000) and the UTC offset (+0100). So you'll have an additional step:
parse the original string to a date
add the +0100 offset to the parsed date
format this date to another format
With Joda-Time, this can be achieved with the following code:
import org.joda.time.DateTimeZone
import org.joda.time.LocalDateTime
import org.joda.time.format.DateTimeFormat
import org.joda.time.format.ISODateTimeFormat
val fmt = DateTimeFormat.forPattern("dd/MM/yyyy h:mm a")
// parse the date
val localDate = LocalDateTime.parse("01/01/2017 6:54 PM", fmt)
// add the +01:00 offset
val dt = localDate.toDateTime(DateTimeZone.forOffsetHours(1))
// format to ISO8601
print(ISODateTimeFormat.dateTime().print(dt))
The output will be:
2017-01-01T18:54:00.000+01:00
Note that the offset is printed as +01:00. If you want exactly +0100 (without the :), you'll need to create another formatter:
val formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
println(formatter.print(dt))
The output will be:
2017-01-01T18:54:00.000+0100
This is the code I used to achieve the same result. The error occurred because I was trying to parse the wrong date format.
val inputForm = new SimpleDateFormat("MM/dd/yyyy h:mm a")
val outputForm = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
val dateFormat1 = start_iso
val dateFormat2 = stop_iso
val start = outputForm.format(inputForm.parse(start_iso))
val stop = outputForm.format(inputForm.parse(stop_iso))
println(start)
println(stop)

Multi-locale date parsing

I'm trying to write a class, which able to parse multi-format and multi-locale strings into DateTime.
multi-format means that date might be: dd/MM/yyyy, MMM dd yyyy, ... (up to 10 formats)
multi-locale means that date might be: 29 Dec 2015, 29 Dez 2015, dice 29 2015 ... (up to 10 locales, like en, gr, it, jp )
Using the answer Using Joda Date & Time API to parse multiple formats I wrote:
val locales = List(
Locale.ENGLISH,
Locale.GERMAN,
...
)
val patterns = List(
"yyyy/MM/dd",
"yyyy-MM-dd",
"MMMM dd, yyyy",
"dd MMMM yyyy",
"dd MMM yyyy"
)
val parsers = patterns.flatMap(patt => locales.map(locale => DateTimeFormat.forPattern(patt).withLocale(locale).getParser)).toArray
val birthDateFormatter = new DateTimeFormatterBuilder().append(null, parsers).toFormatter
but it doesn't work:
birthDateFormatter.parseDateTime("29 Dec 2015") // ok
birthDateFormatter.parseDateTime("29 Dez 2015") // exception below
Invalid format: "29 Dez 2015" is malformed at "Dez 2015"
java.lang.IllegalArgumentException: Invalid format: "29 Dez 2015" is
malformed at "Dez 2015"
I found what all parsers: List[DateTimeParser] had "lost" their locales after an appending into birthDateFormatter: DateTimeFormatter. And birthDateFormatter has only one locale - en.
I can write:
val birthDateFormatter = locales.map(new DateTimeFormatterBuilder().append(null, parsers).toFormatter.withLocale(_))
and use it like:
birthDateFormatter.map(_.parseDateTime(stringDate))
but it will throw a lots of exceptions. It's terrible.
How can I parse multi-format and multi-locale strings using joda-time?
How can I do it any other way?
That was interesting to investigate. This is a test suite that helped me (in Java, but I hope you'll get the idea):
import java.util.*;
import java.util.stream.Collectors;
import org.joda.time.DateTime;
import org.joda.time.format.*;
import org.junit.Test;
import static org.assertj.core.api.Assertions.*;
public class JodaTimeLocaleTest {
#Test // fails on both assertions
public void testTwoLocales() {
List<Locale> locales = Arrays.asList(Locale.FRENCH, Locale.GERMAN);
DateTimeParser[] parsers = locales.stream()
.map(locale -> DateTimeFormat.forPattern("dd MMM yyyy").withLocale(locale).getParser())
.collect(Collectors.toList())
.toArray(new DateTimeParser[0]);
DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, parsers).toFormatter();
DateTime dateTime1 = formatter.parseDateTime("29 déc. 2015");
DateTime dateTime2 = formatter.parseDateTime("29 Dez 2015");
assertThat(dateTime1).isEqualTo(new DateTime("2015-12-29T00:00:00"));
assertThat(dateTime2).isEqualTo(new DateTime("2015-12-29T00:00:00"));
}
#Test // passes
public void testFrench() {
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd MMM yyyy").withLocale(Locale.FRENCH);
DateTime dateTime = formatter.parseDateTime("29 déc. 2015");
assertThat(dateTime).isEqualTo(new DateTime("2015-12-29T00:00:00"));
}
#Test // passes
public void testGerman() {
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd MMM yyyy").withLocale(Locale.GERMAN);
DateTime dateTime = formatter.parseDateTime("29 Dez 2015");
assertThat(dateTime).isEqualTo(new DateTime("2015-12-29T00:00:00"));
}
}
First of all, your first example
birthDateFormatter.parseDateTime("29 Dec 2015")
passes only because your machine's default locale is English. If it was different, also this case would have failed. That's why I'm using French and German when running on a machine with English locale. In my case, both assertions fail.
It turns out that the locale is not stored in the parser, but in the formatter only. So when you do
DateTimeFormat.forPattern("dd MMM yyyy").withLocale(locale).getParser()
the locale is set on the formatter, but is then lost when creating the parser:
// DateTimeFormatter#withLocale:
public DateTimeFormatter withLocale(Locale locale) {
if (locale == getLocale() || (locale != null && locale.equals(getLocale()))) {
return this;
}
// Notice how locale does not affect the parser
return new DateTimeFormatter(iPrinter, iParser, locale,
iOffsetParsed, iChrono, iZone, iPivotYear, iDefaultYear);
}
Next, when you create a new formatter
new DateTimeFormatterBuilder().append(null, parsers).toFormatter()
it's created with the system's default locale (unless you override it with withLocale()). And that locale is used during parsing:
// DateTimeFormatter#parseDateTime
public DateTime parseDateTime(String text) {
InternalParser parser = requireParser();
Chronology chrono = selectChronology(null);
// Notice how the formatter's locale is used
DateTimeParserBucket bucket = new DateTimeParserBucket(0, chrono, iLocale, iPivotYear, iDefaultYear);
int newPos = parser.parseInto(bucket, text, 0);
// ... snipped
}
So it turns out that although you can have multiple parsers to support multiple formats, still only a single locale can be used per formatter instance.
Answer to question 1 (How can I parse multi-format and multi-locale strings using joda-time?):
No this is not possible the way you want, see also the good answer of #Adam Michalik. So the only way is just to write a list of multiple Joda-formatters and to try each one for a given input - possibly catching exceptions. You have already found the right workaround so I don't describe the details here.
Answer to question 2 (How can I do it any other way?):
My library Time4J has got a new MultiFormatParser-class since v4.11. However, I discovered some performance issues with its format engine in general (mainly due to autoboxing feature of Java) so I decided to wait with this answer until release v4.12 where I have improved the performance. According to my first benchmarks Time4J-4.12 seems to be quicker than Joda-Time (v2.9.1) because internal exceptions are strongly reduced. So I think you can give that latest version of Time4J a try and report then some feedback if it works for you.
private static final MultiFormatParser<PlainDate> TIME4J;
static {
ChronoFormatter<PlainDate> f1 =
ChronoFormatter.ofDatePattern("dd.MM.uuuu", PatternType.CLDR, Locale.ROOT);
ChronoFormatter<PlainDate> f2 =
ChronoFormatter.ofDatePattern("MM/dd/uuuu", PatternType.CLDR, Locale.ROOT);
ChronoFormatter<PlainDate> f3 =
ChronoFormatter.ofDatePattern("uuuu-MM-dd", PatternType.CLDR, Locale.ROOT);
ChronoFormatter<PlainDate> f4 =
ChronoFormatter.ofDatePattern("uuuuMMdd", PatternType.CLDR, Locale.ROOT);
ChronoFormatter<PlainDate> f5 =
ChronoFormatter.ofDatePattern("d. MMMM uuuu", PatternType.CLDR, Locale.GERMAN);
ChronoFormatter<PlainDate> f6 =
ChronoFormatter.ofDatePattern("d. MMMM uuuu", PatternType.CLDR, Locale.FRENCH);
ChronoFormatter<PlainDate> f7 =
ChronoFormatter.ofDatePattern("MMMM d, uuuu", PatternType.CLDR, Locale.US);
TIME4J = MultiFormatParser.of(f1, f2, f3, f4, f5, f6, f7);
}
...
static List<PlainDate> parse(List<String> input) {
ParseLog plog = new ParseLog();
int n = input.size();
List<PlainDate> result = new ArrayList<>(n);
for (int i = 0; i < n; i++){
String s = input.get(i);
plog.reset();
PlainDate date = TIME4J.parse(s, plog);
if (!plog.isError()) {
result.add(date);
} else {
// log or report error
}
}
return result;
}
Every single parser within MultiFormatParser keeps its own locale.
The order of parser components matters in terms of performance. Prefer those patterns and locales for first positions which are most common in your input.
I strongly recommend to use a static constant for the MultiFormatParser because a) it is immutable and b) constructing formatters is expensive in every library (and Time4J is no exception about this detail).
For interoperability with Joda-Time you can consider this conversion: LocalDate joda = new LocalDate(plainDate.getYear(), plainDate.getMonth(), plainDate.getDayOfMonth()); But keep in mind that every conversion has some extra costs. On the other side, Joda-Time offers less features than Time4J so latter one can do the full job of all date-time-zone relevant tasks, too.
I am not a scala guy but assume that following scala code might compile:
val parser = MultiFormatParser.of(patterns.flatMap(patt => locales.map(locale => ChronoFormatter.ofDatePattern(patt, PatternType.CLDR, locale))).toArray)
By the way: The performance of Joda-Time is not so bad since it was a tough task for me to make it better in Time4J-v4.12. Parsing so different patterns and locales is always a complex task. Surprising for me: The new time library built in Java-8 (package java.time) is the worst in terms of performance according to my own experiments (obviously due to internal exception handling).
If you don't work on Java-8-platforms then you can use Time4J-v3.15 (backport to Java-6-platforms).

How to convert english date to Chinese and Korean

I have tried to implement locale for date. But it is working fine except for Chinese and Korean Language. For Chinese and Korean language, '??' is coming in the place of month.
Language: Groovy,grails 1.3.7
please see the code below
Date from=new Date(params.from)
Date to=new Date(params.to)+1
//params.from and params.to is in english
dateMap.from=from
dateMap.to=to
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy",locale)
String fromDate
String toDate
fromDate=sdf.format(from)
toDate=sdf.format(to)
println "fromdate :"+fromDate
output is
fromdate :09-??-2015
This is working fine for all other languages.
Please see below one more code i tried.
locale=new Locale("zh","CN")
SimpleDateFormat dateFormatCN = new SimpleDateFormat("dd-MMM-yyyy", locale);
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy");
Date temp = dateFormat.parse("05-Sep-2013");
println "temp :"+temp
String out=dateFormatCN.format(temp);
println "out :"+out
output :
temp :Thu Sep 05 00:00:00 IST 2013
out :05-??-2013
This is a problem with your console.
I tried the below in groovyConsole and it worked
import java.text.SimpleDateFormat
def params = [to: '05-Sep-2013', from: '05-Sep-2013']
def locale=new Locale("zh","CN")
Date from=new Date(params.from)
Date to=new Date(params.to)+1 //params.from and params.to is in english
SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy",locale)
String fromDate
String toDate
fromDate=sdf.format(from)
toDate=sdf.format(to)
println "fromdate :"+fromDate​

How to get the current date without time in scala

I want to get the current date so I used:
Calendar.getInstance().getTime()
But as it .getTime() it is returning:
Fri Jul 11 15:07:03 IST 2014
I want only date in any format but without the time.
scala> java.time.LocalDate.now
res4: java.time.LocalDate = 2014-07-11
You may use formatting:
val format = new SimpleDateFormat("d-M-y")
println(format.format(Calendar.getInstance().getTime()))
Since Java 8, you can also use LocalDate class:
println(java.time.LocalDate.now)
OR
java.time.LocalDate.now.toString
val dateFormatter = new SimpleDateFormat("dd/MM/yyyy hh:mm aa")
var submittedDateConvert = new Date()
submittedAt = dateFormatter.format(submittedDateConvert)
You need to use SimpleDate formate method. You can specify which formate you want.
SimpleDateFormat formatter = new SimpleDateFormat("dd/mm/yy");
String dateSelected = formatter.format(new Date());
In case if you want to format the date in your way.
println(DateTimeFormatter.ofPattern("dd-MM-YYYY").format(java.time.LocalDate.now))
if someone needs the date in a format so that to be able to add to PostgreSQL here we go:
import java.util.Calendar
val calendar = Calendar.getInstance
val now = new Timestamp(calendar.getTime.getTime)
or in one line:
val now = new Timestamp(java.util.Calendar.getInstance.getTime.getTime)

DateTime format not formatting correctly

I have a datetime format in XML and I'm trying to unmarshall the values as follows:
2013-03-17T19:12:14Z -> 2013-03-17 19:12 +0100
I have used Joda's DateTime and a DateTimeAdapter class to override the unmarshalling. The datetime format is coming out weird, as follows:
{"iMillis":1363510800000,"iChronology":{"iBase":{"iBase":{"iBase":
{"iMinDaysInFirstWeek":4}},"iParam":{"iZone":{"iTransitions":
[-9223372036854775808,-3852662325000,-1691964000000,-1680472800000,
-1664143200000,-1650146 400000,-1633903200000,-1617487200000,
-1601848800000,- etc etc.
Can anyone help me format this date?
I was unable to figure out the answer so I've tried the following:
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm Z");
DateTime dateTime = new DateTime(v);
long dateTimeMiliSec = dateTime.getMillis();
Date date = new Date(dateTimeMiliSec);
return sd.format(date);
So 2013-03-17T09:00:00Z converts to 2013-03-17 09:00 +0000