SimpleDateFormat from UTC Calendar Error when Locale is Arabic - android

I am attempting to convert a timestamp into a date, which I am then putting into the query parameters of a request.
Here is the function that converts a timestamp into a date:
override fun fromTimestamp(timestamp: Long): String {
val tz = TimeZone.getTimeZone("UTC")
val calendar = Calendar.getInstance(tz)
calendar.timeInMillis = timestamp
// Quoted "Z" to indicate UTC, no timezone offset
val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
df.timeZone = tz
return df.format(calendar.time)
}
This seems to work perfectly fine when my device is set in English, after URL encoding the date, this is what appears in the parameters: 1970-01-01T00%3A00%3A00.000Z
Now, if my device is set in Arabic, the URL encoded date looks like this: %D9%A2%D9%A0%D9%A1%D9%A8-%D9%A1%D9%A0-%D9%A1%D9%A7T%D9%A1%D9%A9%3A%D9%A4%D9%A7%3A%D9%A1%D9%A9.%D9%A0%D9%A8%D9%A2Z
I can only assume that all of those characters, that are suppose to be integers, that are encoded are Arabic characters depicting a date
After some googling, I found this discussion which says to initialize the Calendar with a Locale from the US. But looking at the code above, you notice the calendar is being initialized with a UTC timezone.
I would think that a calendar instanced from a UTC timezone would kick out integers instead of a specific language's characters depicting integers, but I'm not entirely sure how the Android system works with that.
Any idea on how I can modify my function to represent calendar dates with integers no matter what the Locale is?

My bet goes for
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US)

Related

Which type of date format it is?? "2018-09-06T10:12:21-0300"

What type of date format is it?
"2018-09-06T10:12:21-0300"
And how can I format it to something like that "06 Sep" ???
What type of date format is it?
This format is one of the ISO 8601 standard, but obviously not for the java.time.format.DateTimeFormatter which considers it a custom format consisting of ISO-standard date and time of day plus an offset from UTC without a separator (colon) between hours and minutes.
And how can I format it to something like that "06 Sep" ???
You need to define two DateTimeFormatters, one for parsing the non-standard input and the other one for outputting day of month and abbreviated month name only. Here's an example:
fun main(args: Array<String>) {
// some non-ISO formatted String
val inputDateTime = "2018-09-06T10:12:21-0300"
// build up a DateTimeFormatter that can parse such a String
val inputParser = DateTimeFormatterBuilder()
// date part uuuu-MM-dd
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T') // the T separating date from time
// the time of day part
.append(DateTimeFormatter.ISO_LOCAL_TIME)
// the offset part without a separator between hours and minutes
.appendPattern("X")
// (just for completeness) a locale
.toFormatter(Locale.ENGLISH)
// parse the String to an OffsetDateTime
val offsetDateTime = OffsetDateTime.parse(inputDateTime, inputParser)
// define another formatter for output, make it only use day of month and abbreviated month in English
val outputFormatter = DateTimeFormatter.ofPattern("dd MMM", Locale.ENGLISH)
// print the results
println("$offsetDateTime ---> ${offsetDateTime.format(outputFormatter)}")
}
Example output:
2018-09-06T10:12:21-03:00 ---> 06 Sep
This format respects the ISO 8601 standard for representing a date/time with timezone information.
There is enough information to parse this string into an OffsetDateTime, but apparently Java's formatter is a bit strict with respect to the missing separator between hours and minutes in the offset representation (which is technically allowed by the standard). This means a plain OffsetDateTime.parse(text) will throw an exception.
Therefore, you'll to define a custom DateTimeFormatter as explained by #deHaar.

Conversion of date and time to unix timestamp - kotlin

In timestamp variable, I want to get the timestampt value with the current hour, minute and second. The currentDataTime gives me the time in this format: 2020-08-28 17:18:02.
Currently, the timestamp variable returns me 1598645882634 (the last 3 numbers are the miliseconds) but when I convert it in a online conversor to a Human readable format, it gives me 08/28/2020 # 8:18pm (UTC). The only one problem is the hour and minute tha is 3 hours different because of my zone. How can I convert the date AND time to timestamp?
object DateTime {
val currentDataTime: String
#SuppressLint("SimpleDateFormat")
get() {
val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
return dateFormat.format(Date())
}
val timestamp: String
get(){
val formatter: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date = formatter.parse(currentDataTime) as Date
return date.time.toString().dropLast(3) //it is returning
}
}
A Unix timestamp is defined to be (almost) UTC. It carries no timezone information so it cannot be shifted according to a timezone difference without everything based on it falling apart. (If you'd like to hardwire it anyway, according to your example just add your timezone difference in milliseconds. But read on first.)
Localized time can only be interpreted consistently as long as the proper timezone is attached. It jumps back and forth whenever daylight-savings time starts or ends. If that's not complicated enough, the rules for daylight-savings time may change at any time (and do so around the globe).
Your online converter apparently just took a UTC-based timestamp and displayed it according to your local timezone.
To handle localized date and time values, use the multiplatform date/time library kotlinx-datetime. In the README section Converting an instant to local date and time components you'll find this example:
val currentMoment: Instant = Clock.System.now()
val datetimeInUtc: LocalDateTime = currentMoment.toLocalDateTime(TimeZone.UTC)
val datetimeInSystemZone: LocalDateTime = currentMoment.toLocalDateTime(TimeZone.currentSystemDefault())
There you'll also find elaborate explanations on which type of date and time to use in which scenario.

Getting 4 hours difference while getting while converting data in to device timezone?

I am getting the 4 hours difference on time zone from below lines of code on my device:
I am getting the time in such a way like 2018-09-30T13:45:00Z
My start and End Date is as follow: -
"start_date":"2017-09-13T12:15:00Z",
"end_date":"2018-09-30T13:45:00Z",
Calendar c = Calendar.getInstance(Locale.getDefault());
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
Date localStartDate = formatter.parse(startTime);
Date localEndDate = formatter.parse(endTime);
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm");
dateFormatter.setTimeZone(c.getTimeZone());
localStartDate = dateFormatter.parse(startTime);
localEndDate = dateFormatter.parse(endTime);
SimpleDateFormat monthFormat = new SimpleDateFormat("MMM");
monthFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));
String monthName = monthFormat.format(localStartDate);
eventDate.setMonth(monthName);
SimpleDateFormat dateFormat = new SimpleDateFormat("dd");
dateFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));
String dateName = dateFormat.format(localStartDate);
eventDate.setDate(dateName);
SimpleDateFormat dayNameFormat = new SimpleDateFormat("EEEE");
dayNameFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));
String dayName1 = dayNameFormat.format(localStartDate);
String dayName2 = dayNameFormat.format(localEndDate);
eventDate.setDayName1(dayName1);
eventDate.setDayName2(dayName2);
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm a");
timeFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));
String startTimeName = timeFormat.format(localStartDate);
String endTimeName = timeFormat.format(localEndDate);
System.out.println("My Start date and end date==>>>"+startTimeName+" " +endTimeName );
Problem: Getting the 4 hours difference from above code, as I am setting my time zone to BOSTON(US), getting error.
My result from the below #Hugo solution is as below
And i am expecting the result as below
Please check it once..I have also set the TimeZone of Eastern DayLight Time but not getting proper solution..please check it once..And let me know
SimpleDateFormat and Calendar uses the JVM default timezone (unless you set a different one on them), and the default timezone can be different in each device/machine/environment. Not only that, this default can be changed without notice, even at runtime, so it's better to always make it explicit which one you're using.
When you do things like:
Calendar c = Calendar.getInstance(Locale.getDefault());
dateFormatter.setTimeZone(c.getTimeZone());
monthFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getDisplayName()));
The Calendar is created with the default timezone, so dateFormatter will also have the same zone. So does monthFormat, and also the other formatters you created. The only formatter set to a different zone is the first one (which is set to UTC).
Also, the second formatter is redundant (it does the same thing that the first one is already doing: parsing the String to a Date), so you can remove it.
Assuming that your input is a String with the value 2018-09-30T13:45:00Z: the Z in the end indicates that this date is in UTC. So you should parse it using a formatter set to UTC. So, instead of using c.getTimeZone() and TimeZone.getDefault(), you should use only TimeZone.getTimeZone("UTC").
For the output, you must set the formatters with the timezone you want to convert to. If the timezone is "EDT", set to it (but don't use exactly "EDT", see below). If you want to use the JVM default, use TimeZone.getDefault() - just check this value before, to make sure the default is what you need.
Just keep in mind that short names like "EDT" and "EST" are not real timezones. Those abbreviations are ambiguous and not standard. Prefer to use IANA timezones names (always in the format Region/City, like America/New_York or Europe/Berlin).
So, when you do TimeZone.getTimeZone("EDT"), it usually returns "GMT" (because "EDT" is not recognized, and "GMT" is returned as default). That's because "EDT" is used by more than one timezone, so you must choose specifically which one you're using (I'm using America/New_York in these examples).
Another detail is that in the first 2 formatters you use hh, which means "hour of am/pm" (values from 1 to 12), but the input doesn't have AM/PM designators to properly resolve this. You need to change it to HH ("hour of day", with values from 0 to 23).
// input is in UTC
TimeZone inputZone = TimeZone.getTimeZone("UTC");
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
formatter.setTimeZone(inputZone);
Date localStartDate = formatter.parse(startTime);
Date localEndDate = formatter.parse(endTime);
...
// removed the second formatter (it was redundant)
// output is in EST (America/New_York)
// or use TimeZone.getDefault() to get JVM default timezone
TimeZone outputZone = TimeZone.getTimeZone("America/New_York");
SimpleDateFormat monthFormat = new SimpleDateFormat("MMM");
monthFormat.setTimeZone(outputZone);
...
SimpleDateFormat dateFormat = new SimpleDateFormat("dd");
dateFormat.setTimeZone(outputZone);
...
SimpleDateFormat dayNameFormat = new SimpleDateFormat("EEEE");
dayNameFormat.setTimeZone(outputZone);
...
SimpleDateFormat timeFormat = new SimpleDateFormat("hh:mm a");
timeFormat.setTimeZone(outputZone);
...
System.out.println("My Start date and end date==>>>" + startTimeName + " " + endTimeName);
With this, you're explicity using UTC for input and a specific timezone for output, instead of relying on the JVM default timezone (which can be different in each device and you can't control).
The output is:
My Start date and end date==>>>08:15 AM 09:45 AM
Java new Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).
First you can use a org.threeten.bp.Instant to parse the input, because it's in UTC (designated by the Z in the end). Then you use a org.threeten.bp.ZoneId to convert it to a org.threeten.bp.ZonedDateTime:
// output timezone
// or use ZoneId.systemDefault() to get JVM default timezone
ZoneId zone = ZoneId.of("America/New_York");
// parse the inputs
ZonedDateTime startDate = Instant.parse(startTime).atZone(zone);
ZonedDateTime endDate = Instant.parse(endTime).atZone(zone);
Then you can use these objects to get the other fields:
// get month name
System.out.println(startDate.getMonth().getDisplayName(TextStyle.SHORT, Locale.getDefault()));
This is equivalent to MMM pattern, and it will print the month name in the default locale. If you want the month name in a specific language, just use another java.util.Locale value (such as Locale.ENGLISH or any other one as described in the javadoc).
The org.threeten.bp.format.TextStyle defines if the month name will be narrow (usually just one letter), short (usually 2 or 3 letters) or full (the full name). The output varies according to the locale used.
I personally prefer to not use the default locale, because it can be changed without notice, even at runtime. It's always better to specify the locale you want.
To get the day of month, you can choose to get it as an int or as a formatted String (using a org.threeten.bp.format.DateTimeFormatter):
// get day of month as int
int day = startDate.getDayOfMonth(); // 30
// get day of month as formatted string
String dayStr = DateTimeFormatter.ofPattern("dd").format(startDate); // 30
To get the day of week, it's similar to the code used to get the month:
// get day of week
System.out.println(startDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault()));
The same logic applies here: the TextStyle defines how the name will be (in this case, FULL is equivalen to EEEE, and it prints the full name), and the locale defines the language used.
Finally, to get the corresponding time, you can use another DateTimeFormatter:
// get time
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("hh:mm a");
System.out.println(fmt.format(startDate)); // 08:15 AM
System.out.println(fmt.format(endDate)); // 09:45 AM
This will the date/time in the timezone you chose for the output.
If you're going to use the JVM default (ZoneId.systemDefault()), just check its value before to make sure it's the one you want (it might not be because this can be changed at runtime, so it's always better to specify one).

Android, Right way to programticly format date time as time ago

Example, from 1 min ago
i use api and time response result from Api service as:
{
"date_time":"2016-03-10 03:20:30"
}
Please discuss step by step, And if available how can programticly display it in Arabic format
منذ 15 دقيقة
My Code for date in list view adapter
TextView date_time = (TextView) convertView.findViewById(R.id.date_time_list_home);
date_time.setText(m.dateTime());
First, for any date/time manipulation on Android, I highly recommend using the ThreeTenABP library. This is a back port of the Java 8 java.time.* package, circumventing the notoriously disappointing java.util.Date and java.util.Calendar classes.
To parse your "date_time" using this library, you can use the following code:
// I set the ZoneId to systemDefault, but you should really use the ZoneId of the server
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.getDefault())
.withZone(ZoneId.systemDefault());
Instant instant = formatter.parse(m.dateTime(), Instant.FROM);
Android provides the DateUtils class to display date and time information. This class takes into account system settings such as Locale and 12/24-hour format. Therefore, if the Locale of the device is set to any of the Arabic locales (ar_), the date/time will be displayed in a format suited for it.
String display = DateUtils.getRelativeTimeSpanString(
instant.toEpochMilli(),
Instant.now().toEpochMilli(),
DateUtils.MINUTE_IN_MILLIS);
date_time.setText(display);
The last parameter in getRelativeTimeSpanString is the minimum resolution, so setting DateUtils.MINUTE_IN_MILLIS will not display the difference in seconds.
If you insist on using the Java 7 classes, here is the same code using them:
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = format.parse(m.dateTime());
String display = DateUtils.getRelativeTimeSpanString(
date.getTime(),
new Date(),
DateUtils.MINUTE_IN_MILLIS);
date_time.setText(display);
If you need a "transition resolution" greater than a single day (i.e. you want to display a date/time that is further than one day in the past as "… days ago") you can use the DateUtils.getRelativeDateTimeString() method instead:
String display = DateUtils.getRelativeDateTimeString(
mContext,
instant.toEpochMilli(),
DateUtils.MINUTE_IN_MILLIS,
DateUtils.WEEK_IN_MILLIS,
DateUtils.FORMAT_ABBREV_ALL);
Any date that is further back than the transitionResolution (in this case, one week) will be displayed in an appropriate date format, instead of the relative format. The minResolution and transitionResolution can be any long value, DateUtils contains other convenient constants such as MONTH_IN_MILLIS and YEAR_IN_MILLIS.
The last parameter takes an integer for formatting flags. These flags override the default formatting that DateUtils uses for each Locale; view the documentation for more information.

timezone clarification in java / android

I have the below code
public Long getEpochTime(String dateToGetItsEpoch) throws ParseException
{
TimeZone timeZone = TimeZone.getTimeZone("UTC");
final String REQUEST_DATE_FORMAT = "dd/MM/yyyy h:m";
DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
Date localDate = format.parse(dateToGetItsEpoch);
Calendar cal = Calendar.getInstance(timeZone);
cal.setTime(localDate);
format.setTimeZone(timeZone);
final String utcTime = format.format(cal.getTime());
Date d = cal.getTime();
return d.getTime();
}
If I change the locale of my device to whatever, I always get the UTC time as the return value. Which is correct, however I want to know how is this happening ? How does the device know Which timezone is the date I am giving to it so that it calculates accordingly ?
A Date doesn't have a time zone at all. A SimpleDateFormat does as a default for parsing and formatting; a Calendar does too; a Date doesn't.
Given this sequence of operations:
TimeZone timeZone = TimeZone.getTimeZone("UTC");
DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
Date localDate = format.parse(dateToGetItsEpoch);
Calendar cal = Calendar.getInstance(timeZone);
cal.setTime(localDate);
format.setTimeZone(timeZone);
final String utcTime = format.format(cal.getTime());
... you're initially parsing the string using the default time zone of the device, then you're formatting it in UTC. Note that the Calendar part is irrelevant here - you'd get the same result with:
TimeZone timeZone = TimeZone.getTimeZone("UTC");
DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
Date date = format.parse(dateToGetItsEpoch);
format.setTimeZone(timeZone);
final String utcTime = format.format(date);
I would personally recommend using Joda Time where possible for date/time work in Java, mind you. It's a much cleaner API than Calendar/Date.
java.time
The Answer by Jon Skeet is correct. Here is some code updated to use the modern java.time classes that have supplanted the troublesome legacy date-time classes.
Formatting pattern
Define a formatting pattern to match your inputs.
By the way, yours is a poor choice of formats. Instead I recommend using the standard ISO 8601 formats designed for exchanging date-time values as text.
12-hour versus 24-hour clock
Your input data or formatting pattern has a flaw. You used lowercase h which means one or two digits for an hour in the 12-hour clock (rather than 24-hour clock, which is uppercase H or HH). So your input makes no sense unless you add some indicator of AM or PM. I will assume you mistakenly omitted this from your Question's code.
Locale locale = Locale.US ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu h:m a" ).withLocale( locale ) ;
LocalDateTime
Parse such strings as LocalDateTime objects, as they lack an indicator of the intended time zone or offset-from-UTC.
String input = "23/01/2020 4:5 PM" ;
LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
ldt.toString(): 2020-01-23T16:05
Moment
The LocalDateTime object we obtained above does not represent a moment, is not a point on the timeline. We have a time of around 4 PM on the 23rd. But we cannot know if this was meant to be 4 PM in Tokyo, Toulouse, or Toledo — all very different moments several hours apart.
To determine a moment, we must know for certain the intended time zone. Then apply that zone as a ZoneId to get a ZonedDateTime. Then we have arrived at a moment.
Locale is not a time zone
locale of my device to whatever
A Locale has nothing to with time zone. A Locale is used for localizing generated text representing a date-time object.
To localize, specify:
FormatStyle to determine how long or abbreviated should the string be.
Locale to determine:
The human language for translation of name of day, name of month, and such.
The cultural norms deciding issues of abbreviation, capitalization, punctuation, separators, and such.
Example:
Locale l = Locale.CANADA_FRENCH ; // Or Locale.US, Locale.JAPAN, etc.
DateTimeFormatter f =
DateTimeFormatter
.ofLocalizedDateTime( FormatStyle.FULL )
.withLocale( l )
;
String output = myZonedDateTime.format( f );
You could have an engineer from Québec who uses the Locale.CANADA_FRENCH for human language and cultural norms, but while visiting in Japan uses Asia/Tokyo time zone for scheduling appointments.
ZonedDateTime
Back to your LocalDateTime object. If you are certain it was meant to represent a moment as seen in the wall-clock time in Tunisia, then apply a time zone of Africa/Tunis.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;
You asked:
How does the device know Which timezone is the date I am giving to it so that it calculates accordingly ?
You were using terrible date-time classes that failed to account for the concept of a date-time lacking an indicator of time zone or offset-from-UTC. So technically, your code is a mess, a hack, unavoidable in those days before Joda-Time and its successor, java.time.
I suggest spending no effort on trying to understand that behavior of Date & Calendar. Just move on to using java.time, the industry-leading date-time handling framework.

Categories

Resources