I have a GUI that have two comboxes and one textfield to enter the user's Hijiri birth date. I want to convert that Hijiri date to a Gregorian one ... every solution I found either a complex one or does not work .. I even checked the Joda-Time - API - Islamic calendar(HijrahChronology) and it does not have any converting methods !!
tl;dr
LocalDate.from( // Convert from Hijrah chronology to ISO 8601 chronology.
HijrahDate.of( 1400 , 2 , 19 ) // Instantiate a recent birthdate using Islamic calendar.
).toString() // Generate text representing this ISO date.
1980-01-08
Details
The java.time.chrono package offers a Hijrah chronology, as well as other chronologies.
While I know nothing about the Islamic calendar, it appears to me you can easily convert between ISO 8601 chronology to that Hijrah chronology.
Instantiate in Hijrah chronology.
// Instantiate a recent birth date using Islamic calendar.
HijrahDate hd = HijrahDate.of( 1400 , 2 , 19 );
System.out.println( "hd.toString(): " + hd );
hd.toString(): Hijrah-umalqura AH 1400-02-19
Convert to ISO 8601 chronology.
// Convert from Hijrah chronology to ISO 8601 chronology.
LocalDate ld = LocalDate.from( hd );
System.out.println( "ld.toString(): " + ld );
ld.toString(): 1980-01-08
Related
So I have:
Date startDate which is Sun Mar 27 17:32:01 EEST 2022
and
String period which is PT240H
And I need to generate a new date based on those 2 values. I need to add that 240H period to the startDate. The 240H meaning 10 days which I need to add to startDate and I will eventually need to have a new date which should be Wed Apr 6 17:32.01 EEST 2022.
PS. I am new to Java, hopefully I don't ask stupid things.
tl;dr
java.util.Date.from(
myJavaUtilDate
.toInstant()
.plus( Duration.parse( "PT240H" ) )
)
Details
Putting together those posted Comments…
You are using terrible date-time classes that were years ago supplanted by the modern java.time classes defined in JSR 310. Avoid using Date, Calendar, and such.
If handed a java.util.Date object, immediately convert to its replacement class, java.time.Instant. Use new conversion methods added to the old classes.
Instant instant = myJavaUtilDate.toInstant() ;
Parse your input string in standard ISO 8601 format as a Duration object.
Duration d = Duration.parse( "PT240H" ) ;
Add to our Instant to produce a second Instant, per immutable objects.
Instant later = instant.plus( d ) ;
You said:
The 240H meaning 10 days
Incorrect, 240 hours is not necessarily 10 days. Adding a value of 240 hours may or may not result in a moment ten days later, if you adjust into a time zone. Some dates in some time zones vary in length, running 23, 23.5, 25, or other numbers of hours long.
And be aware that both java.util.Date and Instant represent a moment as seen in UTC, that is, with an offset of zero hours-minutes-seconds. Unfortunately, the Date#toString method dynamically applies the JVM’s current default time zone while generating its text — giving a false illusion. This confusing behavior is one of the many design flows in the legacy date-time classes.
If you must interoperate with old code not yet updated to java.time, you can convert back to Date. But I strongly recommend moving away from these legacy classes ASAP.
java.util.Date date = Date.from( someInstant ) ;
Example code
FYI, EEST is not a time zone. Such 2-4 letter pseudo-zones indicate whether Daylight Saving Time (DST) is in effect, and hint at possible time zones. These should be used only for presentation to the user, never in your business logic, data storage, nor data exchange.
Real time zones are named in format of Continent/Region such as Africa/Casablanca and Asia/Tokyo.
The pseudo-zone EEST implies many different time zones. In this example code I use the real time zone "Europe/Bucharest". I am guessing that is your zone, given your user profile.
First we need to recreate your moment reported by Date#toString as ‘Sun Mar 27 17:32:01 EEST 2022’.
// Recreate original conditions.
LocalDate ld = LocalDate.of( 2022 , Month.MARCH , 27 ); // Sun Mar 27 17:32:01 EEST 2022
LocalTime lt = LocalTime.of( 17 , 32 , 1 );
ZoneId z = ZoneId.of( "Europe/Bucharest" );
TimeZone.setDefault( TimeZone.getTimeZone( z ) );
ZonedDateTime zdtStarting = ZonedDateTime.of( ld , lt , z );
Instant then = zdtStarting.toInstant();
java.util.Date startingPoint = Date.from( then );
Convert from legacy class to modern.
Instant instant = startingPoint.toInstant();
Add your desired 240 hours. Adjust into a time zone to obtain a ZonedDateTime, so we can better see its true meaning.
Duration duration = Duration.parse( "PT240H" );
Instant later = instant.plus( duration );
Date endingPoint = Date.from( later );
ZonedDateTime zdtLater = later.atZone( z );
Dump to console.
System.out.println( "-------| Start |--------------------" );
System.out.println( "zdtStarting = " + zdtStarting );
System.out.println( "startingPoint = " + startingPoint );
System.out.println( "instant = " + instant );
System.out.println( "-------| End |--------------------" );
System.out.println( "later = " + later );
System.out.println( "endingPoint = " + endingPoint );
System.out.println( "zdtLater = " + zdtLater );
When run.
-------| Start |--------------------
zdtStarting = 2022-03-27T17:32:01+03:00[Europe/Bucharest]
startingPoint = Sun Mar 27 17:32:01 EEST 2022
instant = 2022-03-27T14:32:01Z
-------| End |--------------------
later = 2022-04-06T14:32:01Z
endingPoint = Wed Apr 06 17:32:01 EEST 2022
zdtLater = 2022-04-06T17:32:01+03:00[Europe/Bucharest]
I am using org.joda.time.DateTime; package to convert ISO 8601 datetime for Eg "2017-02-07T00:00:00.000+05:30" to a format "yyyy-MM-dd HH:mm:ss.SSS".
Code is :
String dateTimePattern = "yyyy-MM-dd HH:mm:ss.SSS";
DateTimeFormatter dtf = DateTimeFormat.forPattern(inputDateTimePattern);
DateTime jodatime = dtf.parseDateTime("2017-02-07T00:00:00.000+05:30");;
System.out.println("Converted datetime is: ",jodatime.toString(dtf))
But i get error mentioning
java.lang.IllegalArgumentException: Invalid format: is malformed at ".T00:00:00.000+05:30"
How to convert ISO 8601 datetime format in required format in java ?
tl;dr
Joda-Time is replaced by the java.time classes.
OffsetDateTime.parse( "2017-02-07T00:00:00.000+05:30" )
java.time
The Joda-Time project is now in maintenance mode, with its team advising migration to the java.time classes.
In java.time, your input string can be parsed directly as a OffsetDateTime object. No need to specify a formatting pattern.
OffsetDateTime odt = OffsetDateTime.parse( "2017-02-07T00:00:00.000+05:30" );
A time zone is a history of offsets for a particular region. So always better to use if you are certain of the intended time zone.
ZoneId z = ZoneId.of( "Asia/Kolkata" ); // Or "America/Montreal", etc.
ZonedDateTime zdt = odt.atZoneSameInstant();
Joda-Time
In Joda-Time, you can parse a string in standard ISO 8601 format with an offset-from-UTC in either of two ways:
Constructornew DateTime( "2017-02-07T00:00:00.000+05:30" ) ;
Static parse methodDateTime.parse( "2017-02-07T00:00:00.000+05:30" )
These two routes are not the same! See the class doc from the parse method:
However, when this method is passed a date-time string with an offset, the offset is directly parsed and stored. As such, DateTime.parse("2010-06-30T01:20+02:00") and new DateTime("2010-06-30T01:20+02:00")) are NOT equal. The object produced via this method has a zone of DateTimeZone.forOffsetHours(2). The object produced via the constructor has a zone of DateTimeZone.getDefault().
Well... the solution might be a mix between 2 already existing answers here at Stack Overflow
First - look at this post How to parse and generate DateTime objects in ISO 8601 format which describes how to write such a code using C# (C# and JAVA are very similar)
Second - please use the SimpleDateFormat as described here How to parse a date?
This combination should do the trick
Looks like you got confused while using DateTimeFormat. Given time string is not in yyyy-MM-dd HH:mm:ss.SSS format. So you are getting the exception which is expected.
SimpleDateFormat target = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
SimpleDateFormat source = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
Date date=source.parse("2017-02-07T00:00:00+05:30");
System.out.println(target.format(date)); // prints 2017-02-07 00:00:00.000
This code will format the date into yyyy-MM-dd HH:mm:ss.SSS.
Use the below method
public static Calendar toCalendar(String iso8601string) {
DateTime dt = new DateTime(iso8601string);
Date date = new Date(dt.getMillis());
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
return calendar;
}
I use SimpleDateFormat to parse strings to Date objects and I wonder why the results are not what I expect.
For example:
DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date date = yyyyMMdd.parse("20100725");
System.out.println(date);
works as expected and outputs
Sun Jul 25 00:00:00 CEST 2010
But
Date date = yyyyMMdd.parse("2010-07-25");
System.out.println(date);
also works and outputs
Mon Dec 07 00:00:00 CET 2009
I expected a ParseException, but it seems that SimpleDateFormat interpretes the month part -07 and the day part -25 as a negative number. First I couldn't figure out how it comes to 7th of december. So I tried another value:
Date date = yyyyMMdd.parse("2010-7-25");
System.out.println(date);
and it outpus
Sun Apr 05 00:00:00 CEST 2009
So it seems that it somehow subtracts 7 month from the year 2010 which whould be 1th of may, and 25 days so the result is 5th of april 2009.
Image that you use the pattern yyyyMMdd in an service implementation and some client accidentially sends the date as yyyy-MM-dd. You will not get an exception. Instead you will get totally different dates. I guess this is not what you expect.
E.g.
String clientData = "2010-05-23";
DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date parsedDate = yyyyMMdd.parse(clientData);
System.out.println("Client : " + clientData);
System.out.println("Service : " + yyyyMMdd.format(parsedDate));
Do I miss something?
How do I prevent SimpleDateFormat to parse 'wrong' dates?
Sure I can use a regular expression to check first, but is there a better way?
Use SimpleDateFormat.setLenient(false); to get an exception. Otherwise it will try to parse the input as best as it can, which is usually wrong.
For some reason they decided that leniency should be true by default, but that is hardly a surprise.
Specify whether or not date/time parsing is to be lenient. With
lenient parsing, the parser may use heuristics to interpret inputs
that do not precisely match this object's format. With strict
parsing, inputs must match this object's format.
The accepted Answer by Cayman is correct: leniency in parsing by default is the problem.
java.time
You are using troublesome old date-time classes now supplanted by the java.time classes.
No such leniency-by-default problem in java.time. If the input does not strictly match the formatting pattern, a DateTimeParseException is thrown.
The LocalDate class represents a date-only value without time-of-day and without time zone.
ISO 8601 format
For standard ISO 8601 formatted inputs of YYYY-MM-DD, simply call parse directly.
String input = "2010-05-23";
try {
LocalDate ld = LocalDate.parse( input ); // Expects standard ISO 8601 input format.
} catch ( DateTimeParseException e ) {
…
}
“Basic” ISO 8601 format
The ISO 8601 standard allows for “basic” formats that minimize the use of separators. Not that I recommend these variations, but they exist.
Currently java.time predefines only a single one of these “basic” variations, DateTimeFormatter.BASIC_ISO_DATE.
String input = "20100725";
try {
LocalDate ld = LocalDate.parse( input , DateTimeFormatter.BASIC_ISO_DATE );
} catch ( DateTimeParseException e ) {
…
}
Custom format
For other formats, specify a formatter.
String input = "2010/07/25";
try {
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu/MM/dd" );
LocalDate ld = LocalDate.parse( input , f ); // Custom format.
} catch ( DateTimeParseException e ) {
…
}
Localized format
Or let java.time determine the localized format.
String input = … ;
try {
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( l );
LocalDate ld = LocalDate.parse( input , f ); // Localized format.
} catch ( DateTimeParseException e ) {
…
}
SimpleDateFormat.setLenient(false);
Is what needs to be done, or the input will be tried to be parsed well, and as you know, that doesn't always work. With the function above, the compiler will be strict about the format.
First, if you want to parse String "2010-05-23" your mask should be "yyyy-MM-dd" and not "yyyyMMdd". Second SimpleDateFormat has serious problems as it is not Thread safe. If you use java 8 then use learn and use new package "java.time". If you use any java earlier then version 8 then use some other frameworks for parsing date. One of the most popular is Joda time. Works much better.
I have a String with local time:
"2012-12-12T08:26:51+000"
I now need to create a String with GMT time based on the old String. For example, assuming 2 hours difference between local and GTM:
"2012-12-12T10:26:51+000"
I have created a SimpleDateFormat:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+SSSS");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String time = dateFormat.parse(mCreatedTime).toString();
But the time String is now in different format:
Wed Dec 12 etc
How do I get the output to be in format yyy-MM-dd'T'HH:mm:ss+SSSS but GMT time?
The dateFormat.parse() method returns an instance of Date, and when you call toString() on it the date will be printed in the default locale.
Use dateFormat.format() to get your Date value back to your required format.
As my comments on this question suggest, I believe the original poster of this question is confused and miseducated about date-time work. Nevertheless, I wrote some example code delivering exactly what Pierre asked for, along with my caveat that he is following some very bad practices.
Using Joda-Time 2.3 library and Java 7.
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;
// CAUTION: The question asked specifically for the format used here.
// But this format incorrectly uses the PLUS SIGN to mean milliseconds rather than offset from UTC/GMT.
// Very bad thing to do. Will create no end of confusion.
// Another bad thing: This code creates strings representing date-times in different time zones without indicating they are in different time zones.
// Time Zone list: http://joda-time.sourceforge.net/timezones.html
// "Atlantic/South_Georgia" is a time zone two hours behind UTC.
DateTimeZone southGeorgiaZone = DateTimeZone.forID( "Atlantic/South_Georgia" );
DateTimeFormatter formatter = DateTimeFormat.forPattern( "yyyy-MM-dd'T'HH:mm:ss+SSS" );
DateTime dateTimeInSouthGeorgia = formatter.withZone( southGeorgiaZone ).parseDateTime( "2012-12-12T08:26:51+000" );
DateTime dateTimeInUtc = dateTimeInSouthGeorgia.toDateTime( DateTimeZone.UTC );
String screwyBadPracticeDateTimeString = formatter.print( dateTimeInUtc );
System.out.println( "2012-12-12T08:26:51+000 in southGeorgiaDateTime: " + dateTimeInSouthGeorgia );
System.out.println( "same, in UTC: " + dateTimeInUtc );
System.out.println( "screwyBadPracticeDateTimeString: " + screwyBadPracticeDateTimeString );
When run…
2012-12-12T08:26:51+000 in southGeorgiaDateTime: 2012-12-12T08:26:51.000-02:00
same, in UTC: 2012-12-12T10:26:51.000Z
screwyBadPracticeDateTimeString: 2012-12-12T10:26:51+000
I'm exporting users from an ldap programmatically. Therefor I'm retrieving the users from ldap. One of the attributes is whenCreated.
One of the values I have to convert is: 20090813145607.0Z Directly splitting it up I get the following format: yyyyMMddHHmmss+.0Z. The problem is that the application is running in CET timezone and the time stored is UTC which is probably indicated by the .0Z . It is 14:56 UTC and the local representation is 16:56. For summer time it seems to be 2 hours and for winter time 1 hour.
I checked the SimpleDateFormat and there is a placeholder for the timezone, however its a different format.
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sdf.parse("20090813145607.0Z");
Will show the wrong date as it ignores the dates time zone.
Is there a way to convert it directly?
ISO 8601
As a couple of other Answers mentioned, the date-time format in question is defined by RFC 4517 Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules. See section 3.3.13, Generalized Time.
That section explains this LDAP format is a restricted version of one of the date-time formats defined by ISO 8601. This style using a minimum of separators is known as “basic” in ISO 8601.
In these formats, the Z on the end is short for Zulu and means UTC (basically same as GMT).
The decimal point and digit at the end represents a fraction of a second. Note that a comma is possible instead of the dot (period) in both RFC 4517 and ISO 8601. The comma is actually recommended over the dot in ISO 8601. The RFC 4517 spec allows for only a single digit fraction (some tenths of a fraction) or no dot/comma & digit at all. Note that in contrast: (a) ISO 8601 allows for any number of fractional digits, and (b) java.time objects have nanosecond resolution for up to nine digits of fractional second.
java.time
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
Now in maintenance mode, the Joda-Time project also advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time.
Parsing
Define a formatting pattern to fit RFC 4517. Study the DateTimeFormatter class for the pattern coding. This should work: uuuuMMddHHmmss[,S][.S]X. The square brackets mean optional. We accommodate either a dot or comma. Note the singular digit for fraction of second. The X on the end allows for either a Z or an offset-from-UTC such as -08 or -0830 or -08:30 or -083015 or -08:30:15.
String input = "20090813145607.0Z";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "uuuuMMddHHmmss[,S][.S]X" );
OffsetDateTime odt = OffsetDateTime.parse ( input , f );
Instant instant = odt.toInstant ();
Dump to console.
System.out.println ( "input: " + input + " | odt: " + odt + " | instant: " + instant );
input: 20090813145607.0Z | odt: 2009-08-13T14:56:07Z | instant: 2009-08-13T14:56:07Z
Of course you should also be coding a check for java.time.format.DateTimeParseException in case of unexpected input.
Checking the RFC mentioned above it seems like using UTC is the recommended default behavior for ldap dates. Therefor I converted it directly:
public Date parseLdapDate(String ldapDate){
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
return sdf.parse(ldapDate);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
What about using the split you described above, then reformatting the 0Z timezone into a standard format, then using sdf.parse(...)? Maybe something like this (with appropriate error checking added, of course):
String[] parts = inputDateTime.split("[.]");
String dateTimePart = parts[0];
String timeZonePart = "+0" + parts[1].substring(0, parts[1].length() - 1) + "00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssZ");
Date theDate = sdf.parse(dateTimePart + timeZonePart);
You can use the methods of org.apache.directory.shared.ldap.util.DateUtils:
String ldapDate="20090813145607.0Z";
Date date = DateUtils.parse(ldapDate);
String generalizedTime = DateUtils.getGeneralizedTime(date);
The syntax of the attribute is described in the directory schema. Applications must use the schema when converting, comparing, and ordering data that was retrieved from or stored in the directory. If the syntax of the whenCreated attribute is generalizedTime, then applications must use libraries for generalized time when converting. The syntax for generalizedTime is described in RFC4517.
This is the only piece of code that worked for me :
static String parseLdapDate(String ldapDate) {
long nanoseconds = Long.parseLong(ldapDate); // nanoseconds since target time that you want to convert to java.util.Date
long mills = (nanoseconds / 10000000);
long unix = (((1970 - 1601) * 365) - 3 + Math.round((1970 - 1601) / 4)) * 86400L;
long timeStamp = mills - unix;
Date date = new Date(timeStamp * 1000L); // *1000 is to convert seconds to milliseconds
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); // the format of your date
//sdf.setTimeZone(TimeZone.getTimeZone("GMT")); // give a timezone reference for formating (see comment at the bottom
String formattedDate = sdf.format(date);
return formattedDate;
}
I tried to use the apache util GeneralizedTime class http://directory.apache.org/api/gen-docs/1.0.0-M11/apidocs/org/apache/directory/shared/util/GeneralizedTime.html with mixed results
to convert from current time to Active Direcotry format:
GeneralizedTime gt = new GeneralizedTime(Calendar.getInstance());
String gtADString = gt.toGeneralizedTime(GeneralizedTime.Format.YEAR_MONTH_DAY_HOUR_MIN_SEC_FRACTION, GeneralizedTime.FractionDelimiter.DOT, 1, GeneralizedTime.TimeZoneFormat.Z).replaceFirst("Z", "\\.0Z");
The only problem is that it does not work as advertised. The length of the fraction portion after the dot is supposed to be "1" according to this call but the result still comes out as 3. Instead of "20120410011958.6Z" I get "20120410011958.687Z" so I still have to get the time in seconds and insert ".0" before the Z. So here's what you have to do (in my case I don't care about the fraction so I put zero. AD cares)
GeneralizedTime gt = new GeneralizedTime(Calendar.getInstance());
String gtADString = gt.toGeneralizedTime(GeneralizedTime.Format.YEAR_MONTH_DAY_HOUR_MIN_SEC, GeneralizedTime.FractionDelimiter.DOT, 1, GeneralizedTime.TimeZoneFormat.Z).replaceFirst("Z", "\\.0Z");
Incidentally this code converts from AD GeneralizedTime string format to Java Date
GeneralizedTime gt = new GeneralizedTime(str);
Date d = gt.getCalendar().getTime();