Using DateUtils.parseDate method, want to specify a specific TimeZone, but can't - apache-commons-lang3

When I use the DateUtils.parseDate method, I need to specify a special time zone, such as ZoneId.of("America/New_York"), to parse the special string into a Date object. Because I like this framework very much, and I hope it becomes more usable. I hope the project team can make the following adjustments:
The DateUtils.parseDateWithLeniency method can open TimeZone and use TimeZone as a method parameter.
Otherwise, I can only use the SimpleDateFormat class in jdk8 to solve my work problem.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone(DatePatternConstants.NEW_YORK_ZONE_ID));
try {
long l = sdf.parse(value).getTime() / 1000;
} catch (ParseException e) {
throw new RuntimeException(e);
}

Related

Manipulating a mocked Calendar object to return specific days

I'm using a Calendar object to determine whether or not to increase the workload of a system based on current day/hour values. Given that this object uses static methods, I'm using PowerMock to mock the static methods with the following annotations:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Calendar.class })
The code under test is pretty simple (though my logic needs work, I know):
public void determineDefaultMaximumScans() throws ParseException{
parseTime();
Calendar cal = Calendar.getInstance();
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
System.out.println(cal.get(Calendar.DAY_OF_WEEK));
if(dayOfWeek == (Calendar.SATURDAY) || dayOfWeek == (Calendar.SUNDAY)){
setDefaultMax(calculateNewDefaultMax(getDefaultMax()));
System.out.println("defaultMax increased by 20%");
} else {
if(currentTime.after(afterHoursBegin) && currentTime.before(afterHoursEnd)){
System.out.println("Not afterhours. Maintaining current maximum.");
setDefaultMax(defaultMax);
System.out.println("Current Maximum number of scans: " + getDefaultMax());
}
}
}
My test case reads as follows:
#SuppressWarnings("static-access")
#Test
public void testDetermineMaximumScans() throws ParseException{
PowerMock.mockStatic(Calendar.class);
String beginningTime = "18:00";
String endingTime = "05:00";
mockAfterHoursBegin = parser.parse(beginningTime);
mockAfterHoursEnd = parser.parse(endingTime);
mockCurrentTime = parser.parse(parser.format(new Date()));
EasyMock.expect(Calendar.getInstance()).andReturn(mockCalendar);
EasyMock.expect(mockCalendar.get(Calendar.DAY_OF_WEEK)).andReturn(6);
EasyMock.replay(mocks);
offHourMaximumCalculator.determineDefaultMaximumScans();
EasyMock.verify(mocks);
}
As of now, all of my attempts to return a specific value result in the following assertion error. Now I vaguely understand why it's returning the default but I do not see why I can't force the value or how to get around this expectation. Mocks in general are still a frustrating mystery to me. What am I missing?
java.lang.AssertionError:
Expectation failure on verify:
Calendar.get(7): expected: 1, actual: 0
Mocks are fairly simple. But wanting to mock static methods is a big running after complexity. I generally do not recommend to mock something like a Calendar. If you do weird and complex thing with it, just encapsulate in something you can test and mock easily.
And in fact, we pretty much never use Calendar.getInstance(). It returns something according to the locale. But it's rare that you don't want a specific calendar i.e. GregorianCalendar. So just do new GregorianCalendar.
But anyway, add a protected method doing
protected Calendar newCalendar() {
return Calendar.getInstance(); // or new GregorianCalendar()
}
will take 2 minutes and then a simple partial mock will do the trick.
Finally, I also don't recommend to use Calendar. You have a much nicer API in java.util.date in Java 8.
All this said, here is how you should do it. Calendar is a system class, so you need to follow a real specific path which is explained here.
#RunWith(PowerMockRunner.class)
#PrepareForTest(Calendar.class)
public class MyTest {
#Test
public void testDetermineMaximumScans() throws ParseException {
PowerMock.mockStatic(Calendar.class);
Calendar calendar = mock(Calendar.class);
EasyMock.expect(Calendar.getInstance()).andReturn(calendar);
EasyMock.expect(calendar.get(Calendar.DAY_OF_WEEK)).andReturn(6);
// really important to replayAll to replay the static expectation
PowerMock.replayAll(calendar);
assertThat(Calendar.getInstance().get(Calendar.DAY_OF_WEEK)).isEqualTo(6);
// and verifyAll is you want to verify that the static call actually happened
PowerMock.verifyAll();
}
}

ServiceStack.Text CSV serialization of IEnumerable<object> ignores custom serialization functions

Firstly, please forgive any rookie mistakes here - I'm not a regular poster I'm afraid.
Now on to the nitty gritty...
I am trying to use ServiceStack.Text to serialize objects to CSV. If I keep it simple, everything works as expected when serializing objects of a known type.
However I want to serialize many objects and I don't know the type at runtime so I am writing a reusable component where all data is treated as a System.Object. We already do this same routine for Json serialization without problems. But CsvSerializer appears to handle objects differently during serialization.
Sample code
public void TestIEnumerableObjectSerialization()
{
var data = GenerateSampleData();
JsConfig<DateTime>.SerializeFn =
time => new DateTime(time.Ticks, DateTimeKind.Utc).ToString("yyyy-MM-dd HH:mm:ss");
var csv = CsvSerializer.SerializeToCsv(data);
Console.WriteLine(csv);
Assert.Equal("DateTime\r\n"
+ "2017-06-14 00:00:00\r\n"
+ "2017-01-31 01:23:45\r\n",
csv);
}
object[] GenerateSampleData()
{
return new object[] {
new POCO
{
DateTime = new DateTime(2017,6,14)
},
new POCO
{
DateTime = new DateTime(2017,1,31, 01, 23, 45)
}
};
}
public class POCO
{
public DateTime DateTime { get; set; }
}
The result of this code is that the custom serialization function is not invoked, and the DateTime is written out using the standard ToString() method.
The cause?
The CsvWriter.Write method is inspecting the type of the records and if the type is Object it is treated as a Dictionary<string, object> and CsvDictionaryWriter generates the output.
In turn, CsvDictionaryWriter uses the ToCsvField() extension method to write each property a record.
The problem is that ToCsvField() converts the value of each property to a string using ToString() meaning no custom serialization is performed.
JsonSerializer uses TypeSerializer.SerializeToString(text) to serialize the properties of an Object using any configured custom serialization functions; but this doesn't happen with CsvSerializer.
A possible solution?
Without complicating CsvSerializer, the ToCsvField() extension method could be updated to use TypeSerializer to handle the serialization to a string. Here is what I've been testing with so far:
public static object ToCsvField(this object text)
{
var textSerialized = TypeSerializer.SerializeToString(text).StripQuotes();
return textSerialized == null || !CsvWriter.HasAnyEscapeChars(textSerialized)
? textSerialized
: string.Concat
(
CsvConfig.ItemDelimiterString,
textSerialized.Replace(CsvConfig.ItemDelimiterString, CsvConfig.EscapedItemDelimiterString),
CsvConfig.ItemDelimiterString
);
}
So far I haven't come across an issue with this change, although someone may prefer not to allocate a new intermediate variable before the return statement.
Hopefully that is enough information, so on to my questions...
Has anyone else experienced this issue?
Am I doing something wrong and should I be serializing Objects a different way?
If this is a suitable fix/implementation of TypeSerializer, what are the chances of this being addressed in an update to ServiceStack.Text? I would raise an issue on GitHub but the ServiceStack.Text repo doesn't let me raise issues.
Thanks in advance.

The time period format should be in format dd,MMM,yyyy,hh,mm

Hi I am sending JSON data and the JSON will have to validated at the backend. The time period should be in this format--- dd,MMM,yyyy,hh,mm.
This is my JSON
{
"equipmentID":"234",
"modality":"healthcaare",
"facilityID":"manipal",
"countryCode":"abc",
"isoCode":"1234",
"problemType":"234",
"problemArea":"priyanka",
"equipmentStatus":"sdsd",
"name":"taneja",
"phoneNumber":"13333344",
"extension":"12123",
"description":"x ray machine error",
"shortDescription":"2",
"timePeriod":"03-12-2011 04-37",
"serviceCode":"sdfdf",
"locale":"werfd",
"requestingApp":"icenter",
"examNumber":"sdd",
"seriesNumber":"dfdf",
"imageNumber":"dfdfd"
}
This is the validation class
public class RequestValidator implements Validator {
#Override
public ValidationResult validate(String objectName, RequestData rqdata) {
// TODO Auto-generated method stub
ValidationResult result = new ValidationResult();
if (rqdata == null) {
result.addError("error.invalidObjectGraph", "Object graph not initialized correctly");
return result;
}
Validation.rule("EquipmentId", rqdata.getEquipmentID()).required().run(result);
Validation.rule("Modality", rqdata.getModality()).required().run(result);
Validation.rule("FacilityID", rqdata.getFacilityID()).required().run(result);
Validation.rule("CountryCode", rqdata.getCountryCode()).required().maxLength(3).matches("^[a-zA-Z]*$")
.run(result);
Validation.rule("ProblemType", rqdata.getProblemType()).required().run(result);
Validation.rule("Name", rqdata.getName()).required().maxLength(20).run(result);
Validation.rule("PhoneNumber", rqdata.getPhoneNumber()).required().maxLength(25).matches("[0-9]+").run(result);
Validation.rule("Extension", rqdata.getExtension()).required().maxLength(10).matches("[0-9]+").run(result);
Validation.rule("Description", rqdata.getDescription()).required().maxLength(300).run(result);
Validation.rule("ShortDescription", rqdata.getShortDescription()).required().maxLength(80).run(result);
Validation.rule("TimePeriod", rqdata.getTimePeriod()).required().matches("dd-MMM-yyyy hh-mm").run(result);
Validation.rule("Locale", rqdata.getLocale()).required().run(result);
System.out.println("value of requesting app is:" + rqdata.getRequestingApp());
Validation.rule("RequestingApp", rqdata.getRequestingApp()).required().matches("icenter").run(result);
System.out.println(result.getErrorDetails());
return result;
}
}
But i am getting an error that the date is not in appropriate format. Please help me out. Thanks
Matches would require a regular expression inside it.
Try this
^(([0-9])|([0-2][0-9])|([3][0-1]))\-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\-\d{4}$
This would fit in to check against the MMM for the month
Time one of the parameters which have different ways. Please consider epoch format of data exchange for dates. This will be a better choice.
[EDIT]
Change this
Validation.rule("TimePeriod", rqdata.getTimePeriod()).required().matches("dd-MMM-yyyy hh-mm").run(result);
To
String regEx = "^(([0-9])|([0-2][0-9])|([3][0-1]))\-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\-\d{4}$";
Validation.rule("TimePeriod", rqdata.getTimePeriod()).required().matches(regEx).run(result);

save encoded String in hidden vs session

Given:
I have a List<ComplexObjectThatContainsOtherObjectsAndEvenLists> and I want to retain this data across pages/requests. This objects is quite large containing up to 1000 objects.
Current implementation:
What I am currently doing is serializing this complex object using below (I just found this code here in SO and I am grateful to the author who unfortunately I cannot recall, I am sorry)
public static String serialize(Object object) {
ByteArrayOutputStream byteaOut = new ByteArrayOutputStream();
GZIPOutputStream gzipOut = null;
try {
gzipOut = new GZIPOutputStream(new Base64OutputStream(byteaOut));
gzipOut.write(new Gson().toJson(object).getBytes("UTF-8"));
} catch(Exception e) {
return null;
} finally {
if (gzipOut != null) try { gzipOut.close(); } catch (IOException logOrIgnore) {}
}
return new String(byteaOut.toByteArray());
}
and hiding the String output <input type="hidden"> in my page and passing at it back to my controller whenever I need it back. This string is around 1300-2000 characters in length.
Question:
Is saving this String in session better? (see below)
session.setAttribute("mySerializedString", mySerializedString);
Can you please provide pros and cons?
My pros and cons so far (I am not sure though):
I'm not sure but hidden implementation I think can have an effect while the page is rendered (since it's too long) and when it's submitted back to the controller, although this doesn't trouble me of manually unsetting the session variable if I choose the session implementation.

Set Jackson Timezone for Date deserialization

I'm using Jackson (via Spring MVC Annotations) to deserialize a field into a java.util.Date from JSON. The POST looks like - {"enrollDate":"2011-09-28T00:00:00.000Z"}, but when the Object is created by Spring & Jackson it sets the date as "2011-09-27 20:00:00".
How can I set the proper timezone in Jackson?
Or if that is not the problem, how do I send EST from the JSON message?
Javascript/jQuery:
var personDataView = { enrollDate : new Date($("#enrollDate").val()),
//...other members
};
$.postJSON('/some/path/', personDataView, function(data){
//... handle the response here
});
JSON Message:
{"enrollDate":"2011-09-28T00:00:00.000Z"}
Spring Controller:
#RequestMapping(value="/", method=RequestMethod.POST)
public #ResponseBody String saveProfile(#RequestBody personDataView persondataView, HttpServletRequest request)
{
//...dataView has a java.util.Date enrollDate field
//...other code
}
In Jackson 2+, you can also use the #JsonFormat annotation:
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone="America/Phoenix")
private Date date;
If it doesn't work this way then try wrapping Z with single quotes, i.e. pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
Have you tried this in your application.properties?
spring.jackson.time-zone= # Time zone used when formatting dates. For instance `America/Los_Angeles`
If you really want Jackson to return a date with another time zone than UTC (and I myself have several good arguments for that, especially when some clients just don't get the timezone part) then I usually do:
ObjectMapper mapper = new ObjectMapper();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
dateFormat.setTimeZone(TimeZone.getTimeZone("CET"));
mapper.getSerializationConfig().setDateFormat(dateFormat);
// ... etc
It has no adverse effects on those that understand the timezone-p
I am using Jackson 1.9.7 and I found that doing the following does not solve my serialization/deserialization timezone issue:
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZ");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
objectMapper.setDateFormat(dateFormat);
Instead of "2014-02-13T20:09:09.859Z" I get "2014-02-13T08:09:09.859+0000" in the JSON message which is obviously incorrect. I don't have time to step through the Jackson library source code to figure out why this occurs, however I found that if I just specify the Jackson provided ISO8601DateFormat class to the ObjectMapper.setDateFormat method the date is correct.
Except this doesn't put the milliseconds in the format which is what I want so I sub-classed the ISO8601DateFormat class and overrode the format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition)
method.
/**
* Provides a ISO8601 date format implementation that includes milliseconds
*
*/
public class ISO8601DateFormatWithMillis extends ISO8601DateFormat {
/**
* For serialization
*/
private static final long serialVersionUID = 2672976499021731672L;
#Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition)
{
String value = ISO8601Utils.format(date, true);
toAppendTo.append(value);
return toAppendTo;
}
}
Looks like older answers were fine for older Jackson versions, but since objectMapper has method setTimeZone(tz), setting time zone on a dateFormat is totally ignored.
How to properly setup timeZone to the ObjectMapper in Jackson version 2.11.0:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setTimeZone(TimeZone.getTimeZone("Europe/Warsaw"));
Full example
#Test
void test() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);
objectMapper.setTimeZone(TimeZone.getTimeZone("Europe/Warsaw"));
ZonedDateTime now = ZonedDateTime.now();
String converted = objectMapper.writeValueAsString(now);
ZonedDateTime restored = objectMapper.readValue(converted, ZonedDateTime.class);
System.out.println("serialized: " + now);
System.out.println("converted: " + converted);
System.out.println("restored: " + restored);
Assertions.assertThat(now).isEqualTo(restored);
}
`
Just came into this issue and finally realised that LocalDateTime doesn't have any timezone information. If you received a date string with timezone information, you need to use this as the type:
ZonedDateTime
Check this link
Your date object is probably ok, since you sent your date encoded in ISO format with GMT timezone and you are in EST when you print your date.
Note that Date objects perform timezone translation at the moment they are printed. You can check if your date object is correct with:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTime(date);
System.out.println (cal);
I had same problem with Calendar deserialization, solved extending CalendarDeserializer.
It forces UTC Timezone
I paste the code if someone need it:
#JacksonStdImpl
public class UtcCalendarDeserializer extends CalendarDeserializer {
TimeZone TZ_UTC = TimeZone.getTimeZone("UTC");
#Override
public Calendar deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
JsonToken t = jp.getCurrentToken();
if (t == JsonToken.VALUE_NUMBER_INT) {
Calendar cal = Calendar.getInstance(TZ_UTC);
cal.clear();
cal.setTimeInMillis(jp.getLongValue());
return cal;
}
return super.deserialize(jp, ctxt);
}
}
in JSON model class just annotate the field with:
#JsonDeserialize(using = UtcCalendarDeserializer.class)
private Calendar myCalendar;
For anyone struggling with this problem in the now (Feb 2020), the following Medium post was crucial to overcoming it for us.
https://medium.com/#ttulka/spring-http-message-converters-customizing-770814eb2b55
In our case, the app uses #EnableWebMvc and would break if removed so, the section on 'The Life without Spring Boot' was critical. Here's what ended up solving this for us. It allows us to still consume and produce JSON and XML as well as format our datetime during serialization to suit the app's needs.
#Configuration
#ComponentScan("com.company.branch")
#EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0, new MappingJackson2XmlHttpMessageConverter(
new Jackson2ObjectMapperBuilder()
.defaultUseWrapper(false)
.createXmlMapper(true)
.simpleDateFormat("yyyy-mm-dd'T'HH:mm:ss'Z'")
.build()
));
converters.add(1, new MappingJackson2HttpMessageConverter(
new Jackson2ObjectMapperBuilder()
.build()
));
}
}