Hot questions for Using Joda-Time in simpledateformat

Top Java Programmings / Joda-Time / simpledateformat

Question:

I'm trying to modify some code to use Joda-Time rather than java.sql.Timestamp

Currently the code is using Threadlocal and SimpleDateFormat:

 public static final ThreadLocal<DateFormat> FORMAT_TIMESTAMP = new ThreadLocal<DateFormat>() {
    @Override
    protected DateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
    }
};

public static String format(Timestamp timestamp) {
    return FORMAT_TIMESTAMP.get().format(timestamp);
}

My understanding is that Joda-time is thread safe, so there is no need to use ThreadLocal

With this in mind I have modified the code to this:

 public static String format(Instant timestamp) {

    Instant formated = Instant.parse(timestamp.toString(), DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssXXX"));

    return formated.toString();
}

If I nned to insert the values into a DB later in the code I plan to use this method.

Assuming I'm going about this the right way, is there anyway to format the DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssXXX") like the

SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX")

At the moment I'm getting a Invalid format Exception


Answer:

X is not recognised by Joda. Replacing the XXX by ZZ should do what you need.

Because DateTimeFormat is thread safe, you can share it across threads. So your code could look like this:

private static final DateTimeFormatter FORMAT_TIMESTAMP =
                                      DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZZ");

public static String format(Instant timestamp) {
    return FORMAT_TIMESTAMP.print(timestamp);
}

Question:

I can see that GMT time is having 'Z' constant as an indicator that it is GMT time. However when I parse the GMT string, it still is printing local time.

Code:

SimpleDateFormat outFormat = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
String timeGMT = "2015-05-21T08:42:27.334Z";
try {
    System.out.println("Time GMT>>>>>>"+outFormat.parse(timeGMT));
} catch (ParseException e) {
    e.printStackTrace();
}

OUTPUT:

Thu May 21 08:42:27 IST 2015

Expected:

Thu May 21 08:42:27 GMT 2015

Answer:

Two issues here.

The first is that you are using the wrong format to parse. Your format tells the parser to just look at the Z as a literal character with no significance.

This means it will parse it as a local date because it doesn't treat the Z as a marker of time zone. If you want the Z to be interpreted as time zone, your format should have X instead of 'Z' in it:

    String timeGMT = "2015-05-21T08:42:27.334Z";

    DateFormat f1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    DateFormat f2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");

    Date d1 = f1.parse(timeGMT);
    Date d2 = f2.parse(timeGMT);

    System.out.println(d1);
    System.out.println(d2);

I'm currently in GMT+3, and this is the output I'm getting from this:

Thu May 21 08:42:27 IDT 2015
Thu May 21 11:42:27 IDT 2015

As you see, d2 is 3 hours ahead, which means that it interpreted the original time as in GMT.

Your other problem is that you print the resulting date in your default format. The default format is in your local time zone, so it will print it, as it did mine, in the local zone.

To change this you also have to format the output:

    DateFormat f3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
    f3.setTimeZone(TimeZone.getTimeZone("GMT"));

    System.out.println(f3.format(d2));

This yields - for the previous example - the following:

2015-05-21 08:42:27 GMT

Question:

I want to get current time from given timezone that can be like "IST" or "Indian Standard Time" etc. I am not able get time when input is "Indian Standard Time" or "Coordinated Universal Time". it works only for "IST" or "UTC" or "EST" etc. i have tried with Joda-Time and SimpleDateFormat.

SimpleDateFormat sd = new SimpleDateFormat(
            "yyyy.MM.dd G 'at' HH:mm:ss z");
Date date = new Date();
sd.setTimeZone(TimeZone.getTimeZone("IST"));
System.out.println(sd.format(date));



DateTime now = new DateTime();
//DateTimeZone LDateTimeZone = DateTimeZone.forID( "Asia/Kolkata" );
DateTimeZone LDateTimeZone = DateTimeZone.forID( "IST" );   //deprecated
System.out.println( "Joda-Time zone: " + now.toDateTime( LDateTimeZone ) );

Is there any way to handle both the inputs?


Answer:

I cannot advise more strongly against using the legacy java.util.Date. You should use the appropriate java.time class instead.

In java.time.ZonedDateTime, you can create a time zone alias map and populate it as you please. It's not pretty, but it works.

Map<String, String> aliasMap = new HashMap<>();
aliasMap.put("IST", "Asia/Calcutta");
aliasMap.put("Indian Standard Time", "Asia/Calcutta");
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Indian Standard Time", aliasMap));

Question:

I'm encountering a weird issue while working with SimpleDateFormat and timezones.

Basically, I have this code snippet:

    String input              = "2017-12-21 16:15:00";
    String inputTZ            = "America/Los_Angeles";
    String phoneTZ            = TimeZone.getDefault().getID();
    SimpleDateFormat fmtInput = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
    fmtInput.setTimeZone(TimeZone.getTimeZone(inputTZ));

    SimpleDateFormat fmtOutputEventTZ = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
    fmtOutputEventTZ.setTimeZone(TimeZone.getTimeZone(inputTZ));

    SimpleDateFormat fmtOutputPhoneTZ = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
    fmtOutputPhoneTZ.setTimeZone(TimeZone.getTimeZone(phoneTZ));


    try {
        LocalDateTime dt = new LocalDateTime(fmtInput.parse(input));

        Log.d("DEBUG>>>>", "INPUT=" + input);
        Log.d("DEBUG>>>>", "TZ_EVENT=" + inputTZ);
        Log.d("DEBUG>>>>", "TZ_PHONE=" + phoneTZ);
        Log.d("DEBUG>>>>", "DT=" + dt);
        Log.d("DEBUG>>>>", "OUTPUT_EVENT=" + fmtOutputEventTZ.format(dt.toDate()));
        Log.d("DEBUG>>>>", "OUTPUT_PHONE=" + fmtOutputPhoneTZ.format(dt.toDate()));
    }
    catch (Exception e) {
        e.printStackTrace();
    }

The code logic is as follow:

  • I have an input date with Los Angeles timezone (GMT-8)
  • I parse this date specifying the timezone (LA)
  • Then I format the date using the original timezone (LA) and the current phone timezone

I run the code with my phone timezone set to Chicago (GMT-6) and got the following output.

D/DEBUG>>>>: INPUT=2017-12-21 16:15:00
D/DEBUG>>>>: TZ_EVENT=America/Los_Angeles
D/DEBUG>>>>: TZ_PHONE=America/Chicago
D/DEBUG>>>>: DT=2017-12-21T18:15:00.000
D/DEBUG>>>>: OUTPUT_EVENT=2017-12-21 16:15:00
D/DEBUG>>>>: OUTPUT_PHONE=2017-12-21 18:15:00

Basically, the result is what was expected.

However, if I change my phone timezone to be that of New York (GMT-5) without restarting the app, I got the following output

D/DEBUG>>>>: INPUT=2017-12-21 16:15:00
D/DEBUG>>>>: TZ_EVENT=America/Los_Angeles
D/DEBUG>>>>: TZ_PHONE=America/New_York
D/DEBUG>>>>: DT=2017-12-21T18:15:00.000
D/DEBUG>>>>: OUTPUT_EVENT=2017-12-21 15:15:00
D/DEBUG>>>>: OUTPUT_PHONE=2017-12-21 18:15:00

Notice that the timezone returned by TimeZone.getDefault().getID() is the right one, but when it is parsed, SimpleDateFormat convert it as if the phone time zone was still Chicago, thus returning the wrong time!

However, if I kill the app and restart it, the same code works fine:

D/DEBUG>>>>: INPUT=2017-12-21 16:15:00
D/DEBUG>>>>: TZ_EVENT=America/Los_Angeles
D/DEBUG>>>>: TZ_PHONE=America/New_York
D/DEBUG>>>>: DT=2017-12-21T19:15:00.000
D/DEBUG>>>>: OUTPUT_EVENT=2017-12-21 16:15:00
D/DEBUG>>>>: OUTPUT_PHONE=2017-12-21 19:15:00

From my understanding, SimpleDateFormat.parse parse the date and store it as the number of seconds since 1970. Using setTimeZone should use the timezone to shift the time appropriately.

However, seems like the phone timezone change is not reflected by SimpleDateFormat while TimeZone.getDefault().getID() clearly reflects the change.

Is there any way to fix this issue?


Answer:

Found a solution!

I called DateTimeZone.setDefault(DateTimeZone.forID(TimeZone.getDefault().getID())) at the very beginning.

I guess DateTimeZone.setDefault is called when the application is started but never reset afterward by joda-time itself, even though the timezone of the user might have changed.

Question:


Answer:

Just for the case you are using Java 8 or above, make use of java.time. See this simple example:

public static void main(String[] args) {
    // example datetime
    String datetime = "2020-04-03 20:17:46";
    // create a formatter that parses datetimes of this pattern
    DateTimeFormatter parserDtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    // then parse the datetime with that formatter
    LocalDateTime ldt = LocalDateTime.parse(datetime, parserDtf);
    // in order to output the parsed datetime, use the default formatter (implicitly)
    System.out.println(ldt);
    // or format it in a totally different way
    System.out.println(ldt.format(
            DateTimeFormatter.ofPattern("EEE, dd. 'of' MMM 'at' hh-mm-ss a",
                    Locale.ENGLISH)
            )
    );
}

This outputs

2020-04-03T20:17:46
Fri, 03. of Apr at 08-17-46 PM

Please note that this doesn't consider any time zone or offset, it just represents a date and time consisting of the passed or parsed years, months, days, hours, minutes and seconds, nothing else.

Question:

I've taken all of the answers given on SOF and other websites and tried to do this:

String fromDateTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sssX").format(new DateTime().plusMonths(-6));

But I'm being given the exception:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot format given Object as a Date

What am I doing wrong?


Answer:

java.time
    String fromDateTime = OffsetDateTime.now(ZoneId.systemDefault()).minusMonths(6).toString();
    System.out.println(fromDateTime);

Output when running on my computer just now:

2018-08-04T12:45:34.087966+01:00

java.time is the modern Java date and time API and has effectively replaced Joda-Time. From the home page:

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).

In the code I am taking advantage of the fact that the java.time classes’ toString methods produce ISO 8601 format, the format you were asking for. I find it unlikely that the extra decimals on the seconds will pose any problem since thay are allowed within the standard.

Joda-Time
    String fromDateTime = new DateTime().minusMonths(6).toString();

Example output:

2018-08-04T12:50:36.071+02:00

new DateTime() only has millisecond precision. You will always get exactly 3 decimals on the seconds.

I gotta use old java libraries, cause I work for a company that uses java version < 8

java.time works nicely on Java 6 and 7 too, and all things being equal I recommend it over Joda-Time. Only if forced to use Java 5, Joda-Time is no doubt the good choice.

  • In Java 8 and later and on newer Android devices (from API level 26) java.time comes built-in.
  • In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
  • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
What went wrong in your code?

Your code can be compiled without signs of errors, but issues a java.lang.IllegalArgumentException: Cannot format given Object as a Date when run. This is because a SimpleDateFormat cannot format a Joda-Time DateTime object. We would of course have expected this to be reported on compile time. But in addition to SimpleDateFormat.format(Date) there is also an overridden format(Object) inherited from Format. It works for formatting either a Date or a Number (for milliseconds since the epoch). This method is the one that the compiler chooses when you pass a DateTime. Which is why there is no compile-time error message.

Tip: When you don’t immediately understand an error message, paste it into your search engine. It will very often lead you to an explanation and a solution. Also in this case.

Links

Question:

I'm trying to check a current date and time is in between Friday 17:42 and Sunday 17:42 of the week with Java.

At the moment I'm doing this with really really bad code block. It was a hurry solution. Now I'm refactoring but I couldn't find any method in joda or etc.

Any ideas? Thanks

private final Calendar currentDate = Calendar.getInstance();
private final int day = currentDate.get(Calendar.DAY_OF_WEEK);
private final int hour = currentDate.get(Calendar.HOUR_OF_DAY);
private final int minute = currentDate.get(Calendar.MINUTE);

if (day != 1 && day != 6 && day != 7) {
    if (combined != 0) {
        return badge == 1;
    } else {
        return badge == product;
    }
} else {
    if (day == 6 && hour > 16) {
        if (hour == 17 && minute < 43) {
            if (combined != 0) {
                return badge == 1;
            } else {
                return badge == product;
            }
        } else {
            return badge == 0;
        }
    } else if (day == 6 && hour < 17) {
        if (combined != 0) {
            return badge == 1;
        } else {
            return badge == product;
        }
    } else if (day == 1 && hour > 16) {
        if (hour == 17 && minute < 43) {
            return badge == 0;
        } else {
            if (combined != 0) {
                return badge == 1;
            } else {
                return badge == product;
            }
        }
    } else {
        return badge == 0;
    }
}

I've used the solution like thiswith the help of @MadProgrammer and @Meno Hochschild

Method:

public static boolean isBetween(LocalDateTime check, LocalDateTime startTime, LocalDateTime endTime) {
 return ((check.equals(startTime) || check.isAfter(startTime)) && (check.equals(endTime) || check.isBefore(endTime))); }

Usage:

static LocalDateTime now = LocalDateTime.now();
static LocalDateTime friday = now.with(DayOfWeek.FRIDAY).toLocalDate().atTime(17, 41);
static LocalDateTime sunday = friday.plusDays(2).plusMinutes(1);

if (!isBetween(now, friday, sunday)) { ... }

Thanks again for your efforts.


Answer:

Date and Calendar have methods that can perform comparisons on other instances of Date/Calendar, equals, before and after

However, I'd encourage the use of Java 8's new Time API

public static boolean isBetween(LocalDateTime check, LocalDateTime startTime, LocalDateTime endTime) {
    return ((check.equals(startTime) || check.isAfter(startTime)) && 
                    (check.equals(endTime) || check.isBefore(endTime)));
}

Which will return true if the supplied LocalDateTime is within the specified range inclusively.

Something like...

LocalDateTime start = LocalDateTime.now();
start = start.withDayOfMonth(26).withHour(17).withMinute(42).withSecond(0).withNano(0);
LocalDateTime end = start.plusDays(2);

LocalDateTime check = LocalDateTime.now();

System.out.println(check + " is within range = " + isBetween(check, start, end));
check = start;
System.out.println(check + " is within range = " + isBetween(check, start, end));
check = end;
System.out.println(check + " is within range = " + isBetween(check, start, end));
check = start.plusDays(1);
System.out.println(check + " is within range = " + isBetween(check, start, end));
check = end.plusMinutes(1);
System.out.println(check + " is within range = " + isBetween(check, start, end));

Which outputs

2015-06-25T18:31:32.969 is within range = false
2015-06-26T17:42 is within range = true
2015-06-28T17:42 is within range = true
2015-06-27T17:42 is within range = true
2015-06-28T17:43 is within range = false

Joda-Time has an Interval class which makes it even eaiser

Interval targetInterval = new Interval(targetStart, targetEnd);
System.out.println("Contains interval = " + interval.contains(targetInterval)

which is demonstrated here

A different approach...

So I was thinking on way home, assuming all you have is the date/time you want to check, how you might determine if the day falls within your range

LocalDateTime now = LocalDateTime.now();
boolean isBetween = false;
switch (now.getDayOfWeek()) {
    case FRIDAY:
    case SATURDAY:
    case SUNDAY:
        LocalDateTime lastFriday = getLastFriday(now);
        LocalDateTime nextSunday = getNextSunday(now);
        isBetween = isBetween(now, lastFriday, nextSunday);
        System.out.println(lastFriday + " - " + nextSunday + ": " + end);
        break;
}

What this does is checks the dayOfWeek to see if it's within the desired range, if it is, it finds the previous Friday and next Sunday from the specified date and checks to see if it falls between them (see the previous example)

lastFriday and nextSunday simply adds/subtracts a day from the specified date/time until to reaches the desired dayOfWeek, it then seeds the required time constraints

public static LocalDateTime getLastFriday(LocalDateTime anchor) {
    LocalDateTime ldt = LocalDateTime.from(anchor);
    return ldt.with(DayOfWeek.FRIDAY).withHour(17).withMinute(42).withSecond(0).withNano(0);
}

public static LocalDateTime getNextSunday(LocalDateTime anchor) {
    LocalDateTime ldt = LocalDateTime.from(anchor);
    return ldt.with(DayOfWeek.SUNDAY).withHour(17).withMinute(42).withSecond(0).withNano(0);
}

Question:

I've done lots of epoch time to string date conversions

but I never remember if I use some date time object or SimpleDateFormat or JodaTime or Calendar method, and they all have their own legends for formatting. On top of that, I'm not sure if I need to divide my epoch object by 1000 or something to make the timestamp conversion work correctly, it is very... time... consuming

so how would I convert an epoch timestamp to this format "March 16, 8:01 EDT" , where 8:01 is 24 hour time, and EDT is a timezone abbreviation


Answer:

I'm using JodaTime for this:

DateTimeFormatter dtf = DateTimeFormat.forPattern("MMM dd, HH:mm z");
dtf = dtf.withZone(DateTimeZone.UTC);
System.out.println(dtf.print(System.currentTimeMillis()));

Question:

I am working with Hibernate Validator.

I have a java.util.Date object and it is retrieved through reflection how a String due the following:

Object dateDeathObject = BeanUtils.getProperty(value, "dateDeath");
logger.info("dateDeathObject: {}", dateDeathObject);

The output always has the following format:

- dateDeathObject: Mon Sep 01 16:01:42 PET 2014

Using the SimpleDateFormat API

The pattern is EEE MMM dd hh:mm:ss zzz yyyy

Therefore with JDK 8 from the String (that represents the Date.toString() of some Date value) I can get again the original Date representation object to perform later some validation

SimpleDateFormat sdf =  new SimpleDateFormat("EEE MMM dd hh:mm:ss zzz yyyy");
Date date = sdf.parse((String) dateDeathObject);
logger.info("date: {}", date);

And works fine

I can see in the output the following:

  • dateDeathObject: Mon Sep 01 16:01:42 PET 2014
  • date: Mon Sep 01 16:01:42 PET 2014

The problem is with JodaTime, how an alternative to use java.util.Date.

DateTimeFormatter formatter = DateTimeFormat.forPattern("EEE MMM dd hh:mm:ss zzz yyyy");
DateTime dt = formatter.parseDateTime( (String) dateDeathObject );
logger.info("dt: {}", dt);

Observe the same EEE MMM dd hh:mm:ss zzz yyyy pattern, but I get instead the following error:

javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.
...
Caused by: java.lang.IllegalArgumentException: Invalid format: "Mon Sep 01 16:01:42 PET 2014" is malformed at "PET 2014"
    at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:873)
    at com.manuel.jordan.validation.support.DateDeathValidator.isValid(DateDeathValidator.java:67)
    at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:283)
    ... 38 more

I am confused about is malformed at "PET 2014". What is missing with JodaTime?


Answer:

PET is not listed in the list of joda-time supported timezones. You might try updating the database. Hope that helps, leave a comment if you're still having trouble.

Question:

I am trying to parse the current time and date into this simple date format.

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy-kk:mm");
Date datetoday = sdf.parse(Calendar.getInstance().getTime().toString());

However, I get this.

java.text.ParseException: Unparseable date: "Tue May 17 15:28:36 CDT 2016"

I'm still able to parse Strings like: 5/5/1991-12:00 but when given a Calendar Instance, it blows up. I really should have used JodaTime from the get go.

How can I just get the current time, into the SimpleDateFormat as it is? As I understood it, the SimpleDateFormat would take in the String and turn it into a Date once its able to parse it in. The parse would see that Tue would go into a ddd area in a SDF and May would go into a MMM if I had that in a simpleDateFormat. I dont. I have MM, and so its blowing up. I can do a M/d/yyyy in the input, so I end up giving it M, while askign for MM, and that still works.

Should I just scrap everything and go with JodaTime or is there a line or two that I'm missing?


Answer:

This is how you obtain current date represented as a Java Date object:

Date date = new Date();

This is how you obtain current date as a formatted string:

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy-kk:mm");
String formattedDateString = sdf.format(new Date());

You can then further parse it back into a Date:

Date date = sdf.parse(formattedDateString);

But there is no point in doing that because you already had the Date when you did the .format(...) call, only in your case you used

Calendar.getInstance().getTime()

which typically gives the same result as

new Date()

If you want to parse a string into a Date then the string needs to match the format specified in SimpleDateFormat constructor. Date's toString() method that you are using returns a string in a specific format that does not match the format from your example, that's why you are getting the ParseException error.