I have input with dates in many differing formats. I'm basically trying to take all the different formats and standardize them into ISO 8601 format.
If the date contains a month name, e.g., March, then I'm using the following function to get the month number, e.g. 03.
month = String.valueOf(Month.valueOf(month.toUpperCase()).getValue());
Anyway, the problem I am having is that the month names are in many different languages, with no indication what language they will be. I'm getting the following error when running the above function:
Caused by: java.lang.IllegalArgumentException: No enum constant java.time.Month.AUGUSTI
at java.lang.Enum.valueOf(Enum.java:238)
at java.time.Month.valueOf(Month.java:106)
Is there any library that can deal with month names in many different languages, returning the numeric value, or even just translating the month name to English?
Here's a sample of the input date:
1370037600
1385852400
1356994800
2014-03-01T00:00:00
2013-06-01T00:00:00
2012-01-01
2012
May 2012
März 2010
Julio 2009
If you have no idea in what language the month name is, the only way is to loop through all the available values of java.util.Locale, use a java.time.format.DateTimeFormatter and try to parse the month until you find one that works:
String input = "März 2010";
// formatter with month name and year
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("MMMM yyyy");
Month month = null;
for (Locale loc : Locale.getAvailableLocales()) {
try {
// set the locale in the formatter and try to get the month
month = Month.from(fmt.withLocale(loc).parse(input));
break; // found, no need to parse in other locales
} catch (DateTimeParseException e) {
// can't parse, go to next locale
}
}
if (month != null) {
System.out.println(month.getValue()); // 3
}
In the code above, the month will be Month.MARCH and the output will be 3.
Related
I have fetched the date month and year from the text file so now i want to fetch only month part and I have to get month name I have done like this
String s;
String keyword = "Facture du";
while ((s = br.readLine()) != null) {
if (s.contains(keyword)) {
// s= s.replaceAll("\\D+","");
System.out.println(s);
}
}
Actual Output: Facture du 28/05/2018
Expected Output: only Month name
Using java-8's LocalDate you can just do :
String strDate = "28/05/2018";
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate localDate = LocalDate.parse(strDate, format);
System.out.println(localDate.getMonth());
which gives the output as MAY
Nicholas K already provided an answer nicely showing the use of java.time. I just wanted to add that java.time can do a bit more than shown there.
DateTimeFormatter factureLineFormatter
= DateTimeFormatter.ofPattern("'Facture du' dd/MM/uuuu");
String keyword = "Facture du";
String s = "Facture du 28/05/2018";
if (s.contains(keyword)) {
LocalDate date = LocalDate.parse(s, factureLineFormatter);
Month month = date.getMonth(); // Extract a `Month` enum object.
String output =
month.getDisplayName( // Get localized name of month.
TextStyle.FULL, // How long or abbreviated should the month name be.
Locale.FRENCH) // `Locale` determines the human language used in translation, and the cultural norms used in abbreviation, punctuation, and capitalization.
;
System.out.println(output);
}
Output:
mai
I am parsing the entire line immediately by adding the literal text in quotes in the format pattern string. I am printing the localized month name — here in French, but you can choose another language. You may also choose to have it abbreviated if you prefer.
Edit: Basil Bourque has kindly edited my code spelling out in comments what each method and argument does. This makes the code look long, but is great for the explanation in a Stack Overflow answer. In production code you would probably use a one-liner:
System.out.println(date.getMonth().getDisplayName(TextStyle.FULL, Locale.FRENCH));
You could use Calendar from the java.utils package:
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date date = format.parse("28/05/2018");
Calendar cal = Calendar.getInstance();
cal.setTime(date);
System.out.println(cal.getDisplayName(Calendar.MONTH, Calendar.LONG_FORMAT, Locale.FRENCH));
I'm assuming you speak french and want to display the french name. Otherwise you will need to adjust the Locale parameter.
For your date this code will output "mai".
I'm trying to save a range of dates that will be filled in by the user, via 2 datepickers that were previously translated to Spanish.
The problem is that when I use strtotime() on the dates it takes the month as the day and vice versa.
Example:
I choose day 27 month 05 and year 2017, but the return value is an incorrect date format since the month is 27. If I choose day 01 month 05 year 2017 then it shows in the array as day 05 month 01 and year 2017.
Here are the functions I use to take the dates from the input texts, and to generate the range between the dates
function takedates() {
if(isset($_POST['repS'])){
$dateStart = $_POST['txtdesde'];
$dateEnd = $_POST['txthasta'];
$fechaArray = generafechas($dateStart,$dateEnd);
}
function generafechas($date1,$date2){
$fecharray = array();
if (is_string($date1) === true){
$deit1 = strftime("%d-%m-%Y",strtotime($date1));
}
if (is_string($date2) === true){
$date2 = strftime("%d-%m-%Y",strtotime($date2));
}
do {
$fecharray[] = date("m-d-Y", $date1);
$date1 = strtotime("+1 day", $date1);
} while($date1 <= $date2);
return $fecharray;
}
?>
My question is: How do i fill the array with the dates in the spanish date format?
PS: I've already used setLocale(LC_TIME,'es_ES') in the file where I'm using these functions, and the input shows the dates like this "dd/mm/yyyy"
strtotime does not take your locale into consideration when parsing the datetime string. If you use a date separated by slashes it is assumed to be American-style m/d/y. If you use a date separated by periods (or dashes if the year is four digits), it is assumed to be rest-of-the-world-style (d.m.y or d-m-Y). (Note that if you only use a two digit year and use dashes, PHP will try try to parse it as y-m-d.)
Instead of strtotime, you should use date-create-from-format / DateTime::createFromFormat to get a DateTime object, then build your date string from that.
UPDATE BASED ON COMMENTS: In order to get the output you want, you need to use the intl extension's IntlDateFormatter class to make the output.
To modify your code above (untested):
function generafechas($date1,$date2){
$fecharray = array();
if (is_string($date1) && is_string($date2)){
// These lines assume the input is formatted `day-month-year`,
// with 2-digit day and month, and 4-digit year.
$date1 = DateTime::createFromFormat('d-m-Y', $date1)
$date2 = DateTime::createFromFormat('d-m-Y', $date2)
} else {
throw new InvalidArgumentException('Must provide two date strings');
}
// Create the formatter
$formatter = IntlDateFormatter::create('es_ES', null, null, null, null, "d 'de' MMMM 'del' yyyy");
do {
// This line prints the first date in the format you chose above
$fecharray[] = $formatter->format($date1);
$date1->add(new DateInterval("P1D")); // `P1D` means "Duration of 1 Day" in the ISO 8601 standard
} while($date1 <= $date2);
return $fecharray;
}
If you provide the Locale along with the data, you can change what format string is used in createFromFormat as needed.
This code:
DateTimeParser[] parsers = { DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss zzz").getParser(),
DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss").getParser(), DateTimeFormat.forPattern("dd/MM/yyyy HH:mm").getParser(),
DateTimeFormat.forPattern("HH:mm").getParser() };
DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, parsers).toFormatter();
Session session;
DateTime dTime = null;
Calendar calendar;
try{
if (completedTime != null && !completedTime.equalsIgnoreCase("")){
LocalDateTime jt = LocalDateTime.parse(completedTime, formatter);
LocalDateTime dt;
LocalDateTime retDate;
produces the error:
java.lang.IllegalArgumentException: Invalid format: "09/05/2015 04:00:00 GDT" is malformed at " GDT"
at the LocalDateTime jt = LocalDateTime.parse(completedTime, formatter); line
I can't for the life of me work out why it is failing. I am pretty sure it is something simple, but I haven't spotted it.
You may want to refer to this thread (or one of the many others like it). My best advice would be to try cutting to only one "z" in your parser.
You need to manually specify a mapping from timezone abbreviation to timezone. For example:
return new DateTimeFormatterBuilder()
.appendPattern("dd/MM/yyyy HH:mm:ss ")
.appendTimeZoneShortName(UK_TIMEZONE_SYMBOLS)
.toFormatter();
Here UK_TIMEZONE_SYMBOLS is a Map<String,DateTimeZone> which contains our view of timezone names (so BST is British summer time, not Bangladesh standard time)
Here's how we build ours:
public static Map<String, String> buildTimeZoneSymbolMap(Locale locale) {
Map<String, String> timeZoneSymbols = Maps.newLinkedHashMap();
for (String[] zoneInfo : DateFormatSymbols.getInstance(locale).getZoneStrings()) {
String timeZone = zoneInfo[0];
if (!timeZoneSymbols.containsKey(zoneInfo[2])) {
timeZoneSymbols.put(zoneInfo[2], timeZone);
}
if (zoneInfo[4] != null && !timeZoneSymbols.containsKey(zoneInfo[4])) {
timeZoneSymbols.put(zoneInfo[4], timeZone);
}
}
timeZoneSymbols.put("UTC", "GMT");
return timeZoneSymbols;
}
public static Map<String, DateTimeZone> buildDateTimeZoneSymbolMap(Locale locale) {
return Maps.transformValues(buildTimeZoneSymbolMap(locale), input -> DateTimeZone.forTimeZone(TimeZone.getTimeZone(input)));
}
public static final Map<String, DateTimeZone> UK_TIMEZONE_SYMBOLS = ImmutableMap.copyOf(buildDateTimeZoneSymbolMap(Locale.UK));
First thing to note:
What is "GDT"? The website http://www.timeanddate.com/time/zones/ does not yield an answer. So if it really exists and is not a typo then what is your locale? Remember that time zone names and abbreviations are highly localized.
Second: The count of pattern symbols "z" is okay - for classes like SimpleDateFormat etc. - see its documentation. Either four letters for the full name or less than four letters for the abbreviation:
General time zone: Time zones are interpreted as text if they have
names. Text: For formatting, if the number of pattern letters is 4 or
more, the full form is used; otherwise a short or abbreviated form is
used if available. For parsing, both forms are accepted, independent
of the number of pattern letters.
But you use Joda-Time. Its documentation clearly states:
Zone names: Time zone names ('z') cannot be parsed.
I have verified this non-support using the newest Joda-Time version 2.7 by following code:
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss z").withLocale(Locale.GERMANY);
DateTime dt = formatter.parseDateTime("09/05/2015 04:00:00 MESZ");
System.out.println("Joda-Time: " + dt);
// Exception in thread "main" java.lang.IllegalArgumentException: Invalid format: "09/05/2015 04:00:00 MESZ" is malformed at "MESZ"
Of course, "MESZ" is correct and must be interpreted as Europe/Berlin in context of given german locale.
However, since version update (2.2) the same code set to Locale.US works for some timezones names like "EDT", "PST" etc., see also this commit. So we can finally say, the parsing support of Joda-Time for timezone names and abbreviations is best to say very limited. Once again, what is your Locale? If it is not US then I can understand why you get the exception. And you will also get an exception for the input "GDT" even if we consider it as valid due to the limited capabilities of Joda-Time-parser.
I have following code which checks for the valid format date
private void validateDate(String date){
try {
String validDate = "MM/dd/yyyy";
SimpleDateFormat format = new SimpleDateFormat(validDate);
format.setLenient(false);
Date theDate = new Date();
theDate = format.parse(date);
}
catch (Exception e) {
}
}
I am passing the date value as 06/25/20014. In this year format is wrong and I was expecting that it will throw exception and will go inside catch, but it never happens and it successfully passes the code format.parse(date); due to which my application is not throwing error.
I also debugged the line format.parse(date); and it returns "Fri Jul 31 00:00:00 MST 20015". I am not sure why that line is not throwing any exception.
Thanks for your help!!
In the standard date formats for SimpleDateFormat, the number 'y' 's doesn't necessarily correspond to the number of digits (it isn't a regex). One or two y's is an indicator for a 2 digit year (15, 98, etc.) and 3 or more y's is an indicator for the full year.
If this line did throw an exception, then your program would stop working after year 9999, which is not usually what you want. Sure, you do not expect your program to last this long, but people did not expect to last up to y2k either; so Java's choice not to block at any date seems reasonable.
If you want to check the year is between 1 and 9999, you can just write a if: (see related question I want to get Year, Month, Day, etc from Java Date to compare with Gregorian Calendar date in Java. Is this possible?)
Calendar cal = Calendar.getInstance();
cal.setTime(date);
if (cal.get(Calendar.YEAR) > 9999)
throw new RuntimeException("Blah");
This is actually documented behaviour (at least for the Gregorian calendar, which I assume you will be using unless you explicitely set it to a different calendar):
Year: If the formatter's Calendar is the Gregorian calendar, the
following rules are applied.
For formatting, if the number of pattern
letters is 2, the year is truncated to 2 digits; otherwise it is
interpreted as a number. For parsing, if the number of pattern letters
is more than 2, the year is interpreted literally, regardless of the
number of digits.
And yes, 20015 will probably be a valid year some day ;) If you want to check for exactly 4 digits, you might want to consider using a regular expression matching before parsing.
I can see that the provided date is valid (although 18k years ahead)
public static void validateDate(String date){
try {
String validDate = "MM/dd/yyyy";
SimpleDateFormat format = new SimpleDateFormat(validDate);
format.setLenient(false);
Date theDate = format.parse(date);
Calendar c = new GregorianCalendar();
c.setTime(theDate);
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
int pos = c.get(Calendar.DAY_OF_WEEK);
if (Calendar.WEDNESDAY == pos)
System.out.print("day is Wednesday on " + year + "/" + (month+1) + "/" + day);
}
catch (Exception e) {
System.out.print("Inside exception block " + e);
}
}
prints: day is Wednesday on 20014/6/25
If you need some year validation you can add additional check
if (year > MAX_YEAR) {
// add error here
}
Here is DEMO
I have a date as APR-JUN10 or APR-JUN 2010 and i need output as 2010-06-30 I need the best way to parse above date in java and should be flexible in adding more such format of dates. note: APR-JUN10 will not parse by any java api, we have to break down APR & JUN 10 and get date as 2010-06-30.
You need to firm up your requirements.
Currently all you have told us is that APR-JUN 2010 should translate to the last day of June.
But what about FEB-JUN 2010? Should that also translate to the last day of June? Or should it throw a parse exception due to not being a full quarter? What about JUL-JUN 2010, where the second month is before the first? What about MAY-JUL 2010 -- three months but perhaps your definition of "quarter" requires starts of January, April, July, October.
Once you have your own requirements down, you can get to work on the conversion.
It's unlikely that an existing DateFormat implementation will do this exact task for you. You're likely to need to parse the string in your own code.
If the only legal options are JAN-MAR, APR-JUN, JUL-SEP, OCT-DEC, then you just have a five-way switch statement to set the month and day on a Calendar object (the fifth way being a default: case that throws an exception.
If your requirement is more complex, then your code will need to be more complex. Breaking the string into parts using a regex would be a good first step.
Pattern patt = Pattern.compile("(.{3})-(.{3}) (\d+)");
Matcher matcher = patt.matcher(qaurterString);
if(! matcher.find() || m.groupCount() != 3) {
throw new ParseException(...)
}
String fromMonth = matcher.group(1);
String toMonth = matcher.group(2);
int year = Integer.parseInt(matcher.group(3));
I think you'll have to write parsing code from scratch, whatever you do. The neatest end result would for you to create a class that implements DateFormat.
String s = "APR-JUN10";
// validation of quarter part
String quarter = s.substring(0, 7);
if (
!quarter.equals("JAN-MAR") && !quarter.equals("APR-JUN")
&& !quarter.equals("JUL-SEP") && !quarter.equals("OCT-DEC")
) {
throw new IllegalArgumentException("Input is not a quarter date: " + s);
}
// text processing with preprocessing hack (substring(4))
SimpleDateFormat inputFormat = new SimpleDateFormat("MMMyy", Locale.ENGLISH);
inputFormat.setLenient(false);
Date date = inputFormat.parse(s.substring(4));
System.out.println(date);
// Output: Tue Jun 01 00:00:00 CEST 2010 [format chooses 1 as default day-of-month]
// Go to end of month/quarter
GregorianCalendar gcal = new GregorianCalendar();
gcal.clear();
gcal.setTime(date);
gcal.set(Calendar.DAY_OF_MONTH, gcal.getActualMaximum(Calendar.DAY_OF_MONTH));
// format as ISO-date
SimpleDateFormat outputFormat = new SimpleDateFormat("yyyy-MM-dd");
String output = outputFormat.format(gcal.getTime());
System.out.println(output); // 2010-06-30
For the input "APR-JUN 2010" you need the input format pattern "MMM yyyy", else the solution is the same. Of course, the proposed solution assumes that every input starts with JAN-MAR, APR-JUN, JUL-SEP or OCT-DEC (you wrote about quarters). If you want you can validate it before processing phase by mean of s.substring(0, 7) etc.
UPDATE: I have now added the validation feature, see code.