I have a string with both year-week e.g. "2015-40" and year-month formats e.g. "2015-08", that would like to transform into LocalDate in Scala.
I have tried using
val date = "2015-40"
val formatter = DateTimeFormatter.ofPattern("yyyy-ww")
LocalDate.parse(date, formatter)
but end up with DateTimeParseException error. Would appreciate any help on how to do this
Thanks in advance
String date = "2015-40";
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("YYYY-ww")
.parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.MONDAY.getValue())
.toFormatter(Locale.FRANCE);
LocalDate ld = LocalDate.parse(date, formatter);
System.out.println(ld);
Sorry that I can write only Java, I trust you to translate to Scala. Output is:
2015-09-28
Why were you getting an exception? There were a couple of things missing for us to parse 2015-40 into a LocalDate:
A LocalDate is a calendar date, and week 40 consists of 7 days. Java doesn’t know which of those 7 days you want and refuses to make the choice for you. In my code above I have specified Monday. Any other day of the week should work.
A bit subtler. While to a human year 2015 and week 40 is unambiguous, the same is not always the case around New Year where week 1 may begin before New Year or week 52 or 53 extend after New Year. So a calendar year and a week number don’t always define one specific week. Instead we need the concept of a week year or week-based year. A week year lasts from week 1 inclusive no matter if that means it begins a few days before or after New Year. And it lasts until the last day of the last week, again most often a few days before or after New Year. To tell a DateTimeFormatter that we want to parse (or print) the week year we need to use upper case YYYY instead of lower case yyyy (or uuuu).
As an aside, if you can influence the format, consider 2015-W40 with a W. This is ISO 8601 format for year and week. In ISO 2015-12 denotes year and month, and many will read it this way. So to disambiguate and avoid misreading.
Edit:
In my explanation I have assumed ISO week scheme (Monday is the first day of week and week 1 is defined as the first week having at least 4 days in the new year). You may pass a different locale to the formatter builder to obtain a different week scheme.
If you are sure that your weeks follow ISO, Andreas’ suggestion in the comment is good enough that we want as part of the answer:
Alternatively, add the ThreeTen Extra library, so you can use the
YearWeek
class, that has a nice atDay(DayOfWeek dayOfWeek)
method for getting a LocalDate.
Link: Wikipedia article: ISO 8601
LocalDate has three parts: year, month and day of the month. So, in the case of year-month string, you will have to get the LocalDate on a specific day as per your requirement. Parsing the year-month string is straight forward as shown in the demo code.
In the case of year-week string, you will have to get the LocalDate on a specific day of the week e.g. Mon or today's day etc. Also, instead of parsing the string directly, I found it easier to get the year and week and then use the methods LocalDate to get the required instance of LocalDate.
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.time.temporal.WeekFields;
public class Main {
public static void main(String[] args) {
//#################### Year-Month #######################
// Given year-month string
var yearMonthStr = "2015-08";
// LocalDate parsed from yearMonthStr and on the 1st day of the month
LocalDate date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atDay(1);
System.out.println(date2);
// LocalDate parsed from yearMonthStr and on the last day of the month
date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atEndOfMonth();
System.out.println(date2);
// LocalDate parsed from yearMonthStr and on specific day of the month
date2 = YearMonth.parse(yearMonthStr, DateTimeFormatter.ofPattern("u-M")).atDay(1).withDayOfMonth(10);
System.out.println(date2);
//#################### Year-Week #######################
// Given year-week string
var yearWeekStr = "2015-40";
// Split the string on '-' and get year and week values
String[] parts = yearWeekStr.split("-");
int year = Integer.parseInt(parts[0]);
int week = Integer.parseInt(parts[1]);
// LocalDate with year, week and today's day e.g. Fri
LocalDate date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week);
System.out.println(date1);
// LocalDate with year, week and next Mon (or same if today is Mon)
date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week)
.with(TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY));
System.out.println(date1);
// LocalDate with year, week and today's day previous Mon (or same if today is Mon)
date1 = LocalDate.now()
.withYear(year)
.with(WeekFields.ISO.weekOfYear(), week)
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
System.out.println(date1);
}
}
Output:
2015-08-01
2015-08-31
2015-08-10
2015-10-02
2015-10-05
2015-09-28
Related
I'm having issues with the below code displays Thursday as the dayOfTheWeek regardless of the date. Any ideas where I've gone wrong with this?
public void CreatePlan() {
editPlanName = findViewById(R.id.editPlanName);
String plan_name = editPlanName.getText().toString();
DatabaseManager db;
int day = datepicker.getDayOfMonth();
int month = datepicker.getMonth();
int year = datepicker.getYear();
SimpleDateFormat sdf = new SimpleDateFormat("EEEE");
Integer d_name = day;
Integer plan_recipe = 0;
Log.d("Date", String.valueOf(d_name));
String dayOfTheWeek = sdf.format(d_name);
String date = day + "/" + month + "/" +year;
db = new DatabaseManager(getApplicationContext());
Log.d("Recipe name", recipe_name);
db.createPlanRecipe(d_name, date, dayOfTheWeek, recipe_name);
db.createPlan(plan_name, plan_recipe);
}
… Any ideas where I've gone wrong with this?
day in your program is the day of the month from 1 to 31. Therefore d_name holds this number too.
Your SimpleDateFormat accepts formatting a number as a date and time expecting a count of milliseconds since the epoch of January 1, 1970 at 00:00 in UTC. So it will always format a date and time within the first 31 milliseconds after the epoch. Depending on your time zone the point in time that you format falls either on Wednesday, December 31, 1969 or on Thursday, January 1, 1970. So you will either always get Wednesday or always Thursday.
SimpleDateFormat.format(Object) accepts either a Date or a Number. Since Integer is a subclass of Number, it works as described.
The SimpleDateFormat class is notoriously troublesome, you have seen but a small corner of the problems that people often have with it. The Calendar class used in one other answer is poorly designed too. Both are long outdated. I suggest you look into java.time, the modern Java date and time API instead.
Further link: My answer to another question about getting the day of week from an Android date picker.
You are getting the value of the day-of-the-month, the month and the year in the following lines of code but you are not setting these values into the Calendar object which is supposed to give you other information (e.g. the day-of-week) by processing these values:
int day = datepicker.getDayOfMonth();
int month = datepicker.getMonth();
int year = datepicker.getYear();
So, before you try to get any other information from the Calendar object, set these values to the object as shown below:
// Set the picked values into an instance of Calendar
Calendar calendar = Calendar.getInstance();
calendar.clear();// Make sure to call this to reset all fields
calendar.set(year, month - 1, day);// Make sure to decrease month by 1
Now, your rest of code will work as you are expecting e.g. let's say you select 4 as the day-of-the-month, 10 as the month, and 2020 as the year, the following code will give you Sunday as the day-of-the-week.
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class Main {
public static void main(String[] args) {
int day = 4;
int month = 10;
int year = 2020;
// Set the picked values into an instance of Calendar
Calendar calendar = Calendar.getInstance();
calendar.clear();// Make sure to call this to reset all fields
calendar.set(year, month - 1, day);// Make sure to decrease month by 1
System.out.println(calendar.getTime());
// Your desired format
SimpleDateFormat sdf = new SimpleDateFormat("EEEE");
// The day-of-the-week for the specified date
String dayOfTheWeek = sdf.format(calendar.getTime());
System.out.println(dayOfTheWeek);
}
}
Output:
Sun Oct 04 00:00:00 BST 2020
Sunday
Note that I have decreased the month (picked from the date-picker) by 1 because java.util date-time API is based on 0 as the month of January.
A piece of advice:
I recommend you switch from the outdated and error-prone java.util date-time API and SimpleDateFormat to the modern java.time date-time API and the corresponding formatting API (from the package, java.time.format). Learn more about the modern date-time API from Trail: Date Time.
If your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
By using the modern date-time API:
import java.time.LocalDate;
import java.time.format.TextStyle;
import java.util.Locale;
public class Main {
public static void main(String[] args) {
int dayOfMonth = 4;
int month = 10;
int year = 2020;
// Instantiate a LocalDate object using the picked values
LocalDate date = LocalDate.of(year, month, dayOfMonth);
// The day-of-the-week for the specified date
String dayOfTheWeek = date.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH);
System.out.println(dayOfTheWeek);
}
}
Output:
Sunday
You have to create Date from your datepicker, then format it to find day like below:
int day = datePicker.getDayOfMonth();
int month = datePicker.getMonth();
int year = datePicker.getYear();
Calendar calendar = Calendar.getInstance();
calendar.set(year, month - 1, day);
SimpleDateFormat sdf = new SimpleDateFormat("EEEE");
dayOfTheWeek = sdf.format(calendar.getTime());
I am trying to create a method which converts DAY, MONTH, YEAR to Date object in Java
I have
String DAY = "31"
String YEAR = "2012"
String MONTH = "11"
I need a configurable Format as output.
String format = "MM/dd/yyy";
I will always get DAY string from "01" to "31" only
I will always get YEAR string from "1000" to "9999" only
I will always get MONTH string from "01" to "12" only (Never get Jan, Feb etc)
I understand that SimpleDateFormat(format) can work up to some extend.
new SimpleDateFormat("MM/dd/yyyy") will parse "02/01/2010" as Feb 01 2010 but
new SimpleDateFormat("mm/dd/yyyy") will parse "02/01/2010" as Jan 01 2010
Is it possible to write a generic method in java which converts given Strings (DAY, MONTH, YEAR) to Date object based on a configurable pattern?? and which can throw exception is I supply a wrong combination of DAY, MONTH, YEAR (like DAY = "31", MONTH = "02", YEAR = "2010")
Something like :
Date dateParser(String format){
String DAY = "01";
String YEAR = "1922";
String MONTH = "02";
return date;
}
There are basically two ways:
Parse each string to a number and put the numbers together to a date.
Put the strings together to one string and parse it into a date.
Some middle forms are thinkable, but I recommend you take a pure stance and use one of the ways only. The answer by Arvind Kumar Avinash shows option 1. My taste is rather for option 2., so let me demonstrate.
I recommend you use java.time, the modern Java date and time API for your date work.
String format = "MM/dd/yyy";
String day = "31";
String year = "2012";
String month = "11";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(format, Locale.US);
String isoDateString = year + '-' + month + '-' + day;
LocalDate date = LocalDate.parse(isoDateString);
String formattedDate = date.format(dateFormatter);
System.out.println(formattedDate);
Since there are only 30 days in November (month 11), this code very sensibly throws an exception:
Exception in thread "main" java.time.format.DateTimeParseException:
Text '2012-11-31' could not be parsed: Invalid date 'NOVEMBER 31'
That is, we have got input validation for free. For a valid date the code will parse the date and format as requested.
Link
Oracle tutorial: Date Time explaining how to use java.time.
I recommend you switch from the outdated and error-prone java.util date-time API and SimpleDateFormat to the modern java.time date-time API and the corresponding formatting API (package, java.time.format). Learn more about the modern date-time API from Trail: Date Time.
Note that mm is used for minute; not for month. For month, you use MM. Check this for more information.
Using the modern date-time API:
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(getLocalDate("2010", "02", "28"));
System.out.println(getLocalDate("2010", "02", "31"));
}
static LocalDate getLocalDate(String year, String month, String dayOfMonth) {
return LocalDate.of(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(dayOfMonth));
}
}
Output:
2010-02-28
Exception in thread "main" java.time.DateTimeException: Invalid date 'FEBRUARY 31'
at java.base/java.time.LocalDate.create(LocalDate.java:459)
at java.base/java.time.LocalDate.of(LocalDate.java:271)
at Main.getLocalDate(Main.java:11)
at Main.main(Main.java:7)
Note that a date-time object is supposed to store the information about date, time, time-zone etc. but not about the formatting. When you print an object of a date-time type, its is supposed to print what its toString method returns. If you need to print the date-time in a different format, you need a formatting class (e.g. DateTimeFormatter) which can return you a string in your desired pattern e.g.
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(getFormattedLocalDate("2010/02/28", "yyyy/MM/dd", "EEEE dd MMMM yyyy"));
System.out.println(getFormattedLocalDate("28/02/2010", "dd/MM/yyyy", "yyyy MMM EEE dd"));
}
static String getFormattedLocalDate(String date, String inputPattern, String outputPattern) {
return LocalDate.parse(date, DateTimeFormatter.ofPattern(inputPattern))
.format(DateTimeFormatter.ofPattern(outputPattern));
}
}
Output:
Sunday 28 February 2010
2010 Feb Sun 28
I need to get today’s date at midnight in my time zone in the format YYYYMMDDHHMMss.000Z as date and time in GMT.
I'm interested in the date, not the time of day.
In my system when I have got a date: 20201001220000.000Z it is a day 20201003 as summertime for GMT. I am asking this question because I need to compare the 20201001220000.000Z with today’s date.
I need to get the date ( TODAY midnight) in format yyyyMMddHHmmss.SSSZ. TODAY midnight 20201004 in my system is: 20201003220000.000Z this date will be compared with other dates e.g 20201002220000.000Z. The problem is that I can't get midnight.
java.time
Edit:
I tend to understand that you want to format today’s date into your format for comparison with other date-time strings in UTC (GMT) in the same format. For comparing dates and times I suggest that you compare date-time objects, not strings. So the options are two:
Parse the existing string, convert it to a date in your time zone and compare it to today’s date.
Parse your existing string into a point in time. Compare to the start of today’s date.
Let’s see both options in code.
1. Convert to date and compare dates:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuuMMddHHmmss.SSSX");
ZoneId zone = ZoneId.systemDefault();
LocalDate today = LocalDate.now(zone);
String gmtString = "20201001220000.000Z";
LocalDate dateFromGmtString = formatter.parse(gmtString, Instant::from)
.atZone(zone)
.toLocalDate();
if (dateFromGmtString.isAfter(today)) {
System.out.println(gmtString + " is in the future");
} else if (dateFromGmtString.isBefore(today)) {
System.out.println(gmtString + " was on a past date");
} else {
System.out.println(gmtString + " is today");
}
Output:
20201001220000.000Z was on a past date
2. Find start of today’s date and compare times
Instant startOfDay = LocalDate.now(zone).atStartOfDay(zone).toInstant();
String gmtString = "20201001220000.000Z";
Instant timeFromGmtString = formatter.parse(gmtString, Instant::from);
if (timeFromGmtString.isBefore(startOfDay)) {
System.out.println(gmtString + " was on a past date");
} else {
System.out.println(gmtString + " is today or later");
}
20201001220000.000Z was on a past date
Original answer
You may be after the following. I recommend that you use java.time, the modern Java date and time API, for your date and time work.
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("uuuuMMddHHmmss.SSSX");
String gmtString = "20201001220000.000Z";
Instant instant = sourceFormatter.parse(gmtString, Instant::from);
LocalDate date = instant.atZone(ZoneId.systemDefault()).toLocalDate();
String dayString = date.format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(dayString);
When I run the code in Europe/Warsaw time zone, the output is:
20201002
So the date has been converted from October 1 GMT to October 2 in Poland.
Edit:
… How can I get midnight?
To get the start of the day (usually 00:00):
ZonedDateTime startOfDay = time.atZone(ZoneId.systemDefault())
.truncatedTo(ChronoUnit.DAYS);
System.out.println(startOfDay);
2020-10-02T00:00+02:00[Europe/Warsaw]
Link: Oracle tutorial: Date Time explaining how to use java.time.
Try this.
String format = "yyyy-MM-dd HH:mm:ss.SSS O";
ZonedDateTime zdt = ZonedDateTime.now().withZoneSameInstant(ZoneId.of("GMT"));
System.out.println(zdt.format(DateTimeFormatter.ofPattern(format)));
Prints a US East coast time as
2020-10-03 14:47:18.809 GMT
You can delete the spaces, dashes and colons as desired. But do not use Date or related formatters as they are outdated. Use the classes from the java.time package.
You can do it as follows:
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Main {
public static void main(String[] args) {
// Today
LocalDate today = LocalDate.now();
// Midnight in the JVM's default time-zone
ZonedDateTime zdt = today.atStartOfDay(ZoneId.systemDefault());
// Printing zdt in its default format i.e. value returned by zdt.toString()
System.out.println(zdt);
// Printing zdt in your custom format
DateTimeFormatter customFormat = DateTimeFormatter.ofPattern("yyyyMMddHHmmss.SSSZ");
String dateTimeStrCustom = zdt.format(customFormat);
System.out.println(dateTimeStrCustom);
}
}
Output:
2020-10-04T00:00+01:00[Europe/London]
20201004000000.000+0100
Java code to create date from given day of week, week of month, month and year as input. Example- if the iputs are as below:
day-Monday, month-july, week-1, year-2018,
then output should be-02/07/2018.
Below is the code used:
System.out.println("Enter a year,month,week,day:");
int year = Integer.parseInt(obj.nextLine());
int month = Integer.parseInt(obj.nextLine());
int week = Integer.parseInt(obj.nextLine());
String day = obj.nextLine();
String date;
SimpleDateFormat dateFormat = new SimpleDateFormat ("yyyy/MM/dd");
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year); // set the year
cal.set(Calendar.MONTH, month-1); // set the month
cal.set(Calendar.WEEK_OF_MONTH, week);
//***error in the below line********
cal.set(Calendar.DAY_OF_WEEK,day);
date=dateFormat.format(cal.getTime());
System.out.println("Result:" +date);
The marked line won’t compile. Why not? How should I fix it?
The bit you seem to be missing is that when the user enters for example “Monday”, you need to convert this string into something that Java can understand as a day of week. This is done through parsing. Fortunately using java.time, the modern Java date and time API, it is not so hard (when you know how). This is what we use the dowFormatter for in the following code (dow for day of week):
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
DateTimeFormatter dowFormatter
= DateTimeFormatter.ofPattern("EEEE", Locale.getDefault(Locale.Category.FORMAT));
DayOfWeek dow = DayOfWeek.from(dowFormatter.parse(day));
WeekFields wf = WeekFields.of(Locale.getDefault(Locale.Category.DISPLAY));
LocalDate date = LocalDate.of(year, month, 15)
.with(dow)
.with(wf.weekOfMonth(), week);
System.out.println("Result: " + date.format(dateFormatter));
Now when I enter 2018, 7, 1 and Monday, I get:
Result: 2018/07/02
If you want to control in which language the user should enter the day of week, pass the appropriate locale to the formatter (instead of Locale.getDefault(Locale.Category.FORMAT)). If you want it to work in English only, you may use the shorter DayOfWeek dow = DayOfWeek.valueOf(day.toUpperCase(Locale.ROOT));, but it’s a bit of a hack.
If you want to control the week scheme used, pass the appropriate locale to WeekFields.of() or just specify WeekFields.ISO or WeekFields.SUNDAY_START.
I recommend that you don’t use SimpleDateFormat and Calendar. Those classes are long outdated. java.time is much nicer to work with.
Link: Oracle tutorial: Date Time explaining how to use java.time.
I have verified that the date is read correctly from a file, but once I use SimpleDateFormat.format with the pattern "dd/MM/yy" it suddenly adds a month. This leads me to believe lenient mode is calculating the wrong value. But I have no idea what would make it add a full month.
Some example dates I read:
16/09/2013
23/09/2013
30/09/2013
07/10/2013
14/10/2013
21/10/2013
The code used to parse the date (it's a wrapper around Calendar I made):
public static SimpleDateTime parseDate(String date)
{
String[] dateParts = date.split("[-\\.:/]");
int day = Integer.parseInt(dateParts[0]);
int month = Integer.parseInt(dateParts[1]);
int year = Integer.parseInt(dateParts[2]);
return new SimpleDateTime(dag, maand, jaar);
}
The constructor used here:
public SimpleDateTime(int day, int month, int year)
{
date = Calendar.getInstance();
date.setLenient(true);
setDay(day);
setMonth(month);
setYear(year);
}
The setters for day, month and year:
public void setYear(int year)
{
date.set(Calendar.YEAR, year);
}
public void setMonth(int month)
{
date.set(Calendar.MONTH, month);
}
public void setDay(int day)
{
date.set(Calendar.DAY_OF_MONTH, day);
}
And the code used to format the date:
public String toString(String pattern)
{
String output = new SimpleDateFormat(pattern, Locale.getDefault()).format(date.getTime());
return output;
}
where the pattern passed is:
"dd/MM/yy"
Intended to print a date as:
16/09/13
23/09/13
Instead I get:
16/10/13
23/10/13
January is 0 in Java; February is 1 and so on.
See Calendar.JANUARY, Calendar.FEBRUARY.
So when you're reading 1 from the file
you think you read JAN but you read FEB.
You should do: date.set(Calendar.MONTH, month-1); to fix this.
Months are indexed from 0 not 1 so 10 is November and 11 will be December.
Calendar.MONTH
From documentation:
Field number for get and set indicating the month. This is a calendar-specific value. The first month of the year is JANUARY; the last depends on the number of months in a year.
So if you check JANUARY you see it starts in zero.
Make sure your month is in the interval 0-11. Possibly it is in 1-12.
The reason for this is that the counting starts at 0.
January == 0
February == 1
and so on. See the documentation.
THe problem is that you pass 9 to SimpleDateFormat and since month are indexed from 0 to 11 it will parse month '9' as the 10th month.
You need to subtract 1 from the month :)
Calendar class in Java holds months starting from 0, hence when you set the month as 0, it would consider it as January. SimpleDateFormat provides for a way to correctly display the value as 01.
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, 0);
System.out.println(new SimpleDateFormat("dd/MM/yy").format(cal.getTime()));
Output:
29/01/14
The workaround for you to align you file that Calendar can work with (since December - or 12 would trickle over to the next year) or modify your logic to pick Constants like:
cal.set(Calendar.MONTH, Calendar.JANUARY);
The answer by peter.petrov is almost correct, except for one major problem. Like your question, it neglects to account for time zone.
For your information, this kind of work is much easier in Joda-Time (or new java.time.* classes in Java 8). Joda-Time is so much cleaner you won't even feel the need to create a wrapper class.
// Specify the time zone for which the incoming date is intended.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Brussels" );
String input = "16/09/2013";
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy").withZone( timeZone );
DateTime dateTime = formatter.parseDateTime( input );
String output = formatter.print( dateTime );
Dump to console…
System.out.println( "dateTime: " + dateTime );
System.out.println( "output: " + output );
System.out.println( "millis since Unix epoch: " + dateTime.getMillis() );
When run…
dateTime: 2013-09-16T00:00:00.000+02:00
output: 16/09/2013
millis since Unix epoch: 1379282400000