Joda DateTime Timezone doesn't show up correctly - android

I'm using Joda-Time DateTime in Android.
It seems that the DateTimeZone doesn't work correctly. Maybe it has something to do with daylight savings?
At this moment, we have GMT +2. This will change in a few weeks, for the winter. Then it will be GMT +1. But right now it's not correct.
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import org.joda.time.LocalDateTime
// GMT: Thursday 26 October 2017 12:11:54
val epochTime: Long = 1509019914
var dateServer = LocalDateTime(epochTime * 1000).toDateTime(DateTimeZone.forID("Europe/Amsterdam"))
Expected (correct time in Amsterdam):
14:11:54 GMT+02:00 DST
Actual:
13:11 2017-10-26T13:11:54.000+01:00

The LocalDateTime constructor gets the epochTime value and converts to the default timezone to get the date and time values - check the value of DateTimeZone.getDefault(), it's probably a timezone that's currently using the +01:00 offset.
Then the toDateTime method creates a DateTime that corresponds to the same date and time represented by the LocalDateTime, but at the specified timezone (it just "attaches" the timezone to the date/time values, and no conversion is made).
If you want to get the date and time that corresponds to the epochTime in a specific timezone, just create the DateTime directly:
val dateServer = DateTime(epochTime * 1000, DateTimeZone.forID("Europe/Amsterdam"));
With this, dateServer will be:
2017-10-26T14:11:54.000+02:00
An example in my default timezone, just to be more clear. My default timezone (returned by DateTimeZone.getDefault()) is America/Sao_Paulo, which is using the offset -02:00 (2 hours behind UTC) at October 26th 2017.
The epochTime 1509019914 corresponds to UTC 2017-10-26T12:11:54Z.
When I do LocalDateTime(epochTime * 1000), it gets the corresponding UTC value (12:11) and converts to the default timezone: in my case, to 10:11, so the LocalDateTime will have the value 2017-10-26T10:11:54.
Then the toDateTime method just creates a DateTime that corresponds to the same date and time (2017-10-26T10:11:54), at the specified timezone. So it creates 2017-10-26T10:11:54 at Amsterdam (+02:00).
Your default timezone is probably one that uses the +01:00 offset, which would explain the difference you're getting (12:11 UTC is first converted to LocalDateTime with 13:11, and then toDateTime creates 13:11 at Amsterdam).
JSR310 - new date/time API
Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it 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).".
If you can't (or don't want to) migrate from Joda-Time to the new API, you can ignore this section.
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).
To get the corresponding UTC instant from the epochTime, you can use the org.threeten.bp.Instant class. Then you use a org.threeten.bp.ZoneId to convert it to a timezone, resulting in a org.threeten.bp.ZonedDateTime:
val dateServer = Instant.ofEpochSecond(epochTime).atZone(ZoneId.of("Europe/Amsterdam"));
dateServer will be a org.threeten.bp.ZonedDateTime, with the value corresponding to 2017-10-26T14:11:54+02:00[Europe/Amsterdam].

Related

How to parse date without using Local Time Zone?

From Server I get following values:
epochMillis=1556532279322
iso8601=2019-04-29T10:04:39.322Z
When I do serverTimeDateFormat.parse(iso8601), I get as a result Mon Apr 29 10:04:39 GMT+02:00 2019
and for serverTimeDateFormat.parse(iso8601).time, the result is 1556525079322, which is different from what I get from the server (2 hours behind from UNIX time), while I am in timeZone + 2 hours.
When I format it back with serverTimeDatFormat.format(1556525079322), the result is 2019-04-29T10:04:39.322Z
I understand that SimpleDateFormat is using local timezone, but why is the result 2 hours behind and how can I parse the Date without taking into account timezone? I don't understand the logic of all this.
My code:
private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z"
val epochMillis = 1556532279322
serverTimeDateFormat.parse(iso8601).time
The problem lies with the pattern for your SimpleDateFormat. At the end, you have 'Z', which indicates there should be a literal "Z" in the date string to be parsed. However, the "Z" at the end of the date has a special meaning, namely it signifies the UTC timezone. Hence, you should parse it as a timezone designator so that the correct date value will be obtained. You can do this with the pattern XXX (See JavaDocs).
private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z"
print( serverTimeDateFormat.parse(iso8601).time ) // 1556532279322
Runnable example on pl.kotl.in
Addendum: While the above code should work for you, if at all possible, you should consider adding ThreeTen Android Backport to your project. This will give you access to the newer time classes added by JSR310 to Java/Kotlin (Also available by default in Android API >=26). The classes have generally easier API, and use ISO8601 by default, so you wouldn't need any formatter at all:
print( ZonedDateTime.parse(iso8601).toInstant().toEpochMilli() )
Avoid legacy date-time classes
You are using terrible date-time classes that were supplanted years ago by the modern java.time classes defined in JSR 310.
Date::toString lies
why is the result 2 hours behind
It is not actually two hours behind.
The problem is that while a java.util.Date represents a moment in UTC, its toString method dynamically applies the JVM’s current default time zone while generating the text representing the value of the date-time object. While well-intentioned, this anti-feature confusingly creates the illusion of the Date object having that time zone.
In other words, Date::toString lies. One of many poor design decisions found in these legacy classes. And one of many reasons to never use these legacy classes.
java.time
Instant
Parse your count of milliseconds since the epoch reference of first moment of 1970 in UTC as a Instant.
Instant instant = Instant.ofEpochMilli( 1556532279322 );
Your other input, a standard ISO 8601 string, can also be parsed as an instant.
Instant instant = Instant.parse( "2019-04-29T10:04:39.322Z" ) ;
ZonedDateTime
To see the same moment through the wall-clock time used by the people of a particular region (a time zone), apply a ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ) ;
Both the instant and zdt objects represent the same simultaneous moment. Two ways of reading the same moment, as two people conversing on the phone in Iceland and Québec would each see a different time on the clock on the wall while glancing simultaneously.

Date add GMT to hour

val startHour = SimpleDateFormat("HH:mm").format(pickup.begin())
The pickup.begin value is "Wed Apr 10 10:00:00 GMT+03:00 2019", so I need the start hour to be 10:00 +3h -> 13:00, but I get startHour value of 10:00.
I don't know how to add the GMT value to hour.
No, you’ve misunderstood. Wed Apr 10 10:00:00 GMT+03:00 2019 is (the textual representation of) a java.util.Date the value of which equals 2019-04-10T07:00 UTC. It seems your default time zone is GMT+03:00, and Date is trying to be friendly to you and print the time in this time zone, which is why it prints 10:00:00. 13:00 would certainly be incorrect no matter if you wanted the time in UTC or in your own default time zone.
The Date class returned from pickup.begin() is poorly designed and long outdated, so you may want to consider if a type from java.time, the modern Java date and time API, could be returned instead. It may also make the matter clearer.
Alternatively, convert that java.util.Date object to its modern counterpart, a java.time.Instant. Look for new conversion methods added to the old classes.
Instant instant = pickup.begin().toInstant() ; // Converting legacy `Date` object to modern `Instant` object.
Search Stack Overflow and read the Oracle Tutorial to learn more about Instant, OffsetDateTime, and ZonedDateTime classes.
You can use java.time on older Android versions if you add ThreeTenABP to your Android project. It’s the Android adaptation of the backport of java.time.
Links
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Try adding a Locale to your simpledateformat, matching the +3 timezone.
val startHour = SimpleDateFormat("HH:mm", Locale.Germany).format(pickup.begin())
You can try something like this
I take 'date' as string here
String date="something"
val sdf = SimpleDateFormat("HH:mm")
sdf.timeZone = TimeZone.getTimeZone("UTC")
val gmt = sdf.parse(date)
If this is your case
Date date="somevalue"
then
val gmt = sdf.format(date)
gmt will return you your local time

Why is JodaTime timezone shifting a date time?

When the string "2017-04-21T17:46:00Z" is passed into the first method the resulting formatted date string is "06:46 21 Apr 2017". Why is the hour moving by eleven hours? The input strings are being provided by an HTTP server application in JSON. I thought the Z suffix referred to Zulu, ie GMT.
private static final String DATE_TIME_FORMAT = "hh:mm dd MMM yyyy";
public static String formatTimestamp(String dateTimestamp) {
DateTime dateTime = getDateTimeFromTimestamp(dateTimestamp);
DateTimeFormatter fmt = DateTimeFormat.forPattern(DATE_TIME_FORMAT);
return fmt.print(dateTime);
}
private static DateTime getDateTimeFromTimestamp(String dateTimestamp) {
return new DateTime(dateTimestamp);
}
I suspect it relates to timezones but it's not clear how or where. The code is running on an Android device in the UK, in the GMT timezone.
I've made a test with java 7 and joda-time 2.7 (but not the Android's version)
That's how I could reproduce the problem:
// changing my default timezone (because I'm not in UK)
DateTimeZone.setDefault(DateTimeZone.forID("Europe/London"));
// calling your method
System.out.println(formatTimestamp("2017-04-21T17:46:00Z"));
The output is
06:46 21 Abr 2017
To check what's wrong, I've changed the date format to:
DATE_TIME_FORMAT2 = "hh:mm a dd MMM yyyy Z z zzzz";
Where a means "AM or PM", Z is the timezone offset/id, z is the timezone "short" name and zzzz is the timezone "long" name. Using this format, the output is:
06:46 PM 21 Abr 2017 +0100 BST British Summer Time
So the datetime created is 6PM, just one hour ahead of input, not eleven hours as you thought (actually if you change the format to HH instead of hh, the hours will be 18 instead of 06).
Also note the timezone fields: +0100 BST British Summer Time. The first part (+0100) means that this DateTime is one hour ahead of GMT, and BST British Summer Time means it's in British's Daylight Saving Time.
So, to have your output equals to your input, you have 2 alternatives:
1. Change your default timezone to UTC:
DateTimeZone.setDefault(DateTimeZone.UTC);
System.out.println(formatTimestamp("2017-04-21T17:46:00Z"));
The output will be:
05:46 21 Apr 2017
If you want to change the hours to be 17:46, change your date format, replacing hh by HH
2. Use the DateTime constructor that receives a DateTimeZone:
private static DateTime getDateTimeFromTimestamp(String dateTimestamp) {
// creates a DateTime in UTC
return new DateTime(dateTimestamp, DateTimeZone.UTC);
}
The output will be the same as alternative 1, but in this case you don't need to change the default timezone.
For me, alternative 2 makes more sense, because:
you don't need to change the default timezone (which can cause some mess in other parts of the application)
you already know that all dates handled by this code are in UTC time (because of the "Z" in the end)
Using java.time
The Answer by Hugo seems to be correct and informative. But FYI, the Joda-Time project is now in maintenance mode, with the team advising migration to the java.time classes. For Android, see the last bullet at bottom below.
Your input string is in standard ISO 8601 format. The java.time classes use standard formats when parsing & generating strings. So no need to specify a formatting pattern.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
String input = "2017-04-21T17:46:00Z" ;
Instant instant = Instant.parse( input ) ;
instant.toString(): 2017-04-21T17:46:00Z
For more flexible formatting such as you desire, convert to an OffsetDateTime object were you can specify any offset-from-UTC in hours and minutes. We want UTC itself (an offset of zero) so we can use the constant ZoneOffset.UTC.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
odt.toString(): 2017-04-21T17:46Z
Define a formatting pattern to match your desired format. Note that you must specify a Locale to determine (a) the human language for translation of name of day, name of month, and such, and (b) the cultural norms deciding issues of abbreviation, capitalization, punctuation, separators, and such.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "hh:mm dd MMM yyyy" , Locale.US ) ;
String output = odt.format( f ) ;
output: 05:46 21 Apr 2017
If you want to see this same moment through the lens of a region’s wall-clock time such as Europe/London or Pacific/Auckland, apply a time zone to get a ZonedDateTime.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST or BST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Note the time-of-day is an hour off because of Daylight Saving Time (DST).
zdt.toString(): 2017-04-21T18:46+01:00[Europe/London]
See this code run live at IdeOne.com.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use ThreeTenABP….

Convert milliSeconds To Gregorian Calendar

I want to convert milliSeconds in long format to Gregorian Calendar.
By searching in the web, i use the code below:
public static String getStringDate(int julianDate){
GregorianCalendar gCal = new GregorianCalendar();
Time gTime = new Time();
gTime.setJulianDay(julianDate);
gCal.setTimeInMillis(gTime.toMillis(false));
String gString = Utils.getdf().format(gCal.getTime());
return gString;
}
public static SimpleDateFormat getdf(){
return new SimpleDateFormat("yyyy-MM-dd, HH:MM",Locale.US);
}
Yes, the code works but i find that only the date and the hour are correct but there are errors on minutes. Say if the thing happens on 2014-11-06, 14:00, it will give me 2014-11-06, 14:11. I want to know are there any solutions to modify it or it is not recommended to convert time into Gregorian Calendar. Many thanks!
The problem actually is very simple,
modify SimpleDateFormat("yyyy-MM-dd, HH:MM",Locale.US) with
SimpleDateFormat("yyyy-MM-dd, HH:mm",Locale.getDefault());
will solve the problem
tl;dr
Instant.ofEpochMilli( millis ) // Convert count-from-epoch into a `Instant` object for a moment in UTC.
.atZone( ZoneId.of( "Pacific/Auckland" ) ) // Adjust from UTC to a particular time zone. Same moment, different wall-clock time. Renders a `ZonedDateTime` object.
.format( // Generate a String in a particular format to represent the value of our `ZonedDateTime` object.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd, HH:mm" )
)
java.time
The modern approach uses the java.time classes instead of those troublesome legacy classes.
Convert your count of milliseconds since the epoch reference of first moment of 1970 (1970-01-01T00:00Z) to a Instant object. Be aware that Instant is capable of finer granularity of nanoseconds.
Instant instant = Instant.ofEpochMilli( millis ) ;
That moment is in UTC. To adjust into another time zone, apply a ZoneId to get a ZonedDateTime.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment, so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Generate a string in your desired format using a DateTimeFormatter object.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd, HH:mm" , Locale.US ) ;
String output = zdt.format( f ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….

How to get localization of day in week

I couldnt find a symbol for that in SimpleDateFormat
How can I get the current day's localized name?
For example: Monday: 1. day of week ; Tuesday: 2.day, wednesday:3.day .....
I want to get Presentation Number "1" instead of Monday ...
As per other questions, you don't need SimpleDateFormat to get the numeric day of the week - that is provided by Calendar directly via the DAY_OF_WEEK field (which goes from 1 to 7 where 1 is SUNDAY and 7 is SATURDAY):
Calendar today = Calendar.getInstance();
int dayOfWeek = today.get(Calendar.DAY_OF_WEEK);
// Day of Week is a number between 1 and 7 where 1 is Sunday.
int dayOfWeekMondayFirst = (dayOfWeek + 5) % 7 + 1;
tl;dr
For the number 1-7, meaning Monday-Sunday, for today:
LocalDate.now().getDayOfWeek().getValue()
For the localized name of the day of the week:
LocalDate // Represent a date-only, without time-of-day and without time zone.
.now( // Get today’s current date as seen in the wall-clock time used by the people of a particular region (a time zone).
ZoneId.of( "Africa/Tunis" ) // Specify time zone.
) // Returns a `LocalDate`.
.getDayOfWeek() // Returns one of seven `DayOfWeek` enum objects.
.getDisplayName( // Localize the name of the day-of-week.
TextStyle.FULL , // How long or abbreviated should the localized string be.
Locale.UK // Specify a `Locale` to determine the human language and cultural norms to use in localizing.
) // Returns a string.
Monday
java.time
The modern approach uses the java.time classes that long ago supplanted the terrible legacy date-time classes such as SimpleDateFormat.
To get the day-of-week today, we need the date.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone or offset-from-UTC.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment during runtime(!), so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the JVM’s current default is applied implicitly. Better to be explicit, as the default may be changed at any moment during runtime by any code in any thread of any app within the JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
DayOfWeek
The DayOfWeek enum pre-defines a set of seven objects, one for each day of the week.
Ask the LocalDate object for its DayOfWeek.
DayOfWeek dow = ld.getDayOfWeek() ;
Ask the DayOfWeek object to automatically localize its name. The DayOfWeek::getDisplayName method translates the name of the day into any human language specified by a Locale such as Locale.US or Locale.CANADA_FRENCH.
String output = dow.getDisplayName( TextStyle.FULL , Locale.CANADA_FRENCH );
lundi
Or, in US English.
String output = dow.getDisplayName( TextStyle.FULL , Locale.US );
Monday
To get the number of the day-of-week, where Monday-Sunday is 1-7 per the ISO 8601 standard, ask the DayOfWeek enum object for its value.
int dowNumber = dow.getValue() ; // 1-7 for Monday-Sunday.
To get the number 1-7 as part of a larger formatting pattern use e or c as directed in the DateTimeFormatter class.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Categories

Resources