How do I write a locale-aware DateTime sensibly into Spreadsheet::WriteExcel? - excel

I have got a DateTime object that is in a specific locale (with a DateTime::Locale object attached to it). I want to write a date string to an XLS file using Spreadsheet::WriteExcel, but I want the output that's visible to user of the Excel file to be of the same locale as the one attached to my DateTime object.
There is some documentation on this matter within Spreadsheet::WriteExcel. It's possible to set formats using a combination of a string, $wb->add_format() and $ws->write_date_time().
I can get locale information from DateTime via DateTime::Locale by looking at the CLDR patterns. There are also the named formats, which are easier to use. Something like $locale->date_format_short is actually quite nice.
use DateTime::Locale;
say DateTime::Locale->load('en_GB')->date_format_short; # dd/MM/y
say DateTime::Locale->load('en_US')->date_format_short; # M/d/yy
Now the problem with this is, that Excel does not know what a single y means. So my workaround has been to just replace a single y with yy, as that seems to roughly be the same.
Excel also doesn't like upper case letters in the format. I have no idea how it distinguishes between minutes and months, but it works.
This example seems to work, but I am sure it's not perfect.
use strict;
use warnings;
use Spreadsheet::WriteExcel;
use DateTime;
use DateTime::Locale;
my $workbook = Spreadsheet::WriteExcel->new("date_time.xls");
my $worksheet = $workbook->add_worksheet();
# Write the column headers
$worksheet->write('A1', 'Formatted date');
$worksheet->write('B1', 'Format');
$worksheet->write('C1', 'Locale');
my $row = 0;
for my $locale (qw/en_GB en_US de_DE ko_KR/) {
$row++;
my $format_string =
lc(DateTime::Locale->load($locale)->date_format_short)
=~ s{
(?<!y) # 2. not preceded by a y
y # 1. a single y
(?!y) # 3. not followed by a y
}{yy}xr; # 4. replaced with two y
my $format = $workbook->add_format(num_format => $format_string);
$worksheet->write_date_time($row, 0, DateTime->now->datetime, $format);
$worksheet->write($row, 1, $format_string);
$worksheet->write($row, 2, $locale);
}
This produces the following Excel file.
They all work, but the code is smelly. Is there something I've overlooked? Maybe someone has written a more correct converter for these format strings that I've not seen yet.
Please note that DateTime::Format::Excel is not helpful as it only works the other way around, turning Excel dates into DateTime objects.

Related

Converting a string to float in Perl

I'm new to Perl. I am reading a CSV file using Perl. The first column of the CSV is time (which is a float). I've read the CSV and displayed the contents of the CSV successfully. Further, I wish to use the CSV data for some computations. I need the time column as an array (or any data structure). On reading the time column and storing it in an array, it is stored as a string. I wish to have a numeric array for arithmetic computations.
I've tried adding 0, mul 1 and then storing it in the array,using sprintf but i'm encountering errors.
use v5.30.0;
use strict;
use warnings;
my $file = $ARGV[0] or die;
open(my $data, '<',$file) or die;
my #timeArray;
while(my $line = <$data>){
chomp $line;
my #words = split ",",$line;
#my $temp=$words[1]*1;
my $temp=sprintf "%.6f",$words[1];
push #timeArray,$temp;
}
Error:
Argument ""67.891947295"" isn't numeric in multiplication (*) at 3.pl line 12, <$data> line 19556.
and
Argument ""67.840034174"" isn't numeric in sprintf at 3.pl line 13, <$data> line 19555.
Also, why is the argument in "" "" .
It's a good idea to handle data like that with the proper module, because there are several important details that you didn't take care of. Examples:
The columns values may be enclosed in quotes
The first row may contain the header names of each column
The last record in the file may or may not have an ending line break
Etc.
Read the RFC-4180 document for more information.
There are lots of modules that can parse CSV format, for example: Text:CSV. It's very easy to install, and when you use it, your string to double problem will disappear.

Replacing a certain part of string with a pre-specified Value

I am fairly new to Puppet and Ruby. Most likely this question has been asked before but I am not able to find any relevant information.
In my puppet code I will have a string variable retrieved from the fact hostname.
$n="$facts['hostname'].ex-ample.com"
I am expecting to get the values like these
DEV-123456-02B.ex-ample.com,
SCC-123456-02A.ex-ample.com,
DEV-123456-03B.ex-ample.com,
SCC-999999-04A.ex-ample.com
I want to perform the following action. Change the string to lowercase and then replace the
-02, -03 or -04 to -01.
So my output would be like
dev-123456-01b.ex-ample.com,
scc-123456-01a.ex-ample.com,
dev-123456-01b.ex-ample.com,
scc-999999-01a.ex-ample.com
I figured I would need to use .downcase on $n to make everything lowercase. But I am not sure how to replace the digits. I was thinking of .gsub or split but not sure how. I would prefer to make this happen in a oneline code.
If you really want a one-liner, you could run this against each string:
str
.downcase
.split('-')
.map
.with_index { |substr, i| i == 2 ? substr.gsub(/0[0-9]/, '01') : substr }
.join('-')
Without knowing what format your input list is taking, I'm not sure how to advise on how to iterate through it, but maybe you have that covered already. Hope it helps.
Note that Puppet and Ruby are entirely different languages and the other answers are for Ruby and won't work in Puppet.
What you need is:
$h = downcase(regsubst($facts['hostname'], '..(.)$', '01\1'))
$n = "${h}.ex-ample.com"
notice($n)
Note:
The downcase and regsubst functions come from stdlib.
I do a regex search and replace using the regsubst function and replace ..(.)$ - 2 characters followed by another one that I capture at the end of the string and replace that with 01 and the captured string.
All of that is then downcased.
If the -01--04 part is always on the same string index you could use that to replace the content.
original = 'DEV-123456-02B.ex-ample.com'
# 11 -^
string = original.downcase # creates a new downcased string
string[11, 2] = '01' # replace from index 11, 2 characters
string #=> "dev-123456-01b.ex-ample.com"

How to compare date in linux for specific format

I want to compare date 08-08-2018 and 20-07-2018.
if (dt1 > $ dt2){
Success
}
Please help me.
The core
Time::Piece module
allows you to convert a date/time string to an object using the strptime class method. The resulting objects can be compared using the standard <, >, <=, >=, and == operators as you describe, and being a core module it is unlikely to need installing
Here's a short program that uses the values in your question, converts them to Time::Piece objects $dt1 and $dt2, and compares them as you describe
use strict;
use warnings 'all';
use feature 'say';
use Time::Piece;
my ($dt1, $dt2) = map { Time::Piece->strptime($_, '%d-%m-%Y') } qw/ 08-08-2018 20-07-2018 /;
if ( $dt1 > $dt2 ) {
say 'Success';
}
else {
say 'Failure';
}
output
Success
If your date formats are static, you have two easy options.
Option 1
Strip out the day, month, and year from each date.
Compare the two years.
If the years are the same, compare the two months.
If the months are the same, compare the two days.
Option 2
Strip out the day, month, and year from each date.
Create a new date string in the YYYY-MM-DD format
Do a standard string comparison on the two resulting strings.
I'm sorry, but I'm not in a position to provide you with sample code at this time.

How do I subtract two arrays of cells in Matlab

I am trying to get some variables and numbers out from an Excel table using Matlab.
The variables below named "diffZ_trial1-4" should be calculated by the difference between two columns (between "start" and "finish"). However I get the error:
Undefined operator '-' for input arguments of type"
'cell'.
And I have read somewhere that it could be related to the fact that I get {} output instead of [] and maybe I need to use cell2mat or convert the output somehow. But I must have done that wrongly, as it did not work!
Question: How can I calculate the difference between two columns below?
clear all, close all
[num,txt,raw] = xlsread('test.xlsx');
start = find(strcmp(raw,'HNO'));
finish = find(strcmp(raw,'End Trial: '));
%%% TIMELINE EACH TRIAL
time_trial1 = raw(start(1):finish(1),8);
time_trial2 = raw(start(2):finish(2),8);
time_trial3 = raw(start(3):finish(3),8);
time_trial4 = raw(start(4):finish(4),8);
%%%MOVEMENT EACH TRIAL
diffZ_trial1 = raw(start(1):finish(1),17)-raw(start(1):finish(1),11);
diffZ_trial2 = raw(start(2):finish(2),17)-raw(start(2):finish(2),11);
diffZ_trial3 = raw(start(3):finish(3),17)-raw(start(3):finish(3),11);
diffZ_trial4 = raw(start(4):finish(4),17)-raw(start(4):finish(4),11);
You are right, raw contains data of all types, including text (http://uk.mathworks.com/help/matlab/ref/xlsread.html#outputarg_raw). You should use num, which is a numeric matrix.
Alternatively, if you have an updated version of Matlab, you can try readtable (https://uk.mathworks.com/help/matlab/ref/readtable.html), which I think is more flexible. It creates a table from an excel file, containing both text and numbers.

How can I import data from text files into Excel?

I have multiple folders. There are multiple txt files inside these folder. I need to extract data (just a single value: value --->554) from a particular type of txt file in this folder.(individual_values.txt)
No 100 Value 555 level match 0.443 top level 0.443 bottom 4343
There will be many folders with same txt file names but diff value. Can all these values be copyed to excel one below the other.
I have to extract a value from a txt file which i mentioned above. Its a same text file with same name located inside different folders. All i want to do is extract this value from all the text file and paste it in excel or txt one below the other in each row.
Eg: The above is a text file here I have to get the value of 555 and similarly from other diff values.
555
666
666
776
Yes.
(you might want to clarify your question )
Your question isn't very clear, I imagine you want to know how this can be done.
You probably need to write a script that traverses the folders, reads the individual files, parses them for the value you want, and generates a Comma Separated Values (CSV) file. CSV files can easily be imported to Excel.
There are two or three basic methods you can use to get stuff into a Excel Spreadsheet.
You can use OLE wrappers to manipulate Excel.
You can write the file in a binary form
You can use Excel's import methods to take delimited text in as a spreadsheet.
I chose the latter way, because 1) it is the simplest, and 2) your problem is so poorly stated as it does not require a more complex way. The solution below outputs a tab-delimited text file that Excel can easily support.
In Perl:
use IO::File;
my #field_names = split m|/|, 'No/Value/level match/top level/bottom';
#' # <-- catch runaway quote
my $input = IO::File->new( '<data.txt' );
die 'Could not open data.txt for input!' unless $input;
my #data_rows;
while ( my $line = <$input> ) {
my %fields = $line =~ /(level match|top level|bottom|Value|No)\s+(\d+\S*)/g;
push #data_rows, \%fields if exists $fields{Value};
}
$input->close();
my $tab_file = IO::File->new( '>data.tab' );
die 'Could not open data.tab for output!' unless $tab_file;
$tab_file->print( join( "\t", #field_names ), "\n" );
foreach my $data_ref ( #data ) {
$tab_file->print( join( "\t", #$data_ref{#field_names} ), "\n" );
}
$tab_file->close();
NOTE: Excel's text processing is really quite neat. Try opening the text below (replacing the \t with actual tabs) -- or even copying and pasting it:
1\t2\t3\t=SUM(A1:C1)
I chose c#, because i thought it would be fun to use a recursive lambda. This will create the csv file containing matches to the regex pattern.
string root_path = #"c:\Temp\test";
string match_filename = "test.txt";
Func<string,string,StringBuilder, StringBuilder> getdata = null;
getdata = (path,filename,content) => {
Directory.GetFiles(path)
.Where(f=>
Path.GetFileName(f)
.Equals(filename,StringComparison.OrdinalIgnoreCase))
.Select(f=>File.ReadAllText(f))
.Select(c=> Regex.Match(c, #"value[\s\t]*(\d+)",
RegexOptions.IgnoreCase))
.Where(m=>m.Success)
.Select(m=>m.Groups[1].Value)
.ToList()
.ForEach(m=>content.AppendLine(m));
Directory.GetDirectories(path)
.ToList()
.ForEach(d=>getdata(d,filename,content));
return content;
};
File.WriteAllText(
Path.Combine(root_path, "data.csv"),
getdata(root_path, match_filename, new StringBuilder()).ToString());
No.
just making sure you have a 50/50 chance of getting the right answer
(assuming it was a question answerable by Yes and No) hehehe
File_not_found
Gotta have all three binary states for the response.

Resources