Java 8 LocalDateTime has different results - date

I am trying to update some code to use Java 8's feature for parsing multiple date formats. my local time on my box is set to UTC-11.
the below code works when using the SimpleDateformat.
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
Date correctDate = dateFormat.parse("2018-09-6T03:28:59.039-04:00");
//Gives me correct date
System.println( correctDate);//Wed Sep 5th 20:28:59 GMT-11:00 2018
I am trying to update this code to give the same date as above with the DateTimeFormatter in Java 8 , so i can handle another date format..
DateTimeFormattter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]XXX");
LocalDateTime updateDate = LocalDateTime.parse( "2018-09-6T03:28:59.039-04:00", dtf);
//shows the wrong date of 2018-09-06 03:28:59.039.
System.out.println( updateDate.toString() );// 2018-09-06 03:28:59.039
[solved]
I was able to fix this by using ZonedDateTime.
ZonedDateTime zdt = ZonedDateTime.parse("2018-09-6T03:28:59.039-04:00");
zonedDateTime = zdt.withZoneSameInstance(ZoneId.of("GMT"));
Date correctDate = Date.from( zonedDateTime.toInstance());
//correctDate is what i wanted Wed Sep 5th 20:28:59 GMT-11:00 2018

As soon as you parse your date string into a LocalDateTime the zone offset is lost because LocalDateTime does not hold any time zone or offset information.
When you format the LocalDateTime to a string again, you'll only have the time as it was parsed without offset.
The Documentation of LocalDateTime clearly explains this:
This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, combined with the local time as seen on a wall clock. It cannot represent an instant on the time-line without additional information such as an offset or time-zone.
You should consider using OffsetDateTime or ZonedDateTime.

Solved, using OffsetDateTime as suggested in the accepted 'Answer':
OffsetDateTime odt = OffsetDateTime.parse("2018-09-6T03:28:59.039-04:00");
Date correctDate = Date.from( odt.toInstant());

Related

Convert the String Monday 5 October to date using Google Apps Script

I need to convert the date string of type WEEKDAY DATE MONTHNAME, Example: from "Monday 5 October" to date object.
I have tried with
Utilities.formatDate(new Date("Monday 5 October"), "GMT", "yyyy-MM-dd'T'HH:mm:ss'Z'")
How do I convert it, I am ok using V8 apps script engine
The Date constructor accepts timestamp strings formatted according to IETF-compliant RFC 2822 timestamps and ISO8601.
There are many ways to convert your string to date, but probably one of the simplest is appending the current year to your string, using getFullYear():
const source = "Monday 5 October";
const date = new Date(`${source} ${new Date().getFullYear()}`);
Reference:
Date() constructor
IETF-compliant RFC 2822 timestamps

How to get zone name that respects daylight saving (e.g. EDT)

I have ZonedDateTime instance, trying to get zone as string (e.g. EST/EDT) like this:
merchantLocalReceiptDateTime.getZone().getDisplayName(TextStyle.SHORT, Locale.getDefault())
For my setup, it returns me EST while in fact I was expecting EDT. Pls advise how to get zone string that correctly reflects daylight saving.
static DateTimeFormatter etFormat = DateTimeFormatter.ofPattern("MM/dd/yyyy 'at' hh:mma 'ET'");
static ZoneId istZoneId = ZoneId.of("Asia/Kolkata");
static ZoneId etZoneId = ZoneId.of("America/New_York");
LocalDateTime currentDateTime = LocalDateTime.now();
ZonedDateTime currentISTime = currentDateTime.atZone(istZoneId);
ZonedDateTime currentETime = currentISTime.withZoneSameInstant(etZoneId); //ET Time
System.out.println(etFormat.format(currentETime));
Ok, I do not like this solution but it is the only one I came up with so far, the only one that works:
So we have preinitialized ZonedDateTime merchantLocalReceiptDateTime (remember, this is threetenbp). We can do this then (is it daylight saving for given time?):
boolean isDaylightSaving = merchantLocalReceiptDateTime.getZone()
.getRules().isDaylightSavings(merchantLocalReceiptDateTime.toInstant());
Then, to get short representation of timezone that respects daylight saving, we can do this (TimeZone is not part of threeten, it is java.util):
TimeZone.getTimeZone(merchantLocalReceiptDateTime.getZone().getId())
.getDisplayName(isDaylightSaving, TimeZone.SHORT)
For New York (assuming device language is English/United States), the above produces EST in winter and EDT right now. For timezones without particular daylight saving names, it can give, for example, "GMT+2", "GMT+3" etc. If language is different, you will likely get those "GMT+-".
//SHORT: CEST
DateTimeFormatter.ofPattern("zzz").format(zonedDateTime)
//SHORT: CET
ZoneId.getDisplayName(TextStyle.SHORT,Locale.ENGLISH)
//LONG: Central European Summer Time
DateTimeFormatter.ofPattern("zzzz").format(zonedDateTime)
//LONG: Central European Time
ZoneId.getDisplayName(TextStyle.LONG,Locale.ENGLISH)
//Use this for converting CET to CEST and vice versa
TimeZone tz = TimeZone.getTimeZone(timeZone.getZone());
tz.getDisplayName(true, TimeZone.SHORT, Locale.ENGLISH));

Using Groovy - Add text to a returned date based on the time

I am running this groovy script:
def sdf = new java.text.SimpleDateFormat("yyyy-MM-dd kk:mm:ss")
return sdf.format( new Date())
and it returns = 2016-03-23 16:54:39
Which is fine however at the end of the returned value I want to add a space and either AM or PM depending on the what time it is (AM or PM time) as per the below:
AM time returns = 2016-03-23 11:54:39 AM
PM time returns = 2016-03-23 16:54:39 PM
Any ideas?
If you want to append AM / PM you can append an 'a' (Am/pm marker) at the end of the date formatter pattern:
new java.text.SimpleDateFormat("yyyy-MM-dd kk:mm:ss a").format(new Date())
This will yield:
2016-03-23 18:05:20 PM
See the Java documentation for the exact details of SimpleDateFormat.
It's even easier with groovy, as Date had a format method;
println new Date().format('yyyy-MM-dd HH:mm:ss a')
java.time
The Date and SimpleTextFormat classes are now outmoded by the java.time framework built into Java 8 and later.
An Instant is a moment on the timeline in UTC with up to nanosecond resolution.
The code in this Answer is Java, as I do not know Groovy syntax.
Instant instant = Instant.now();
Apply a time zone to get a ZonedDateTime.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Define your formatting pattern. These codes in DateTimeFormatter are similar to SimpleDateFormat but not exactly the same. Study the class doc.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern ( "yyyy-MM-dd kk:mm:ss" );
Be aware that the kk used in that particular pattern means 1-24 used for hours of the day instead of 0-23 (hh).
Generate a String as a textual representation of your date-time object.
String output = zdt.format( formatter );

How to create new Date in Groovy at specific date and time

I wonder if there is other way how to create new Date in Groovy at specific date and time than parse it from String with Date.parse method. Can I get complete list of Date creation in Groovy?
You can use the existing Java methods to create a date:
// takes the date encoded as milliseconds since midnight, January 1, 1970 UTC
def mydate = new Date(System.currentTimeMillis())
// create from an existing Calendar object
def mydate = new GregorianCalendar(2014, Calendar.APRIL, 3, 1, 23, 45).time
Groovy also provides some streamlined extensions for creating Dates. Date.parse() and Date.parseToStringDate() parse it from a String. The Date.copyWith() method builds a date from a map. You can use them like this:
// uses the format strings from Java's SimpleDateFormat
def mydate = Date.parse("yyyy-MM-dd hh:mm:ss", "2014-04-03 1:23:45")
// uses a format equivalent to EEE MMM dd HH:mm:ss zzz yyyy
def mydate = Date.parseToStringDate("Thu Apr 03 01:23:45 UTC 2014")
def mydate = new Date().copyWith(
year: 2014,
month: Calendar.APRIL,
dayOfMonth: 3,
hourOfDay: 1,
minute: 23,
second: 45)
The other answers are outdated as of Java 8 and later. The old legacy date-time classes such as java.util.Date/.Calendar/.GregorianCalendar have proven to be poorly designed, confusing, and troublesome.
java.time
The java.time framework supplants those old classes.
Groovy can use these classes of course. I do not know the Groovy syntax, but it should be easy to adapt this simple Java example.
LocalDate/LocalTime
The LocalDate and LocalTime classes have no concept of time zone. So they do not represent actual moments on the timeline.
LocalDate localDate = LocalDate( 2016 , Month.JANUARY , 2 ); // year, month, date.
LocalTime localTime = LocalTime( 12 , 30 , 0 , 0 ); // hour, minute, second, nanosecond.
ZonedDateTime
Apply a time zone (ZoneId) to get a ZonedDateTime. Use a proper time zone name, never the 3-4 zone abbreviations commonly seen.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.of( localDate , localTime , zoneId );
Instant
You should avoid the old date-time classes. But if required, you can convert. An Instant is the java.time class to represent a moment on the timeline in UTC with a resolution of nanoseconds. Look for new methods on the old java.util.Date class for converting to/from an Instant. We can extract the needed Instant object from our ZonedDateTime.
Nanosecond vs Millisecond
Note that converting from java.time involves data loss! The java.time classes support nanosecond resolution (up to 9 digits of decimal place on fractional second) while the old java.util.Date supports only milliseconds (up to 3 decimal places). Any 4th to 9th digit of fractional second is truncated. For example, 2016-04-28T02:05:05.123456789 is truncated to 2016-04-28T02:05:05.123.
Instant instant = zdt.toInstant();
java.util.Date utilDate = java.util.Date.from( instant );
Going the other direction, from java.util.Date to Instant. The 4th to 9th decimal place of fractional second will be zeros, the 1st-3rd for any milliseconds.
Instant instant = utilDate.toInstant();
Groovy is good.
new Date(2016-1900, 7, 16, 20, 32, 25)
Tue Aug 16 20:32:25 PDT 2016
Note that the year must be massaged, but you can create a new Java Date representing any second you want.
You can see more Groovy Goodness at http://mrhaki.blogspot.com/2009/08/groovy-goodness-working-with-dates.html
As far as I know there's no other way. You can also pass exact time in millis to constructor of Date class but first You need to get the time in millis.
Maybe this link will be helpful.
Or this code snippet:
def calendar = new GregorianCalendar(2011,1,7,15,7,23)
def date = calendar.getTime()
println date
You can get current date using Date() and format it the way you want using:
/*
y: year
m: month
d: day
h: hour
m: minute
*/
Date().format('yyyy-MM-dd hh-mm')
Date().format('yy/mm/dd hh')
The return type is a string.
LocalDateTime.now().format('yyyy-MM-dd hh-mm')

How to set client specific timezone and date

pardon me if it seems to be a duplicate question.
I have seen many posts already on this topic. However after trying many examples could not find the solution to my problem.
I tried this code
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd yyyy", Locale.ENGLISH )
Date newDate = sdf.parse(sdf.format( new Date( dateTimeString ) ) )
However the second line of code always converts the date to the server specific date and timezone which i don't want. I also tried the following
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss zzz", Locale.ENGLISH )
log.info "+++++++++++++++++hidden date ++++++++ " + params.hiddenGameDateTime.substring(35, 38)
log.info "x = " + sdf.format( new Date ( params.hiddenGameDateTime ))
String tzone = params.hiddenGameDateTime.substring(35, 38)
sdf.setTimeZone( TimeZone.getTimeZone( tzone ) )
log.info "Timezone = " + sdf.getTimeZone().getDisplayName()
Please note that
sdf.format( new Date( dateTimeString ) )
gives me the desired result, however it gives me the string value of the date and the actual value to be stored in database is of the Data type date which can't hold the string value.
the value for date and time in my case gets converted to PST date and time. how can i avoid this. The user input date with timezone should be stored in the database as it is with no change in date and timezone.
An observation: The constructor new Date( dateTimeString ) is deprecated. A better replacement would be something like that:
SimpleDateFormat sdfOriginal = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
Date d = sdfOriginal.parse(dateTimeString);
Furthermore: An expression like sdf.parse(sdf.format(...)) using the same format object does not make much sense.
But most important, your statement "the second line of code always converts the date to the server specific date and timezone" seems to be based on test output like:
System.out.println(newDate);
This implicitly uses toString() which is based on jvm default time zone, in your case the server time zone. But keep in mind, the internal state of j.u.Date does not reference any time zone. A Date is just a container for a long, namely the seconds since 1970-01-01T00:00:00Z in UTC time zone, that is a global time.
Additional remark:
If you need the client time zone (in a scenario with multiple users in different time zones) to create user-specific formatted date strings, then you indeed need to store the time zone preference of every user in the database, so you can use this information for output in an expression like:
SimpleDateFormat sdf = new SimpleDateFormat("{pattern}";
sdf.setTimeZone(TimeZone.getTimeZone("{user-preference-time-zone}");
String userOutput = sdf.format(date);
Date is always jvm timezone specific. You need to normalize it to standard time and store it in DB to cater with different timezone servers.