Setting Time in Excel using POI - apache-poi

I am trying to create an Excel Work sheet using POI api in Java. In that Excel Work Sheet I want to have a cell with TIME alone. By setting this we can include the cell in summation of that particuluar column as we do in number columns. For this we need to format the cell as Time >> 13:30:55. (The internal format is 'h:mm:ss;#' ). And we need to remove the date part from the cell.
When I read the cell the cell value using the POI, it is returning as 'Sun Dec 31 01:00:00 IST 1899' ( When i set the value as 1:00 ), the cell format index is 166 and the cell format string is 'h:mm:ss;#'.
After setting the formats and style which were read from the excel and the cell value as 1800-December-31 and with the time value, the new excel shows cell as '######' (error) and cell value is setted as '-1'. Below is the code I have used. Did I miss anything ? Is it possible to set the value as I required.
InputStream is = new BufferedInputStream(new FileInputStream("<FileName>"));
XSSFWorkbook wb = new XSSFWorkbook(is);
is.close();
XSSFSheet sheet = wb.getSheetAt(0);
XSSFRow row = sheet.getRow(2);
XSSFCell cell = row.getCell(18);
System.out.println("ExcelFileReader main cell.getDateCellValue() : '" + cell.getDateCellValue() + "'");
System.out.println("ExcelFileReader main cell.getCellStyle().getDataFormat() : '" + cell.getCellStyle().getDataFormat() + "'");
System.out.println("ExcelFileReader main cell.getCellStyle().getDataFormat() : '" + cell.getCellStyle().getDataFormatString() + "'");
XSSFRow row1 = sheet.createRow(21);
XSSFCell cell1 = row1.createCell(2);
cell1.setCellStyle(cell.getCellStyle());
cell1.setCellValue(cell.getDateCellValue());
Calendar dummy = Calendar.getInstance();
dummy.setLenient(false);
dummy.set(Calendar.YEAR, 1899);
dummy.set(Calendar.MONTH, Calendar.DECEMBER);
dummy.set(Calendar.DATE, 31);
dummy.set(Calendar.HOUR, 00);
dummy.set(Calendar.MINUTE, 00);
dummy.set(Calendar.SECOND, 00);
dummy.set(Calendar.MILLISECOND, 00);
Calendar cc = Calendar.getInstance();
XSSFRow row2 = sheet.createRow(25);
XSSFCell cell2 = row2.createCell(2);
dummy.set(Calendar.HOUR, cc.get(Calendar.HOUR));
dummy.set(Calendar.MINUTE, cc.get(Calendar.MINUTE));
dummy.set(Calendar.SECOND, cc.get(Calendar.SECOND));
dummy.set(Calendar.MILLISECOND, cc.get(Calendar.MILLISECOND));
System.out.println("ExcelFileReader main dummy : '" + dummy.getTime() + "'");
cell2.setCellValue(dummy.getTime());
CellStyle style = wb.createCellStyle();
DataFormat df = wb.createDataFormat();
style.setDataFormat(df.getFormat("[h]:mm:ss;#"));
cell2.setCellStyle(style);
FileOutputStream fos = new FileOutputStream(new File("<New Excel file>"));
wb.write(fos);
fos.close();
System.out.println("ExcelFileReader DONE");
The following is the output of the program.
ExcelFileReader main cell.getDateCellValue() : 'Sun Dec 31 00:15:00 IST 1899'
ExcelFileReader main cell.getCellStyle().getDataFormat() : '166'
ExcelFileReader main cell.getCellStyle().getDataFormat() : 'h:mm:ss;#'
ExcelFileReader main dummy : 'Sun Dec 31 11:32:24 IST 1899'
ExcelFileReader DONE

I have used the following code to generate the time cell, in Excel using POI. And I am able to use the cell in
XSSFRow row2 = sheet.createRow(25);
XSSFCell cell1 = row2.createCell(4);
XSSFCell cell2 = row2.createCell(5);
CellStyle style = wb.createCellStyle();
DataFormat df = wb.createDataFormat();
style.setDataFormat(df.getFormat("[h]:mm:ss;#"));
cell1.setCellFormula("TIME(0,15,00)"); // 00:15:00
cell1.setCellType(Cell.CELL_TYPE_FORMULA);
cell1.setCellStyle(style);
evaluator.evaluateFormulaCell(cell1);
cell2.setCellFormula("TIME(0,30,00)"); //00:30:00
cell2.setCellType(Cell.CELL_TYPE_FORMULA);
evaluator.evaluateFormulaCell(cell2);
cell2.setCellStyle(style);

Dates and Times in Excel are normally stored as floating point numbers, as full and fractional days since 1900 or 1904. You can see this by typing 0.5 into an excel cell and formatting as a time - it'll show as 12:00:00, or set a value of 1.5 and it'll show as a date in 1900/1904 as a date, 12:00:00 as a time, or 36:00:00 if a time that allows >24 hours (it's a different format code)
What you'll want to do is fire up a copy of Excel, type in the time value you want to have displayed, and work out the format string that makes it show up as you want it to. Then, make a note of that format code, and give that to POI as the data format for the cell. Now, use something like DateUtil in POI to build up your Date object, and set that to the cell as the value. With the right format string in, Excel will show only the time part and not the date

You can use DateUtil.convertTime(String time) method where time is like "HH:MM" or "HH:MM:SS". This method will give you a double value which you can set as value in your cell (cell.setCellValue(double value)).
But convertTime method doesn't work with hours > 23. If you need this opportunity, you can write your own convertTime method, somthing like this:
double convertTime(long hours, long minutes, long seconds) {
double totalSeconds = seconds + (minutes + (hours) * 60) * 60;
return totalSeconds / (SECONDS_PER_DAY);
}
SECONDS_PER_DAY = 60 * 60 * 24. You can simply take it from DateUtil class.
Remember, you must define your cell format properly that excel can put in hours more than 23.

Related

Excel, find date based on day name in cell

I have table:
I'm trying to find dates of specific day (here: Monday) in specific month in 5 cells below DATE cell.
For example, Mondays in March - 6.03.2017, 13.03.2017, 20.03.2017, 27.03.2017,
I'm "playing" with it for about 2h, trying week.day, mod, choose and anything iC can find in Google but no luck.
Any ideas?
This kind of task can be done more easily by dividing steps. I made an example sheet. When you input some values in yellow cells, output(calculated by formulae) will be displayed in green cells. What you really need is weekday function, plus I used date, join, day, etc.
Of course, this can be done via VBA script or Apps Script, which can be undoubtedly much more elegant. I added the script to the above sheet. The red cells are filled by this custom function. Its logic is almost the same as the flow of the sheet.
function getDaysWithNames(year, month, name) {
name = name.slice(0,3).toUpperCase();
var t;
//get the first day
var day;
for(day=1; day<8; day++) {
t = new Date(year, month-1, day).toDateString().slice(0,3).toUpperCase();
if(t == name) break;
}
//first three days are always valid because they are <= 21
var validDays = [day, day+7, day+14];
//test the fourth day
day += 21;
t = new Date(year, month-1, day).getDate();
if(t == day) validDays.push(day); else return validDays;
//test the fifth day
day += 7;
t = new Date(year, month-1, day).getDate();
if(t == day) validDays.push(day);
return validDays;
}

Number and cell Formatting in apache poi

I am creating excel sheet using apache poi. I have numbers like - 337499.939437217, which I want to show as it is in excel without rounding off. Also the cell format should be number (for some columns) and currency (for some columns).
Please suggest which BuiltinFormats should I use to achieve this.
Many thanks for the help.
At first you need to know how to use DataFormats. Then you need to know the guidelines for customizing a number format.
For your number -337499.939437217 which will be displayed rounded with general number format, you could use format #.###############. The # means a digit which will be displayed only if needed (is not leading zero and/or is not zero as last decimal digit) - see guidelines. So the whole format means show up to 15 decimal digits if needed but only as much as needed.
For currency you should really using a built in number format for currency. So the currency symbol depends on the locale settings of Excel. The following BuiltinFormats are usable with apache poi. Using a built in number format you need only the hexadecimal format numbers.
Example:
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class CreateNumberFormats {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("format sheet");
CellStyle style;
DataFormat format = wb.createDataFormat();
Row row;
Cell cell;
short rowNum = 0;
short colNum = 0;
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-337499.939437217); // general format
style = wb.createCellStyle();
style.setDataFormat(format.getFormat("#.###############")); // custom number format
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-337499.939437217);
cell.setCellStyle(style);
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(123.456789012345);
cell.setCellStyle(style);
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(123456789.012345);
cell.setCellStyle(style);
style = wb.createCellStyle();
style.setDataFormat((short)0x7); // builtin currency format
row = sheet.createRow(rowNum++);
cell = row.createCell(colNum);
cell.setCellValue(-1234.5678);
cell.setCellStyle(style);
sheet.autoSizeColumn(0);
FileOutputStream fileOut = new FileOutputStream("CreateNumberFormats.xlsx");
wb.write(fileOut);
fileOut.close();
wb.close();
}
}

Making Google sheets for entries made today, this week, and this month

I have a Google sheet that pulls information from a form. The form inputs a timestamp and IDnumber into a spreadsheet. I need to make different sheets to show which of these entries were made Today, This week, and This month.
Example
Timestamp ID
12/1/2012 12345
12/1/2012 55555
12/4/2012 98765
12/15/2012 74823
I need to make a sheet that puts ID 12345 and 55555 into one "DAILY" Sheet, IDs 12345, 55555, and 98765 into a "WEEKLY" sheet, and one that has all of these in a "MONTHLY" sheet.
Here's what I would do...
Add today's date somewhere in the sheet (let's say cell D2) by inputting '=Today()' in D2.
In column C, next to your first two columns, add formulas to calculate the number of days between the date in column A and today's date. In row 2, the formula would look like this...
=DATEDIF(A2,D2,"D")
Then, using the script editor in the spreadsheet, I would write a script that looks at column C and writes values from column A and B depending on that value. I haven't tested the script below, but I think it's what you would need. Essentially, it loops through column C of your results sheet. Depending on the value (days from today), that row is pushed to an array for each range. Then I cleared the sheet for that range before setting the values of the array. I hope this helps!
function daysBetween(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet(),
formResultsSheet = spreadsheet.getSheetByName("Form Results"),
daySheet = spreadsheet.getSheetByName("Today"),
weekSheet = spreadsheet.getSheetByName("This Week"),
monthSheet = spreadsheet.getSheetByName("This Month"),
allData = formResultsSheet.getDataRange().getValues(),
dayResults = [],
weekResults = [],
monthResults = [];
for (var i = 0; i < formResultsSheet.getLastRow(); i++) {
if (allData[i][2] === 0){
dayResults.push(allData[i])
} else if (allData[i][2] > 0 && allData[i][2] <= 7) {
weekResults.push(allData[i])
} else if (allData[i][2] > 7 && allData[i][2] <= 30) {
monthResults.push(allData[i])
}
}
daySheet.getDataRange().clear()
daySheet.getRange(1,1,dayResults.length,dayResults[0].length).setValues(dayResults)
weekSheet.getDataRange().clear()
weekSheet.getRange(1,1,weekResults.length,weekResults[0].length).setValues(weekResults)
monthSheet.getDataRange().clear()
monthSheet.getRange(1,1,monthResults.length,monthResults[0].length).setValues(monthResults)
}

Using POI to write date (without time)

When i write out a date using POI, I also see the time in Excel. (the time shown is the time on my machine when the cell was written).
This is the code that i have used
XSSFCellStyle dateStyle = wb.createCellStyle();
CreationHelper createHelper = wb.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("dd-mmm-yyyy"));
Calendar cal = Calendar.getInstance();
cal.set(xx.getYear(), xx.getMonthOfYear(), xx.getDayOfMonth());
cell.setCellStyle(dateStyle);
cell.setCellValue(cal);
the date in the cell is correct i.e 12-Dec-2013 but for that cell, IN THE FORMULA bar, the time also shows. So cell shows 12-Dec-2013 and the formula bar show 12-Dec-2013 7:14:39AM. I checked the format of the cell in excel and it shows as custom dd-mm-yyyy, which is what i expect.
Just too be clear - the cell itself show 12-12-2012 but for that cell in the formula bar the time also shows.
I also replaced Calendar with Date - same issue.
Addl info: In excel i changed the format of the col to 'general' - for the cells that were addined in by POI, i see that the values is xxx.xxx like 41319.3769490278, while when i just enter the date by hand the value looks something like 41319. It looks like the digits after the decimal point is causing the time to show up. Not sure how to avoid this when i use POI to write it out
Ok solved. Putting this out there for others who run into the same problem.
i looked into the POI source code and realized that from the calendar, a double is computed, and the cell value is set to that. From the comments its clear that the digits after the decimal point represent the time. So all i did in my code is to truncate that double. The changed lines are commented in the code snippet below.
XSSFCellStyle dateStyle = wb.createCellStyle();
CreationHelper createHelper = wb.getCreationHelper();
dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("dd-mmm-yyyy"));
Calendar cal = Calendar.getInstance();
cal.set(xx.getYear(), xx.getMonthOfYear(), xx.getDayOfMonth());
cell.setCellStyle(dateStyle);
double d = DateUtil.getExcelDate(cal, false); //get double value f
cell.setCellValue((int)d); //get int value of the double
You're not fully clearing the Calendar instance you're getting the date from, that's why the time is coming through
You need to also set the time values to zero, so your code would want to look something like:
Calendar cal = Calendar.getInstance();
cal.set(xx.getYear(), xx.getMonthOfYear(), xx.getDayOfMonth());
calendar.set(Calendar.HOUR, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);

Excel time dd/hh/mm conversion to minutes only

How do I convert turn around time written in an Excel cell as (0D/1H/6), meaning 0 days, 1 hour and 6 minutes to minutes only in another or adjacent Excel cell. I would like to automate that to make the conversion easy. Thanks.
It is possible using some string functions. The code below calculates the minutes, given a cell (A1 in this case) with a string formatted the way you specified:
=VALUE(LEFT(A1;FIND("D/";A1)-1))*24*60 + VALUE(MID(A1;FIND("D/";A1)+2;FIND("H/";A1)-FIND("D/";A1)-2))*60 + VALUE(RIGHT(A1;LEN(A1)-FIND("H/";A1)-1))
For instance, in your case "0D/1H/6" gives 66.
As I dont think thats a format Excel natively understands I would use a VBA formula for this.
Alt+F11, Insert -> Module & add
Public Function TOMINS(value As String) As Long
Dim parts() As String: parts = Split(value, "/")
If (UBound(parts) = 2) Then
TOMINS = Val(parts(0)) * 1440
TOMINS = TOMINS + Val(parts(1)) * 60
TOMINS = TOMINS + Val(parts(2))
End If
End Function
Then if the value is in A1 & you want the minutes in B1, make B1 =TOMINS(A1)

Resources