Getting date/time in server side code for specific Timezone - java

On the server code I would like to get the "Day" but not of server date/time but of a specific timezone, GMT+8 specifically.
I have this code:
DateFormat formatter = new SimpleDateFormat("EEEE", Locale.ENGLISH);
String day = formatter.format(new Date()).toUpperCase();
availabilities.add(Availability.builder()
.day(day)
.from(LocalTime.now())
.to(LocalTime.now())
.build());
How do I get the "day" for the specific timezone and also have to build a LocalTime.now() which will return a LocalTime object but not the current time of the said timezone.
For instance as of this writing GMT+8 now is ~6:25 am so that would be the one that LocalTime.now() returns instead of the cloud server which is in the different timezone.

SDF and new Date() are old API and you don't want these. For example, Date is a lie; it does not represent a date whatsoever, it actually represents an instant in time. This is dumb - that's why there is a new API.
EDIT: Made it simpler by invoking the now method of ZonedDateTime.
private static final ZoneId TARGET_ZONE = ZoneId.of("Singapore");
ZonedDateTime atTarget = ZonedDateTime.now(TARGET_ZONE);
DayOfWeek whatYouWant = atTarget.getDayOfWeek();
NB: You can go with +8 explicitly, then you're looking for an OffsetDateTime, and atOffset(ZoneOffset.ofHours(8)), but that's... weird. Who could possibly want 'UTC+8'? Nobody, except airplanes and military operations in a certain zone, and surely that's not your target audience.

Related

Converting Date String to ZonedDateTime

I'm receiving a query parameter date, as yyyy-MM-DD (2022-03-08).
I want to conver this to java.util.Calendar / java.util.GregorianCalendar formmat.
So my idea is converto:
String -> ZonedDateTime -> Calendar.
What I did:
ZonedDateTime parsedDate = ZonedDateTime.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
//date = 2022-03-08
even with the correct format, I'm getting:
Text '2022-03-08' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor: {},ISO resolved to 2022-03-08 of type java.time.format.Parsed
I found out that this error occurs because my string does not have a TimeZone.
One suggestion was to use LocalDate
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date = LocalDate.parse(fecha, formatter);
but I can't use localDate as an argument for ZonedDateTime.parse().
What else could I try?
I want to conver this to java.util.Calendar / java.util.GregorianCalendar formmat.
That seems silly; Calendar/GregorianCalendar is obsolete, and the API is horrendous. Why use a broken screwdriver when there's a shiny new one right there in the toolbox? Don't do this.
So my idea is converto: String -> ZonedDateTime -> Calendar.
That seems silly. The string does not contain a ZonedDateTime. It doesn't even contain a LocalDateTime. It is clearly a LocalDate. So, convert it to a localdate, and you go from there.
The power of the java.time package is that each different concept in time has a matching type in the j.t package that is properly named. For example, java.util.Date is a lie: It is a timestamp, and it has nothing whatsoever to do with dates; asking a Date object for 'what year is it', is broken (try it, you get a warning).
Calendar, similarly, is an utter falsehood. It does not represent a calendar at all; it, too, represents a timestamp.
LocalDate on the other hand is perfect truth: It represents a date (not a time), and it does not include timezone or other localizing information: It makes sense only as 'locally'.
Each stop should just make sense, on its own:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd");
LocalDate date = LocalDate.parse("2022-10-01", formatter);
So far, so good. I'd just stop there - why lie? Why return a Calendar which is both API wise a lie (that class does not represent calendars), and even if someone knows exactly what Calendar is, it's still a lie: A calendar implies it has exact time and a timezone. You do not have a time, and also don't have a timezone. Why return something that suggests stuff that isn't there?
But, if you MUST, then explicitly add a timezone and a time, and THEN go for it:
ZonedDateTime zdt = someLocalDate.atStartOfDay().atZone(ZoneId.of("Europe/Amsterdam"));
GregorianCalendar gc = GregorianCalendar.from(zdt);
This code is clear and legible: It makes crystal clear that the code picks a time, and picks a zone.
But, again, now you ended up with a horrible, horrible object you should not be using, for anything.
There are other ways of getting a ZonedDateTime than just its static parse() method. Here's how to turn a LocalDateTime into a ZonedDateTime:
ZonedDateTime zoned = dateTime.atZone(ZoneId.of( "America/New_York"));
or if you have a LocalDate:
ZonedDateTime zoned =
date.atStartOfDay( ZoneId.of( "America/New_York" ));
I would not use java.util.Calendar or Date. They're junk. I'd either stick with LocalDate or use ZonedDateTime depending on if you need to keep track of time zones or not. This should get you where you want to go either way, I guess, as it sounds like you know what you want to do once you have a ZonedDateTime.
UPDATE: I looked up how to convert a ZoneDateTime to a Calendar:
Calendar calendar = GregorianCalendar.from(zoned);
just in case you hadn't gotten that far and really want to go that way.

How to parse offset it is not specified?

I have time 12:00:00 in format HH:mm:ss.
I know that this time comes from server witch is setup with +3 offset.
If i use SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");, it parses time with regard to device, which can be in a different timezone.
Is there another way to parse it with regard to +3 offset except adding it to the original string?
First, should your server rather send the time in UTC? If clients are everywhere, this would seem more time zone neutral and standardized. However, the way to handle it in code wouldn’t be much different. In any case the server offset form UTC could be constant:
private static final ZoneOffset serverOffset = ZoneOffset.ofHours(3);
In real code you will probably want to make it configurable somehow, though. To parse:
OffsetTime serverTime = LocalTime.parse("12:00:00").atOffset(serverOffset);
System.out.println(serverTime);
This prints
12:00+03:00
Since your time format agrees with LocalTime’s default (ISO 8601), we need no explicit formatter. If a representation of the time with offset is all you need, we’re done. If you need to convert to the user’s local time, to do that reliably you need to decide both a time zone and a date:
LocalTime clientTime = serverTime.atDate(LocalDate.of(2018, Month.JANUARY, 25))
.atZoneSameInstant(ZoneId.of("Indian/Maldives"))
.toLocalTime();
System.out.println(clientTime);
With the chosen day and zone we get
14:00
Please substitute your desired time zone and date.
Just hypothetically, if you knew the user’s offset from UTC, you could use just that:
LocalTime clientTime = serverTime.withOffsetSameInstant(ZoneOffset.of("-08:45"))
.toLocalTime();
The example yields 00:15. However, no one knows when the politicians introduce summer time (DST) or other anomalies in the user’s time zone, so I discourage relying on an offset alone.
And yes, I too am using java.time. SimpleDateFormat is not only long outdated, it is also notoriously troublesome, so java.time is what I warmly recommend.
Set the timezone on your SimpleDateFormat object:
SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("GMT+03:00"));
I recommend you use the Java 8 date and time API (package java.time) instead of the old API, of which SimpleDateFormat is a part.
Using the Java 8 DateTime API:
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("HH:mm:ss");
LocalTime clientLocalTime = LocalTime
.parse("12:00:00", formatter)
// Create an OffsetTime object set to the server's +3 offset zone
.atOffset(ZoneOffset.ofHours(3))
// Convert the time from the server timezone to the client's local timezone.
// This expects the time value to be from the same day,
// otherwise the local timezone offset may be incorrect.
.withOffsetSameInstant(ZoneId.systemDefault().getRules().getOffset(Instant.now()))
// Drop the timezone info - not necessary
.toLocalTime();

Joda DateTime doesn't display correct time for a specific timezone

In a chat application, the server gave me this info regarding on the date that the message was created
2015-05-04 09:56:27
DateTime instance gives me this 2015-05-04T09:56:27.000+08:00
What I wanted to display on the chat bubble is this format
hh:mm a
My code doesn't seem to display the hours and minutes I wanted, instead it displays the hours and minutes in UTC
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dt = formatter.parseDateTime(strDate);
String str = String.format("%s:%s %s",
dt.toString("hh"),
dt.toString("mm"),
dt.toString("a")
);
it displays like this 09:56 am instead of 5:56 pm. It seems like the +8 offset wasn't counted. I have tried the withOffsetParsed and it doesn't work too
DateTimeFormatter df = formatter.withOffsetParsed();
DateTime dt2 = df.parseDateTime(strDate);
Is there anything I missed?
If DateTime outputs 09:56:27.000+08:00, then that is your local time- and the UTC time is 1:56am.
However, if your expectation that this should output 5:56pm is correct, something else is wrong. The server is giving you a timestamp without a timezone specified, which is a bad API. Presumably this time should be interpreted as UTC?
Try adding .withZoneUTC() to the creation of the DateTimeFormatter. This will make it interpret the time fields of the string as UTC and should produce a DateTime of 09:56:27.000Z. You'll then need to convert this is a DateTime in your local timezone to display it, by calling .withZone(DateTimeZone.getDefault()) (or whatever means you have to get the appropriate timezone for the user, for example)
(You could be pedantic and call parseLocalDateTime to get a LocalDateTime since that is what the server is sending, but personally I'd just encapsulate the UTC rule into the formatter and pull a full DateTime straight out)

JAVA : Get the time of different timezone

I'm currently a developing a sync module for one of my applications. I'm syncing the records between local and server based on LastUpdated time, which is nothing but a timestamp. The server is of Singapore, so how can I set the timezone to Singapore in my Android application?
I have tried,
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("VST") );
and,
Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("Asia/Singapore"));
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
Still no luck.
Or is there any way I can set the common timezone for both local and server? Server side language is ASP.Net
Your SimpleDateFormat example is almost correct. I believe you want SGT for Singapore and you need to call DateFormat.format(Date). You might also pass the TimeZone to Calendar.getInstace(TimeZone) like
TimeZone tz = TimeZone.getTimeZone("SGT");
sdf.setTimeZone(tz);
System.out.println(sdf.format(Calendar.getInstance().getTime()));
System.out.println(Calendar.getInstance(tz));
Joda-Time
When doing anything significant with date-time work, you should avoid the java.util.Date and java.util.Calendar classes as they are notoriously troublesome, confusing, and flawed. Instead use the Joda-Time library which does work in Android.
ISO 8601
Your string input is close to standard ISO 8601 format. Just replace the SPACE with a T. Then pass the result to the constructor of DateTime along with a DateTimeZone object representing the intended time zone of that string. Search StackOveflow for hundreds of examples.
UTC
As Elliott Frisch commented, on the server-side you should be working and storing date-time values all in UTC. Convert to local zoned values only when expected by the user in the user interface.
The server itself should be set to UTC, or if not possible then set to Reykjavík Iceland. But your programming should never assume the server is so set. Your programming should always specify the desired time zone.
Search StackOverflow for hundreds of examples and discussion of these points.
Finally, this is how I solved it.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.ENGLISH);
TimeZone tz = TimeZone.getTimeZone("Asia/Singapore");
sdf.setTimeZone(tz);
java.util.Date date= new java.util.Date();
Timestamp local = new Timestamp(date.getTime());
String strDate = sdf.format(date);
System.out.println("Local in String format " + strDate);

Formatting a Calendar object and not a Date object?

I am trying to format a calendar string to indicate a time zone offset other than my local one. I am aware I could create a simple formatting string and use the Calendar.get(int) method to fill in all the values, but this does not feel like the right way to do this.
Java has a DateFormat, specifically I am trying to use the SimpleDateFormat. The problem is that the format() method of these classes expects a Date object.
I am primarily working with Calendar objects since I believe those are the recommended structure in Java. So, when I go to format my result time, I call Calendar.getTime() which returns a Date object which can be passed into the SimpleDateFormat object.
Until now, I thought this was perfectly simple, but here is where the problem comes in.
Whenever one calls the Calendar.getTime() method, the Date returned is always in the local time zone, regardless of the time zone of the Calendar.
So, I always get the time printed in the local time zone when I pass it to my SimpleDateFormat, which is not what I want. All the research I have done so far says it can't be done and all the examples I have seen simply use the Calendar.get(int) method to fill in some blanks. This seems terrible, why have a DateFormat class if it is going to be so useless?
Here is some example code so you can see what I mean, paste this into your favourite test class:
private static final SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
private static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
public static void main(String[] args)
{
try
{
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));
cal.setTimeInMillis(parser.parse("2012-10-09T22:01:49.108-0700").getTime());
System.out.println(formatter.format(cal.getTime()));
}
catch(ParseException e)
{
e.printStackTrace();
}
}
Output produced (because I am running in Central Time Zone): 2012-10-10T00:01:49-0500
Output expected (should not matter what time zone it is run from): 2012-10-10T01:01:49-0400
To summarize question: Is there a way to make the DateFormat in java accept a Calendar, or a way to get a Date that is not in the local timezone, or is there another class I should be using altogether for this formatting?
Whenever one calls the Calendar.getTime() method, the Date returned is always in the local time zone, regardless of the time zone of the Calendar.
No, it's not. Date doesn't have a time zone. It's just the number of milliseconds since the Unix epoch. Its toString() method does convert it to the local time zone, but that's not part of the information in the Date object.
All you need to do is set the time zone of the formatter to be the same as the time zone of the calendar.
formatter.setTimeZone(calendar.getTimeZone());
... or just set the calendar to be used entirely:
formatter.setCalendar(calendar);
(It's not immediately clear to me whether the latter approach will mean that the calendar can lose its value... basically the Java classes mix "calendar system", "time zone" and "value within the calendar" in a single type, which is very unfortunate.)
I agree with Ian though, in terms of Joda Time being a far more pleasant API to use.
I would give up on the built in java date and time classes for this and use joda time instead. It is designed to handle ISO8601 format strings properly and does the right thing with timezones.
Date is not having any TimeZone, its just the number of milliseconds since Epoch time, represented in a human readable format. We can use "DateFormat" class.
TimeZone tz = TimeZone.getTimeZone("America/Buenos_Aires");
Calendar cal = Calendar.getInstance(tz);
Date d = cal.getTime();
DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm aaa");
df.setTimeZone(tz);
String s = df.format(cal.getTime());
System.out.println(s);

Categories

Resources