Format DateTime to specific format - c#

When this function is called, it creates a text file with these attributes. There is one attribute known as start_date. When I convert to DateTime, the date format will be MM:DD:YYYY and an error will be shown. I am not sure how to change the format to DD:MM:YYYY, I have also read on the DateTime formatting but I still do not understand. Thanks.
Code is as follows:
static void generateInfoForITDepartment()
{
string filepath = #"C:\Users\notgivingmydirectory\HRMasterlist.txt";
List<Employee> people = new List<Employee>();
List<string> lines = File.ReadAllLines(filepath).ToList();
foreach (var line in lines)
{
string[] entries = line.Split('|');
Employee newIT = new Employee();
newIT.Nric = entries[0];
newIT.FullName = entries[1];
newIT.Start_Date = Convert.ToDateTime(entries[3]);
newIT.Department = entries[5];
newIT.MobileNo = entries[6];
people.Add(newIT);
}
List<string> output = new List<string>();
foreach (var it in people)
{
output.Add($"{ it.Nric }, { it.FullName }, { it.Start_Date }, { it.Department }, { it.MobileNo }");
}
File.WriteAllLines("ITDepartment.txt", output);
Console.WriteLine("ITDepartment.txt has been created");
}
Edit:
My textfile currently looks like this:
S1234567A|Jan Lee|Ms|05/10/1990|Software Architect|IT Department|98785432|PartTime|3500
S1234567B|Feb Tan|Mr|10/12/1991|Corporate Recruiter|HR CorporateAdmin|98766432|PartTime|1500
S1234567C|Mark Lim|Mr|15/07/1992|Benefit Specialist|HR Corporate Admin|98265432|PartTime|2900

Since the data is not a valid date format, you need ParseExact or TryParseExact. If you are sure about incoming format is always in dd/MM/yyyy, then use ParseExact
Here is the example with ParseExact
newIT.Start_Date = DateTime.ParseExact(entries[3], "dd/MM/yyyy", CultureInfo.InvariantCulture);
Another example with TryParseExact
DateTime start_date = DateTime.MinValue;
var isValidDate = DateTime.TryParseExact(entries[3], "dd/MM/yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out start_date);
newIT.Start_Date = isValidDate ? start_date : DateTime.MinValue;

We need to use an option to parse the date from a specific format. While where here, let's improve the structure a bit, and the performance and memory use a LOT (what you had was grossly memory inefficient):
static IEnumerable<Employee> readEmployees(string filePath)
{
var lines = File.ReadLines(filepath);
return lines.Select(line => {
var data = line.Split('|');
var result = new Employee();
result.Nric = data[0];
result.FullName = data[1];
result.Start_Date = DateTime.ParseExact(data[3], "dd/MM/yyyy", CultureInfo.InvariantCulture);
result.Department = data[5];
result.MobileNo = data[6];
return result;
});
}
static void generateInfoForITDepartment()
{
string filepath = #"C:\Users\notgivingmydirectory\HRMasterlist.txt";
var people = readEmployees(filePath);
// note the extra format specifier for the date value
var output = people.Select(emp => $"{ emp.Nric }, { emp.FullName }, { emp.Start_Date:d }, { emp.Department }, { emp.MobileNo }");
File.WriteAllLines("ITDepartment.txt", output);
Console.WriteLine("ITDepartment.txt has been created");
}
FWIW, I would also tend to update the Employee class to move some of this code to a ToString() override and a static Parse() method, which could let you simplify the code like this:
static IEnumerable<Employee> readEmployees(string filePath)
{
var lines = File.ReadLines(filepath);
return lines.Select(line => Employee.Parse(line));
}
static void generateInfoForITDepartment()
{
string filepath = #"C:\Users\notgivingmydirectory\HRMasterlist.txt";
var people = readEmployees(filePath);
File.WriteAllLines("ITDepartment.txt", people); //calls the default ToString()
Console.WriteLine("ITDepartment.txt has been created");
}

Related

How to merge date and time from two separate columns into a new datetime column using csvHelper in C#

I have the following CSV file with the following format.
Using csvHelper in the following code snippet (using Visual Studio 2019), I can successfully read the file.
public ActionResult UploadCsvFile(IFormFile file)
{
if (file != null)
{
try
{
var basePath = Path.Combine(_hostEnvironment.WebRootPath, "uploads/");
if (!Directory.Exists(basePath))
Directory.CreateDirectory(basePath);
var filePath = Path.Combine(basePath, file.FileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
file.CopyTo(stream);
}
var config = new CsvConfiguration(CultureInfo.CurrentCulture)
{
Delimiter = ";",
HasHeaderRecord = true
};
using (var Reader = new StreamReader(filePath))
using (var csv = new CsvReader(Reader, config))
{
csv.Context.RegisterClassMap<CsvLineClassMap>();
var records = csv.GetRecords<CsvLine>().ToList();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
return Json(null);
}
From the debugging window I can see the data records (From there you can see the separate columns of date and time).
But, I need to get as a result in a single column with datetime format, As the following recreated.
I have investigated, but have not been able to solve it.
:(
You can use the Convert method in the ClassMap
void Main()
{
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("es");
var data = new StringBuilder();
data.AppendLine("Code;Date;Time;Level;Flow;Volumen");
data.AppendLine("OB-0100-99;16.07.2021;19:00:00;125,53;38,8;1621770");
data.AppendLine(";16.07.2021;20:00:00;138,6;69,4;1621780");
var config = new CsvConfiguration(CultureInfo.CurrentCulture)
{
Delimiter = ";"
};
using (var streamReader = new StringReader(data.ToString()))
using (var csv = new CsvReader(streamReader, config))
{
csv.Context.RegisterClassMap<CsvLineClassMap>();
var records = csv.GetRecords<CsvLine>().ToList().Dump();
}
}
public class CsvLineClassMap : ClassMap<CsvLine>
{
public CsvLineClassMap()
{
Map(x => x.Flow);
Map(x => x.Code);
Map(x => x.DateTime).Convert(x =>
{
return DateTime.ParseExact(
x.Row.GetField("Date") + " " + x.Row.GetField("Time"),
"dd.MM.yyyy H:mm:ss",
CultureInfo.InvariantCulture);
});
Map(x => x.Level);
Map(x => x.Volumen);
}
}
public class CsvLine
{
public double Flow { get; set; }
public string Code { get; set; }
public DateTime DateTime { get; set; }
public double Level { get; set; }
public double Volumen { get; set; }
}
If you are casting the result to a POCO, then write a getter method, in which you concat the Date and Time field and cast it and return it as a DateTime property.
(or)
Use this function
DateTime GetDateTime(string date, string time)
{
return DateTime.ParseExact(date + " " + time, "dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture);
}
and append the result to the return list.
Try add an item to your list records and concatenate Date field and Time field as follow:
...
using (var csv = new CsvReader(Reader, config))
{
csv.Context.RegisterClassMap<CsvLineClassMap>();
var records = csv.GetRecords<CsvLine>().ToList();
var newDateTime = new DateTime();
newDateTime = Convert.ToDateTime(records[2] + " " + records[2]);
records.Add(newDateTime);
}
...
also, you can use the following for the date convert part and format date and time:
DateTime dt1 = DateTime.ParseExact(records[2] + " " + records[2], "dd/MM/yy h:mm:ss tt", CultureInfo.InvariantCulture);

How can I change a string Time = 2:30 PM into TimeSpan = 14:00

I have a string strTimeofDay = "2:30 PM-3:00 PM". I need to convert that into 2 strings using military time.
I have tried this code:
if (Results.TimeOfDay != "-" || Results.TimeOfDay != null)
{
string strTimeofDay = Convert.ToString(Results.TimeOfDay);
if (strTimeofDay != null)
{
string[] times = strTimeofDay.Split('-');
string time1 = times[0];
RadTimePicker1.SelectedTime = ParseTime(time1);
string time2 = times[1];
RadTimePicker2.SelectedTime = ParseTime(time2);
}
}
static public TimeSpan ParseTime(string input)
{
string trimed = input.Trim();
TimeSpan output;
var ok = TimeSpan.TryParseExact(trimed, #"hh\:mm\:tt", CultureInfo.InvariantCulture,out output);
return output;
}
But the output comes out as false. What am I missing?
Converting a string in the format "2:30 PM" into the string "14:00" is as easy as parsing it using one format, and outputting it to a different one
var input = "2:30PM";
var dt = DateTime.ParseExact(input,"h:mm tt",CultureInfo.InvariantCulture,DateTimeStyles.None);
var output = dt.ToString("HH:mm");
Live example: https://dotnetfiddle.net/BRl2du
If you wanted that as a TimeSpan it would simply be dt.TimeOfDay.

Sorting file names in a directory giving wrongly ordered results

I have files in a directory with file names like this:
Batch 1.10.18.xlsx
Batch 2.10.18.xlsx
...
Batch 31.10.18.xlsx
As you see, they have this pattern: Batch dd.mm.yy.xlsx
I need to process them in the order by those dates in the file names.
Code so far:
private void processFiles(string BatchFilePath)
{
IOrderedEnumerable<string> fileEntries =
Directory.GetFiles(BatchFilePath, "Batch *.xlsx")
.OrderBy(f => GetFileDay(f));
foreach (string fileName in fileEntries)
{
Console.WriteLine("Processing File " + Path.GetFileName(fileName));
// Code that read and process files
}
}
private int GetFileDay(string file)
{
string s1= file.Substring(7, 2);
if (s1.Substring(1) == ".")
s1 = s1.Substring(0, 1);
return int.Parse(s1);
}
The code did not work. It still gives me files with names in the wrong order as shown below:
Batch 25.10.18.xlsx
Batch 22.10.18.xlsx...
Batch 9.10.18.xlsx
Batch 3.10.18.xlsx
...
Parse the string (such as "1.10.18") to a real DateTime(2018-10-01):
DateTime GetFileDay(string fileNameOrPath)
{
string fileNameWithoutExt = System.IO.Path.GetFileNameWithoutExtension(fileNameOrPath);
return DateTime.ParseExact(fileNameWithoutExt.Replace("Batch ", ""), "d.M.yy", null);
}
Use regex to parse the date from filename and sort based on datetime. Here is the modified code.
public static IOrderedEnumerable<string> GetFiles(string batchFilePath)
{
if (Directory.Exists(batchFilePath))
{
var directoryInfo = new DirectoryInfo(batchFilePath);
var fileEntries = directoryInfo.GetFiles(#"Batch *.xlsx").Select(x => x.Name).OrderBy(f => GetFileDay(f));
return fileEntries;
}
return null;
}
private static DateTime GetFileDay(string file)
{
var date = default(DateTime);
var extractedDate = Regex.Match(file, #"(\W\S*(\d[\d]{0,2}))").Value;
extractedDate = extractedDate.Replace(".", "-").Trim();
DateTime.TryParseExact(extractedDate, "d-MM-yy", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out date);
return date;
}
Considering your file collection will be IEnumerable<T>, this will work sorting by real date [not string!] of your culture
var l = new List<string>()
{
"c:\\dev\\Batch 1.10.18.xlsx",
"c:\\dev\\Batch 2.10.18.xlsx",
"c:\\dev\\Batch 31.10.18.xlsx"
};
var ci = CultureInfo.GetCultureInfo("fr-FR"); // pick culture is same as pick format. You need to pre-define one
var r = l.Select(x=>new{name = x, parts = Path.GetFileNameWithoutExtension(x).Split(" .".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)}).
Select(a=> new {name = a.name, date = DateTime.Parse(a.parts[1] + "/" + a.parts[2] + "/" + a.parts[3], ci)}).
OrderBy(x => x.date); //OrderByDescending(x => x.date);
r.ToList().ForEach(x => Console.WriteLine(x.name));
Output
Batch 1.10.18.xlsx
Batch 2.10.18.xlsx
Batch 31.10.18.xlsx
This could be done more efficiently but less linear.
You can use the following Regex. Then you can do OrderBy/OrderByDescending on Linq:
Regex r = new Regex(#"\d{1,2}.\d{1,2}.\d{2}");
var orderByDateList = items.Where(po => r.IsMatch(po)).OrderByDescending(po => DateTime.ParseExact(r.Match(po).Value, "d.M.yy", null)).ToList(); // lines that match date pattern

Convert C# DataTable(ColumnName, ColumnType, ColumnValue) to csv and restore back

I am looking for c# code to convert ADO.NET Datatable to csv file, However I want to save/restore
columns name,
column data Type and
column Value
in csv. Most of the solution I have found restores the datatable from CSV in string column type. I also want that nullable values should be restored as DBNull.Value. DateTime column should be saved and restored as DateTime Type only. The concept is to fill datatable using DataAdapter from Oracle/Sqlserver database and later save that table to CSV file and later restore from CSV.
I have used the code from below link to save DataTable to CSV file using DataTableExtensions class c# datatable to csv
For reading the CSV file back to DataTable I used the below Link
http://www.codeproject.com/Articles/11698/A-Portable-and-Efficient-Generic-Parser-for-Flat-F
The Problem is when I restore the CSV file to datatable I have to create Entity from DataTable rows. But They throw Exception on InvalidCast.
Assuming that you want to store the column-name in the first and the types in the second line and the data begins in the third line, you could use following code. Sample data:
DataTable tblExport = new DataTable();
tblExport.Columns.Add("ID", typeof(int));
tblExport.Columns.Add("Name", typeof(string));
tblExport.Columns.Add("DateofBirth", typeof(DateTime)).AllowDBNull = false;
tblExport.Columns.Add("DateofDeath", typeof(DateTime)).AllowDBNull = true;
tblExport.Rows.Add(1, "Tim", new DateTime(1973, 7, 9), DBNull.Value);
tblExport.Rows.Add(2, "Jim", new DateTime(1953, 3, 19), new DateTime(2011, 1, 2));
tblExport.Rows.Add(3, "Toby", new DateTime(1983, 4, 23), DBNull.Value);
Since you need to convert all values to string with value.ToString i'm changing the culture to InvariantCulture at the beginning to force a specific DateTime format, store the old so that you can enable it again at the end. I hope the code is self-explaining:
var oldCulture = CultureInfo.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
write DataTable to csv-file
string delimiter = "\t"; // tab separated
StringBuilder sb = new StringBuilder();
// first line column-names
IEnumerable<string> columnNames = tblExport.Columns.Cast<DataColumn>()
.Select(column => column.ColumnName);
sb.AppendLine(string.Join(delimiter, columnNames));
// second line column-types
IEnumerable<string> columnTypes = tblExport.Columns.Cast<DataColumn>()
.Select(column => column.DataType.ToString());
sb.AppendLine(string.Join(delimiter, columnTypes));
// rest: table data
foreach (DataRow row in tblExport.Rows)
{
IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
sb.AppendLine(string.Join(delimiter, fields));
}
string path = #"C:\Temp\Testfile.csv";
File.WriteAllText(path, sb.ToString());
read csv-file into DataTable
string[] lines = File.ReadAllLines(path);
string[] columns = lines[0].Split(new[] { delimiter }, StringSplitOptions.None);
string[] types = lines[1].Split(new[] { delimiter }, StringSplitOptions.None);
DataTable tblImport = new DataTable();
for (int i = 0; i < columns.Length; i++)
{
string colName = columns[i];
string typeName = types[i];
tblImport.Columns.Add(colName, Type.GetType(typeName));
}
// import data
// use a typeValueConverter dictionary to convert values:
var typeValueConverter = new Dictionary<Type, Func<string, object>> {
{ typeof(DateTime), value => value.TryGetDateTime(null, null) },
{ typeof(Decimal), value => value.TryGetDecimal(null) },
{ typeof(int), value => value.TryGetInt32(null) },
};
foreach (string line in lines.Skip(2))
{
string[] fields = line.Split(new[]{ delimiter }, StringSplitOptions.None);
DataRow r = tblImport.Rows.Add(); // already added at this point
for (int i = 0; i < tblImport.Columns.Count; i++)
{
DataColumn col = tblImport.Columns[i];
string rawValue = fields[i];
object val = rawValue;
if (typeValueConverter.ContainsKey(col.DataType))
val = typeValueConverter[col.DataType](rawValue);
else if (col.DataType != typeof(string) && string.IsNullOrEmpty(rawValue))
val = DBNull.Value;
r.SetField(col, val);
}
}
System.Threading.Thread.CurrentThread.CurrentCulture = oldCulture;
Of course you should separate both in two methods, one for exporting and one for importing.
I've used my extensions method TryGetDateTime, TryGetDecimal and TryGetInt32 which parses strings to DateTime?,Decimal? and int?(null if it couldn't be parsed). They are especially handy in LINQ queries:
public static DateTime? TryGetDateTime(this string item, DateTimeFormatInfo dfi, params string[] allowedFormats)
{
if (dfi == null) dfi = DateTimeFormatInfo.InvariantInfo;
DateTime dt;
bool success;
if(allowedFormats == null)
success = DateTime.TryParse(item, dfi, DateTimeStyles.None, out dt);
else
success = DateTime.TryParseExact(item, allowedFormats, dfi, DateTimeStyles.None, out dt);
if (success) return dt;
return null;
}
public static decimal? TryGetDecimal(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
{
if (formatProvider == null) formatProvider = NumberFormatInfo.InvariantInfo;
decimal d = 0m;
bool success = decimal.TryParse(item, nStyles, formatProvider, out d);
if (success)
return d;
else
return null;
}
public static int? TryGetInt32(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
{
if (formatProvider == null) formatProvider = NumberFormatInfo.InvariantInfo;
int i = 0;
bool success = int.TryParse(item, nStyles, formatProvider, out i);
if (success)
return i;
else
return null;
}

How to read a text file

I read text file and displayed in DataGridView data is displaying, but in the above text file is related to log files, I want to display that log file by month wise, without using a DataTable, if possible.
private void BtnUser_Click(object sender, EventArgs e)
{
dgv1.Columns.Add("col1", "Ipaddress");
dgv1.Columns.Add("col2", "Sysname");
dgv1.Columns.Add("col3", "username");
dgv1.Columns.Add("col4", "text");
dgv1.Columns.Add("col5", "datetime");
string line;
StreamReader strRead = new StreamReader("D:\\login.lgl");
{
int row = 0;
while ((line = strRead.ReadLine()) != null)
{
string[] columns = line.Split('|');
dgv1.Rows.Add();
for (int i = 0; i < columns.Length; i++)
{
dgv1[i, row].Value = columns[i];
}
row++;
}
}
}
You could use Linq to group by month:
var logMonthGroups = File.ReadLines("D:\\login.lgl")
.Select(l => new { Cols = l.Split('|') })
.Select(x => new
{
Ipaddress = x.Cols.ElementAtOrDefault(0),
Sysname = x.Cols.ElementAtOrDefault(1),
username = x.Cols.ElementAtOrDefault(2),
text = x.Cols.ElementAtOrDefault(3),
datetime = x.Cols.ElementAtOrDefault(4) == null ? DateTime.MinValue : DateTime.Parse(x.Cols[4])
})
.GroupBy(x => new { Year = x.datetime.Year, Month = x.datetime.Month })
.OrderBy(g => g.Key.Year).ThenBy(g => g.Key.Month);
foreach(var group in logMonthGroups)
{
// add to the DataGridView ...
}
I would recommend that you create a class for the structure you are parsing in from the file, something like:
public class LogFileItem
{
public string IpAddress {get; set;}
public string Sysname {get; set;}
public string Username {get; set;}
public string Text {get; set;}
public DateTime DateTime {get; set;}
public static List<LogFileItem> ParseLogFile(string path)
{
List<LogFileItem> result = new List<LogFileItem>();
//in a real scenario, this code should have a lot more error checks
string line;
StreamReader strRead = new StreamReader(path);
while ((line = strRead.ReadLine()) != null)
{
string[] columns = line.Split('|');
LogFileItem item = new LogFileItem();
item.IpAddress = columns[0];
item.Sysname = columns[1];
item.Username = columns[2];
item.Text = columns[3];
//this assumes that the dateTime column is parsable by .net
item.DateTime = DateTime.Parse(columns[4]);
result.add(item);
}
return result;
}
}
then you could just do:
private void BtnUser_Click(object sender, EventArgs e)
{
List<LogFileItem> logItems = LogFileItem.ParseLogFile(#"D:\login.lgl");
dgv1.DataSource = logItems;
}
to display the data. Also you could filter the data any which way you want, and if you have a month/year pair to filter on, you could just do:
List<LogFileItem> logItems = LogFileItem.ParseLogFile(#"D:\login.lgl");
var logsPerMonth = logItems.Where(li => li.DateTime.Year = year && li.DateTime.Month == month);
Note that datetime parsing is somewhat of a dark art, so you could take a look at DateTime.ParseExact to make that work. Also, take a look at using an using statement, or reading the lines from a text file with File.ReadAllLines.

Categories

Resources