I have the following String that I would like to change to UTC:
Thu Aug 24 07:38:32 GMT+01:00 2017
I'm using Joda-Time library.
I know how to create a new Datetime eg new dateTime(DateTimeZone.UTC) but how can I create a DateTime object from the above String?
I have tried the following but get an exception. Surely there must be another way to create a DT obect without chopping the original String up? What if the external API changes how it sends my app the orignal String, my String manipulation code would fail.
DateTimeFormatter df = DateTimeFormat.forPattern("dd-MMM-YYYY HH:mm");
String strOrigTime = "Thu Aug 24 07:38:32 GMT+01:00 2017";
DateTime dt = DateTime.parse(strOrigTime, df);
Log.e(TAG, "dt after parse = " + dt.toString());
Error:
Caused by: java.lang.IllegalArgumentException: Invalid format: "Thu Aug 24 07:38:32 GMT+01:00 2017"
at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:866)
at org.joda.time.DateTime.parse(DateTime.java:144)
The format used (dd-MMM-YYYY HH:mm) means: day (dd) followed by -, followed by month (MMM), followed by -, followed by year (YYYY) and so on (check the javadoc for more details).
This format doesn't match the input string (which has day-of-week followed by month, followed by day, then hour/minute/second, etc). So the first thing is to use a format that matches the input, otherwise you'll always get "Invalid format" errors.
Another detail is that day of week and month names are in English, so you must also use a java.util.Locale to specify the language you're using to parse the input. If you don´t use a locale, the system default will be used, and it's not guaranteed to always be English (and it can also be changed, even at runtime, so it's always better to specify one).
I also had to add "GMT" as a literal and call withOffsetParsed() to make it include the offset (+01:00) in the parsed object:
DateTimeFormatter df = DateTimeFormat
// use a pattern that matches input
.forPattern("EEE MMM dd HH:mm:ss 'GMT'Z yyyy")
// use English locale for day of week and month
.withLocale(Locale.ENGLISH)
// include the offset (+01:00) in the parsed object
.withOffsetParsed();
String strOrigTime = "Thu Aug 24 07:38:32 GMT+01:00 2017";
DateTime dt = DateTime.parse(strOrigTime, df);
System.out.println(dt.toString());
The output is:
2017-08-24T07:38:32.000+01:00
Then, you can set the UTC timezone to this object:
dt = dt.withZone(DateTimeZone.UTC);
System.out.println(dt.toString());
The output will be:
2017-08-24T06:38:32.000Z
Note that withZone method preserves the same instant (both dates represent the same point in time), just the timezone used in the output is changed. But both dates are equivalent (they represent the same instant, as 07:38 in offset +01:00 is the same as 06:38 in UTC).
If you want all dates to be converted to UTC, you can also set this in the formatter:
// set UTC to the formatter
df = df.withZone(DateTimeZone.UTC);
Then you don't need to call withZone in the DateTime objects: all parsed dates will be converted to UTC.
You also told that "if the external API changes how it sends my app the orignal String, my String manipulation code would fail".
Well, if the input String changes, you'll have to change your format as well - there's no other way, Joda-Time can't just guess what's the format, you have to tell it.
If you want to parse more than one format, there's a way to create a formatter that uses lots of different patterns and try to parse each one, until one of them works (or throw exception if none works). You could do something like that:
// format 1
DateTimeFormatter f1 = DateTimeFormat
// use a pattern that matches input
.forPattern("EEE MMM dd HH:mm:ss 'GMT'Z yyyy")
// use English locale for day of week and month
.withLocale(Locale.ENGLISH)
// include the offset (+01:00) in the parsed object
.withOffsetParsed();
// format 2
DateTimeFormatter f2 = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss Z");
// array of all possible formats
DateTimeParser[] parsers = { f1.getParser(), f2.getParser() };
// formatter that uses all the possible formats
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// append array of possible formats
.append(null, parsers)
// create formatter
.toFormatter().withLocale(Locale.ENGLISH).withOffsetParsed()
// set all parsed objects to UTC
.withZone(DateTimeZone.UTC);
// parse first format
System.out.println(DateTime.parse("Thu Aug 24 07:38:32 GMT+01:00 2017", formatter));
// parse second format
System.out.println(DateTime.parse("24/08/2017 07:38:32 +01:00", formatter));
Both dates will be parsed to:
2017-08-24T06:38:32.000Z
Then you can add new formats to the array, as needed.
Java 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.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
The code to parse the inputs is very similar, with minor changes in the format.
And I'm using the Instant class, because you want the output in UTC, and Instant represents a UTC instant:
// format 1
DateTimeFormatter f1 = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss O yyyy", Locale.ENGLISH);
// format 2
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss XXX");
// formatter with both formats
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// add format 1
.appendOptional(f1)
// add format 2
.appendOptional(f2)
// create formatter
.toFormatter(Locale.ENGLISH);
// parse first format
System.out.println(Instant.from(formatter.parse("Thu Aug 24 07:38:32 GMT+01:00 2017")));
// parse second format
System.out.println(Instant.from(formatter.parse("24/08/2017 07:38:32 +01:00")));
This will output:
2017-08-24T06:38:32Z
2017-08-24T06:38:32Z
Related
I have a date in this format - 01 May 2020, and I want to parse it in the following format - 2020-05-01T00:00:00. I am using the following code -
static String convertDateStringFormat(String dateString, String originalDateFormat, String outputDateFormat){
DateFormat inputFormat = new SimpleDateFormat(originalDateFormat, Locale.ENGLISH);
Date input = null;
try {
input = inputFormat.parse(dateString);
} catch (ParseException e) {
e.printStackTrace();
}
DateFormat outputFormat = new SimpleDateFormat(outputDateFormat, Locale.ENGLISH);
return outputFormat.format(input);
}
and I am passing the arguments as - convertDateStringFormat("01 May 2020", "dd MMM yyyy", "yyyy-MM-dd'T'HH:mm:ss"). I dont know what I am doing wrong but it gives me this exception - java.lang.IllegalArgumentException: Parse error: 2020-05-01T00:00:00, so it looks like it is converting to the right format and then throwing an exception?
Build a format using DateTimeFormatterBuilder with hour defaulted to 0 and then parse the date string into LocalDateTime.
Demo:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
public class Main {
public static void main(String[] args) {
DateTimeFormatter format = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("dd MMM yyyy"))
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
.toFormatter();
String strDate = "01 May 2020";
LocalDateTime date = LocalDateTime.parse(strDate, format);
DateTimeFormatter targetFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
String strDateInTargetFormat = date.format(targetFormat);
System.out.println(strDateInTargetFormat);
}
}
Output:
2020-05-01T00:00:00
tl;dr
LocalDate
.parse(
"01 May 2020" ,
DateTimeFormatter
.ofPattern( "dd MMMM uuuu" )
.withLocale( Locale.US )
)
.atStartOfDay(
ZoneId.of( "Africa/Tunis" )
)
.format(
DateTimeFormatter.ISO_LOCAL_DATE_TIME
)
See this code run live at IdeOne.com.
2020-05-01T00:00:00
Details
You said:
I have a date in this format - 01 May 2020
That means you have text, a String, not a date. Date-time objects do not have a “format”.
Parse your text as a LocalDate.
String input = "01 May 2020" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd MMMM uuuu" ).withLocale( Locale.US ) ;
LocalDate ld = LocalDate.parse( input , f ) ;
You said:
want to parse it in the following format - 2020-05-01T00:00:00.
No, that is not parsing. That is generating text.
But first we need to determine a time-of-day. I presume you want the first moment of the day.
Some dates in some time zones do not start at 00:00. So always let java.time determine the first moment of the day. We capture this moment as a ZonedDateTime object.
ZoneId z = ZoneId.of( "Asia/Amman" ) ;
ZonedDateTime zdt = ld.atStartOfDay( z ) ;
You ask for text to be generated in a format with no indication of time zone or offset-from-UTC. I recommend against this, as the reading of such text is ambiguous. But if you insist, java.time includes a predefined format for the kind of text you desire. This format is amongst those defined in ISO 8601.
String output = zdt.format( DateTimeFormatter.ISO_LOCAL_DATE_TIME ) ;
See this code run live at IdeOne.com.
output: 2020-05-01T00:00:00
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.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
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. Hibernate 5 & JPA 2.2 support java.time.
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….
Use like that to convert one format to another:
DateFormat originalFormat = new SimpleDateFormat("dd MMM yyyy", Locale.ENGLISH);
DateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = originalFormat.parse("01 May 2020");
String formattedDate = targetFormat.format(date);
I recently started coding my really first Android project using Android Studio 3.1.2 and SDK 19.
One of my Objects has Date attributes. At some points I want to display the whole date time or parts of it in a TextView. So I tried it the rookie way and called toString() on my Date.
However the displayed text contains elements I didn't define in the SingleDateFormat pattern I used to create the Date Object.
This is how I create the Date on myObject:
Date date1;
Date date2;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
date1 = format.parse(json.getString("date_1"));
dtae2 = format.parse(json.getString("date_2"));
} catch(ParseException e) {
//error handling stuff
e.printStackTrace();
}
This is where I want to display the Date on a View:
myTextView.setText("First appearance logged at " + myObject.getDate1().toString());
I expected a String like 2018-08-16 12:14:42 to be displayed. Instead what I get is Thu Aug 12:14:42 GMT +02:00 2018. This seems to be another DateFormat and ignoring my custom pattern.
So my question is, if there's a way to manipulate the output of toString(), so the Date gets displayed in the way I defined in the pattern. Can I somehow pass the pattern to the toString() method?
EDIT
I changed the attributes of my Objects to String type, though it's way easier for presenting. The reason to convert them into a Date is, that I need to calculate the duration between the two guys, but that's not a problem i can't solve. Thanks to the community.
According to your need, you can just use json.getString("date_1").
You don't need to set extra logics. Parsing is needed when you want to convert String date to Date object for some calculation.
If you want to change format of received date then use this method.
changeStringDateFormat(json.getString("date_1"), "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd");
Just put this method inside your Util.
public String changeStringDateFormat(String date, String inputDateFormat, String outPutDateFormat) {
Date initDate = null;
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(inputDateFormat);
initDate = simpleDateFormat.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
SimpleDateFormat outputFormatter = new SimpleDateFormat(outPutDateFormat);
String parsedDate = outputFormatter.format(initDate);
return parsedDate;
}
See Java Date Doc, It returns string from default format.
public String toString()
Converts this Date object to a String of the form:
dow mon dd hh:mm:ss zzz yyyy
Simply Write this code snippet
JAVA FILE
public class MainActivity extends AppCompatActivity {
TextView my_text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
my_text = findViewById(R.id.my_text);
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
String date = simpleDateFormat.format(new Date());
Toast.makeText(getApplicationContext(), "" + date, Toast.LENGTH_SHORT).show();
my_text.setText("Your Date is : " + date);
}
}
XML FILE
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="mydemo.com.anew.MainActivity">
<TextView
android:id="#+id/my_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
See output:
See Screenshot of output same like your requirement get a current date:
Refer this Tutorial
Hope this may help to you
tl;dr
Use the modern java.time classes instead of the terrible legacy Date & SimpleDateFormat classes.
myJavaUtilDate // Never use `java.util.Date`.
.toInstant() // Convert from legacy class to modern replacement. Returns a `Instant` object, a moment in UTC.
.atOffset( // Convert from the basic `Instant` class to the more flexible `OffsetDateTime` class.
ZoneOffset.UTC // Constant defining an offset-from-UTC of zero, UTC itself.
) // Returns a `OffsetDateTime` object.
.format( // Generate a `String` with text representing the value of this `OffsetDateTime` object.
DateTimeFormatter.ISO_LOCAL_DATE_TIME // Pre-defined formatter stored in this constant.
) // Returns a `String` object.
.replace( "T" , " " ) // Replace the standard `T` in the middle with your desired SPACE character.
2018-08-16 10:14:42
java.time
You are using terrible old classes that were supplanted years ago by the java.time classes.
If handed a java.util.Date object, immediately convert to java.time.Instant. Both represent a moment in UTC. Instant has a finer resolution of nanoseconds rather than milliseconds.
To convert between the legacy and modern classes, look to new conversion methods added to the old classes.
Instant
Instant instant = myJavaUtilDate.toInstant() ; // New method on old class for converting to/from java.time classes.
ISO 8601
To generate a String with text in standard ISO 8601 format similar to your desired format, call toString.
String output = instant.toString() ; // Generate text in standard ISO 8601 format.
Elapsed time = Duration
By the way, to calculate elapsed time, use the Duration classes. Pass a pair of Instant objects to calculate the number of 24-hour "days", hours, minutes, and seconds elapsed.
Duration d = Duration.between( start , stop ) ; // Calc elapsed time.
2018-08-16T10:14:42Z
OffsetDateTime
For other formatting, convert from the basic Instant class to the more flexible OffsetDateTime class.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
odt.toString(): 2018-08-16T10:14:42Z
DateTimeFormatter
Your desired format is close to the predefined formatter DateTimeFormatter.ISO_LOCAL_DATE_TIME. Just replace the T in the middle with a SPACE.
String output = odt.format( DateTimeFormatter.ISO_LOCAL_DATE_TIME )
.replace( "T" , " " ) ;
2018-08-16 10:14:42
ZonedDateTime
Keep in mind that we are only looking at UTC so far. For any given moment, the date and the time-of-day both vary around the globe by zone.
If you want to see that same moment through the lens of the wall-clock time used by the people of a certain region (a time zone), then apply a ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString(): 2018-08-16T11:14:42+01:00[Africa/Tunis]
You can use the same formatter as seen above to generate a string in your desired format.
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.
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
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 (<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.
You need to use SimpleDateFormat something like this:
String myFormat = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(myFormat, Locale.US);
myTextView.setText("First appearance logged at " + sdf.format(date1));
In my project I've been formatting using the format() function, like so:
myTextView.setText("First appearance logged at " + format.format(myObject.getData1()));
I hope that helps.
I have a problem in convert time coming from server and I want to convert it to 24 hour. I'm using the following code:
String timeComeFromServer = "3:30 PM";
SimpleDateFormat date12Format = new SimpleDateFormat("hh:mm a");
SimpleDateFormat date24Format = new SimpleDateFormat("HH:mm");
try {
((TextView)findViewById(R.id.ahmad)).setText(date24Format.format(date12Format.parse(timeComeFromServer)));
} catch (ParseException e) {
e.printStackTrace();
}
There is the error:
Method threw 'java.text.ParseException' exception.)
Detailed error message is:
Unparseable date: "3:30 PM" (at offset 5)
But if I replace PM to p.m. it works without any problem like this:
timeComeFromServer = timeComeFromServer.replaceAll("PM", "p.m.").replaceAll("AM", "a.m.");
Can any one tell me which is the correct way?
SimpleDateFormat uses the system's default locale (which you can check using the java.util.Locale class, calling Locale.getDefault()). This locale is device/environment specific, so you have no control over it and can have different results in each device.
And some locales might have a different format for AM/PM field. Example:
Date d = new Date();
System.out.println(new SimpleDateFormat("a", new Locale("es", "US")).format(d));
System.out.println(new SimpleDateFormat("a", Locale.ENGLISH).format(d));
The output is:
p.m.
PM
To not depend on that, you can use Locale.ENGLISH in your formatters, so you won't depend on the system/device's default configuration:
String timeComeFromServer = "3:30 PM";
// use English Locale
SimpleDateFormat date12Format = new SimpleDateFormat("hh:mm a", Locale.ENGLISH);
SimpleDateFormat date24Format = new SimpleDateFormat("HH:mm");
System.out.println(date24Format.format(date12Format.parse(timeComeFromServer)));
The output is:
15:30
The second formatter doesn't need a specific locale as it's not dealing with locale specific information.
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.
One detail is that SimpleDateFormat always works with Date objects, which has the full timestamp (the number of milliseconds since 1970-01-01T00:00Z), and both classes implicity use the system default timezone behind the scenes, which can mislead you and generate unexpected and hard to debug results. But in this specific case, you need only the time fields (hour and minutes) and there's no need to work with timestamp values. The new API has specific classes for each case, much better and less error prone.
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 ThreeTenABP (more on how to use it here).
You can use a org.threeten.bp.format.DateTimeFormatter and parse the input to a org.threeten.bp.LocalTime:
String timeComeFromServer = "3:30 PM";
DateTimeFormatter parser = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
LocalTime time = LocalTime.parse(timeComeFromServer, parser);
System.out.println(time.format(formatter));
The output is:
15:30
For this specific case, you could also use time.toString() to get the same result. You can refer to javadoc for more info about the backport API.
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….
In my app, I receive dates from a webservice in the form yyyy-MM-dd (e.g. 2016-03-05) and I need to format them as [abbreviated month] [date], e.g. Mar 5. Additionally, I have a start and end date and want to show them as a date range, unless both dates are the same.
Currently I'm using DateUtils.formatDateRange(), which should take care of my requirements and provide proper localization, but I'm running into two problems:
When my end date is the day after my start date, formatDateRange()
only shows the formatted start date. For example, if start date is
2016-03-05 and end date is 2016-03-06, the method returns Mar 5 (but it should be Mar 5 - Mar 6). Why does this happen?
When the end date is in the same month, the month is not shown. For example, if start date is 2016-03-05 and end date is
2016-03-12, the method returns Mar 5 - 12. Is there a way to make it show Mar 5 - Mar 12 instead?
Here is my code:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date startDate, endDate;
try {
startDate = sdf.parse(startDateString);
endDate = sdf.parse(endDateString);
} catch (ParseException ignored) {
return null;
}
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH;
return DateUtils.formatDateRange(context, startDate.getTime(), endDate.getTime(), flags);
In the first case, the date formatter is taking your end date in the range as exclusive (as opposed to inclusive in the range). If you simply add one millisecond to the end date, you will see the ranges you expect, because now the date range includes the point in time at midnight for the end date.
In the second case, I'm afraid you are up to the current locale rules for date formatting. You pretty much have to accept what Android thinks is the best formatting, or come up with your own rules for each locale that you want to support.
tl;dr
LocalDate.parse( inputStart )
.format( DateTimeFormatter.ofPattern( "MMM d" ).withLocale( Locale.US ) )
+ " - " +
LocalDate.parse( inputStop )
.format( DateTimeFormatter.ofPattern( "MMM d" ).withLocale( Locale.US ) )
Mar 5 - Mar 6
Details
You can do this quite simply with the java.time classes rather than the troublesome old legacy date-time classes ( Date, SimpleDateFormat ) and the external library DateUtils.
Your input date strings use the standard ISO 8601 format. The java.time classes use the standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.
The LocalDate class represents a date-only value without time-of-day and without time zone.
LocalDate start = LocalDate.parse( "2017-01-23" );
LocalDate stop = LocalDate.parse( "2017-02-14" );
To generate a string with just the abbreviated month name and day-of-month, use DateTimeFormatter.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MMM d" );
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.
f = f.withLocale( Locale.US ) ; // Or Locale.CANADA_FRENCH, Locale.UK, Locale.ITALY, etc.
Ask the LocalDate to generate a string representing its value.
String output = start.format( f ) + " - " + stop.format( f ) ;
Jan 23 - Feb 14
MonthDay
Sounds like you may be interested in the MonthDay class if needing to work with the concept of a month and a day-of-month but without any year.
MonthDay md = MonthDay.of( 1 , 23 ) ;
Or use the Month enum to specify the month argument.
MonthDay start = MonthDay.of( Month.JANUARY , 23 ) ;
MonthDay stop = MonthDay.of( Month.FEBRUARY , 14 ) ;
To generate a string in standard ISO 8601 format, call toString.
String output = start.toString() ;
--01-23
Or use the same DateTimeFormatter seen above.
String output = start.format( f );
Jan 23
The ISO 8601 defines a format indicating a span of time using a slash character. So your same range of month-day values would be:
String output = start.toString() + "/" + stop.toString() ;
--01-23/--02-14
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….
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.