Closed MaciekWin3 closed 9 months ago
I can't repro this. Did you have the v2_develop branch updated?
Date should be changed to what user typed
Only if it's valid date, otherwise don't changes nothing. It's designed to only accept valid typing date.
I can't repro this. Did you have the v2_develop branch updated?
Yes, I have v2_develop branch updated.
Only if it's valid date, otherwise don't changes nothing. It's designed to only accept valid typing date.
I was just typing ones, so end result should be 11/11/1111 and it it's valid date.
Strange I typed 1 until end and doesn't crash. Maybe it's something with the current culture. I'll change to the US locale,
Nope. See the date bellow that it's US locale. Maybe you already did changes for your new features and that perhaps it's causing that exception.
Hmm, downloaded clean repo and still same issue. But on other device everything is working as expected.
It fails here on line 125:
Please, put a breakpoint in that line on the last 1 typed to see the e.NewText
and spaces values. But for sure it's something wrong with that device.
e.NewText
is " 1/15/1024" and spaces _fieldLen is 10 spaces is 1, so after line 124 there spaces are equal to 11.
Sorry was referring the spaces
local variable. But I'm seeing what is the problem here. This " 1/15/1024" should be " 01/15/1024" or " 11/15/1024", if you typed 1. Are you selecting before typing? If day format is "dd" then it should display as 01
, but your system may be configured to not display the prefix 0. In that case we have to force set the e.NewText
to format first, but could throws an exception and thus losing the changing. You must compare the settings on both devices and see what going on.
In line 125 change to string trimedText = e.NewText [..Math.Min (spaces, e.NewText.Length)];
.
Sorry was referring the
spaces
local variable. But I'm seeing what is the problem here. This " 1/15/1024" should be " 01/15/1024" or " 11/15/1024", if you typed 1. Are you selecting before typing? If day format is "dd" then it should display as01
, but your system may be configured to not display the prefix 0. In that case we have to force set thee.NewText
to format first, but could throws an exception and thus losing the changing. You must compare the settings on both devices and see what going on.
Yup, that's the issue.
In line 125 change to
string trimedText = e.NewText [..Math.Min (spaces, e.NewText.Length)];
.
After this change, values are indeed changing, but if the day is less than 10, you can't make it double-digit, and also typing after the last year digit still results in a crash.
After this change, values are indeed changing, but if the day is less than 10, you can't make it double-digit, and also typing after the last year digit still results in a crash.
Of course but you need to know why your two devices, which I think have the same configuration, doesn't behavior on the same way. If a day format is "dd", then it should appear as "01" and not as "1". The e.NewText
is a string and when you type 1 with the cursor position on the first 0, then the text is replaced by 1 on that cursor position. Try to discover why they are different.
Another possibility is the var date = Convert.ToDateTime (trimedText, ccFmt).ToString (ccFmt.ShortDatePattern);
is removing the 0 from the "01". See the value of ccFmt.ShortDatePattern
. If is this convert method that is causing the bad format then we need to rethink about, but I can't repro.
I was using en-GB on my laptop, which uses the format 'dd/MM/yyyy,' and it worked as intended. Are you sure that if you set up CultureInfo to en-US, it works fine? When I run this piece of code:
using System;
using System.Globalization;
public class Program
{
public static void Main()
{
var culture = new CultureInfo ("en-US");
var sepChar = culture.DateTimeFormat.DateSeparator;
var format = culture.DateTimeFormat.ShortDatePattern;
var dateTime = DateTime.Now;
string date = dateTime.ToString(format);
Console.WriteLine(date);
}
}
I am getting "1/15/2024", without leading zero.
That explain the difference, thanks. You are absolute right. I don't know why this wasn't repro before when I tried to simulate the US locale, Sorry for that. Do you want to submit a fix for this or you prefer if I do?
But if it's to respect the locate format "M/d/yyyy" then the constant length (10) will not working properly because the code doesn't consider different format length. We need to discuss the best solution.
I took a look at other CultureInfo
instances, and I noticed that some of them use a single-digit month without a leading zero. So, that's another edge case to consider.
That explain the difference, thanks. You are absolute right. I don't know why this wasn't repro before when I tried to simulate the US locale, Sorry for that. Do you want to submit a fix for this or you prefer if I do?
No problem, glad I could help! :D It's up to you, I don't mind if you take the lead on this, but I can also do it if we agree on the best solution.
But if it's to respect the locate format "M/d/yyyy" then the constant length (10) will not working properly because the code doesn't consider different format length. We need to discuss the best solution.
Yeah, the length for each format can be different, not only because of the day but also due to the month.
Well I know you will found the best solution for this, so is your responsibility. Thanks for your help and please submit a PR 👍
Certainly, I'll delve deeper into this task later this week. Furthermore, I need these changes to enhance the DatePicker functionality so It will also serve as a nice warm-up 🏃♂️
Great. If you don't like the space at start on the DateField
you can set the Padding
left to 1 with the same color scheme and only using the Text
with only all length of the date. But if it's confused for you then never mind 😃
Following my research, I've compiled a list of various date formats originating from CultureInfo
. Here's the list:
Now, if the format length is 10, supporting them (at least most of them) remains relatively straightforward. However, when the length is less than 10, complexities arise with the current implementation. For example, take the widely used en-US format (m/d/yyyy). Due to its length being less than 10, the system encounters challenges in updating single-digit days and months. It's still unclear whether to update the first or second digit of the day/month.
Now, addressing the challenges in supporting these formats:
1) Introduction of a Mandatory Public Property for Date Format: A mandatory public property will be introduced to specify the desired date format within the application. This property will support only a predefined set of formats, acknowledging that users may have preferences beyond their cultural defaults. With this approach, we can decisively determine the date formats supported, making it easier to test and maintain the application efficiently. 2) Component refactor: If anyone has a better idea on how we can support all date formats, I'm eager to hear your thoughts! Although I believe it might necessitate a significant refactor of the current implementation, I'm open to exploring more effective solutions. Your insights and suggestions are welcomed.
Now, addressing the challenges in supporting these formats:
- Introduction of a Mandatory Public Property for Date Format: A mandatory public property will be introduced to specify the desired date format within the application. This property will support only a predefined set of formats, acknowledging that users may have preferences beyond their cultural defaults. With this approach, we can decisively determine the date formats supported, making it easier to test and maintain the application efficiently.
Maybe the best solution is a readonly DateFormat
list obtained by the user culture can provide and thus updating the respective format, separator, etc... and avoiding the user set custom format that can't be supported.
- Component refactor: If anyone has a better idea on how we can support all date formats, I'm eager to hear your thoughts! Although I believe it might necessitate a significant refactor of the current implementation, I'm open to exploring more effective solutions. Your insights and suggestions are welcomed.
I think we should respect the user culture format as possible. In this case the fixed length doesn't makes more sense and a dynamic length is a better approach.
Maybe not validating the date on typing through OnProcessKeyDown
but only on DateField_Changing
event. If it's invalid cancel the change.
Another alternative is use the TextValidateField
where you can use a mask based on the format and separators. I think it's a good choice because it already has validation based on the mask. In this case the DateField
should inherit from TextValidateField
. I know that not everyone is fan of masked controls, so it's up to you to decide.
Now, addressing the challenges in supporting these formats:
- Introduction of a Mandatory Public Property for Date Format: A mandatory public property will be introduced to specify the desired date format within the application. This property will support only a predefined set of formats, acknowledging that users may have preferences beyond their cultural defaults. With this approach, we can decisively determine the date formats supported, making it easier to test and maintain the application efficiently.
Maybe the best solution is a readonly
DateFormat
list obtained by the user culture can provide and thus updating the respective format, separator, etc... and avoiding the user set custom format that can't be supported.
- Component refactor: If anyone has a better idea on how we can support all date formats, I'm eager to hear your thoughts! Although I believe it might necessitate a significant refactor of the current implementation, I'm open to exploring more effective solutions. Your insights and suggestions are welcomed.
I think we should respect the user culture format as possible. In this case the fixed length doesn't makes more sense and a dynamic length is a better approach. Maybe not validating the date on typing through
OnProcessKeyDown
but only onDateField_Changing
event. If it's invalid cancel the change. Another alternative is use theTextValidateField
where you can use a mask based on the format and separators. I think it's a good choice because it already has validation based on the mask. In this case theDateField
should inherit fromTextValidateField
. I know that not everyone is fan of masked controls, so it's up to you to decide.
Thanks for your suggestion; I'll take a look at them. But regarding DateFormat, I meant that it should not be a string value so the user can set it up to whatever he/she wants, but more like an enum with values. But as you said, you are a fan of supporting each culture, and I get it. But some of them will be hard to support even with your suggestions, so I'll give a try at supporting all of them but keep that in mind. Also, going back to the date format, I still think that the user should be able to change it. I, for example, have the default culture set to en-US, and I can't stand the date format with the month first (M/d/yyyy), and I would gladly change it to something else.
Thanks for your suggestion; I'll take a look at them. But regarding DateFormat, I meant that it should not be a string value so the user can set it up to whatever he/she wants, but more like an enum with values. But as you said, you are a fan of supporting each culture, and I get it. But some of them will be hard to support even with your suggestions, so I'll give a try at supporting all of them but keep that in mind. Also, going back to the date format, I still think that the user should be able to change it. I, for example, have the default culture set to en-US, and I can't stand the date format with the month first (M/d/yyyy), and I would gladly change it to something else.
Sorry I expressed myself poorly. What I was referring to is that it should really be an enum type that just presents the different predefined formats where the user can choose what they want but should not edit. But whether you prefer to allow the user to edit the format, for example, from "M/d/yyyy" to "d/M/yyyy" is up to you. Anyway, I prefer that you do it according to your preferences and I will help in whatever way I can, do you agree?
@MaciekWin3 only to inform this is calculating one more week. Perhaps you want to see.
@MaciekWin3 only to inform this is calculating one more week. Perhaps you want to see.
Yes, it is intended behavoiur. I was using windows calendar as a refernece:
However, now I see that calendars are inconsistent (some of them adds last row, some of them doesn't), so I might as well delete the late row. Also, I am thinking about your suggestion that instead of '-', we should use greyed-out days for the previous and next month, but I haven't tried to implement it yet.
Sorry I expressed myself poorly. What I was referring to is that it should really be an enum type that just presents the different predefined formats where the user can choose what they want but should not edit. But whether you prefer to allow the user to edit the format, for example, from "M/d/yyyy" to "d/M/yyyy" is up to you. Anyway, I prefer that you do it according to your preferences and I will help in whatever way I can, do you agree?
I agree with you that by default, we should set it to the default CultureInfo. However, right now, I have taken on the challenge of supporting every format. I took a look at TextValidateField, and instead of masks, I would use Regex to support every date format. For instance, we can only allow the user to change numbers in the date, while other characters will be cemented to the text field. One issue I am encountering when working with TextValidateField is that it lacks some features compared to TextView; for example, the cursor is invisible, and it will probably need to be extended if we are going to go down this route.
Still, formats like 'd/M/yyyy' would need to use the same regex as 'dd/MM/yyyy,' for example, `^\d{1,2}/\d{1,2}/\d{4}$. What do you think?
I agree with you that by default, we should set it to the default CultureInfo. However, right now, I have taken on the challenge of supporting every format. I took a look at TextValidateField, and instead of masks, I would use Regex to support every date format. For instance, we can only allow the user to change numbers in the date, while other characters will be cemented to the text field. One issue I am encountering when working with TextValidateField is that it lacks some features compared to TextView; for example, the cursor is invisible, and it will probably need to be extended if we are going to go down this route.
Instead of extended it I think it's better open a PR to enable the cursor visibility and enable selecting, but for that we already have the TextField
. Complexity 😃
Still, formats like 'd/M/yyyy' would need to use the same regex as 'dd/MM/yyyy,' for example, `^\d{1,2}/\d{1,2}/\d{4}$. What do you think?
I think a very good idea. When you said I'm fan of using the CultureInfo
is only on the order that days, months and years are displayed but not for the number of digits that each one have. I think it's insignificant if a date format is 'd/M/yyyy' is mandatory to use only as '1/1/2024' and is very reasonable to use as '01/01/2024' with a fixed length which is much more easy to handle it. I think I broken the DateField
in this aspect where I think I had a GetLongFormat
method to convert to 'dd/MM/yyyy'. If you prefer to restore that for now to continue using it on your improvements, feel free to do that. Otherwise, make the improvements the way your brain imagines and I will give you all the support you need.
Do you think just one method using pattern matching can handle that? Something like this:
private static string StandardizeDateFormat (string format) =>
format switch {
"MM/dd/yyyy" => "MM/dd/yyyy",
"yyyy-MM-dd" => "yyyy-MM-dd",
"yyyy/MM/dd" => "yyyy/MM/dd",
"dd/MM/yyyy" => "dd/MM/yyyy",
"d?/M?/yyyy" => "dd/MM/yyyy",
"dd.MM.yyyy" => "dd.MM.yyyy",
"dd-MM-yyyy" => "dd-MM-yyyy",
"dd/MM yyyy" => "dd/MM/yyyy",
"d. M. yyyy" => "dd.MM.yyyy",
"yyyy.MM.dd" => "yyyy.MM.dd",
"g yyyy/M/d" => "yyyy/MM/dd",
"d/M/yyyy" => "dd/MM/yyyy",
"d?/M?/yyyy g" => "dd/MM/yyyy",
"d-M-yyyy" => "dd-MM-yyyy",
"d.MM.yyyy" => "dd.MM.yyyy",
"d.MM.yyyy '?'." => "dd.MM.yyyy",
"M/d/yyyy" => "MM/dd/yyyy",
"d. M. yyyy." => "dd.MM.yyyy",
"d.M.yyyy." => "dd.MM.yyyy",
"g yyyy-MM-dd" => "yyyy-MM-dd",
"d.M.yyyy" => "dd.MM.yyyy",
"d/MM/yyyy" => "dd/MM/yyyy",
"yyyy/M/d" => "yyyy/MM/dd",
"dd. MM. yyyy." => "dd.MM.yyyy",
"yyyy. MM. dd." => "yyyy.MM.dd",
"yyyy. M. d." => "yyyy.MM.dd",
"d. MM. yyyy" => "dd.MM.yyyy",
_ => "dd/MM/yyyy"
};
Then we expose public property Culture
and format of date is dependent on it:
/// <summary>
/// CultureInfo for date. The default is CultureInfo.CurrentCulture.
/// If choosen culture cannot be safely parsed to 10 characters date format, it will be set to en-US.
/// </summary>
public CultureInfo Culture {
get => CultureInfo.CurrentCulture;
set {
if (value is not null) {
CultureInfo.CurrentCulture = value;
Format = " " + StandardizeDateFormat (value.DateTimeFormat.ShortDatePattern);
Width = FieldLen + 2;
}
}
}
With this approach I don't like that Culture needs to be set to 10 characters and this can be misleading. What are your thoughts?
Do you think just one method using pattern matching can handle that? Something like this:
The StandardizeDateFormat
may not cover all situations. You can add the GetShortFormat
method again into the DateField
. It handles all the situations base on the format and separator. You can do Theory
unit tests to check that if you want.
Here a unit test where cover all the cultures. May be you can use the both methods (StandardizeDateFormat
or GetShortFormat
) to test which one covers more situations. I used the GetShortFormat
in the test.
[Fact]
public void Using_All_Culture_GetShortFormat ()
{
CultureInfo cultureBackup = CultureInfo.CurrentCulture;
DateTime date = DateTime.Parse ("1/1/1971");
foreach (var culture in CultureInfo.GetCultures (CultureTypes.AllCultures)) {
CultureInfo.CurrentCulture = culture;
var separator = culture.DateTimeFormat.DateSeparator;
var format = culture.DateTimeFormat.ShortDatePattern;
DateField df = new DateField (date);
if ((!culture.TextInfo.IsRightToLeft || (culture.TextInfo.IsRightToLeft && !df.Text.Contains ('\u200f')))
&& (format.StartsWith ('d') || format.StartsWith ('M'))) {
switch (culture.Name) {
case "bg":
case "bg-BG":
Assert.Equal ($" 01{separator}01{separator}1971 г.", df.Text);
Assert.Equal (1, separator.Length);
break;
case "bs-Cyrl":
case "bs-Cyrl-BA":
case "sr":
case "sr-Cyrl":
case "sr-Cyrl-BA":
case "sr-Cyrl-ME":
case "sr-Cyrl-RS":
case "sr-Cyrl-XK":
case "sr-Latn":
case "sr-Latn-BA":
case "sr-Latn-ME":
case "sr-Latn-RS":
case "sr-Latn-XK":
Assert.Equal ($" 01{separator}01{separator}1971.", df.Text);
Assert.Equal (1, separator.Length);
break;
case "bs":
case "bs-Latn":
case "bs-Latn-BA":
case "hr":
case "hr-BA":
case "hr-HR":
Assert.Equal ($" 01{separator}01{separator}1971.", df.Text);
Assert.Equal (2, separator.Length);
break;
case "kkj":
case "kkj-CM":
Assert.Equal ($" 01{separator}01 1971", df.Text);
Assert.Equal (1, separator.Length);
break;
case "ksh":
case "ksh-DE":
case "sk":
case "sk-SK":
case "sl":
case "sl-SI":
Assert.Equal ($" 01{separator}01{separator}1971", df.Text);
Assert.Equal (2, separator.Length);
break;
case "th":
case "th-TH":
Assert.Equal ($" 01{separator}01{separator}2514", df.Text);
Assert.Equal (1, separator.Length);
break;
default:
Assert.Equal ($" 01{separator}01{separator}1971", df.Text);
Assert.Equal (1, separator.Length);
break;
}
} else if (culture.TextInfo.IsRightToLeft) {
if (df.Text.Contains ('\u200f')) {
// It's a Unicode Character (U+200F) - Right-to-Left Mark (RLM)
Assert.True (df.Text.Contains ('\u200f'));
if (culture.Name == "ar-SA") {
Assert.Equal (" 04/11/1390 بعد الهجرة", df.Text);
Assert.Equal (2, separator.Length);
} else {
Assert.Equal (" 01/01/1971", df.Text);
Assert.Equal (2, separator.Length);
}
} else {
switch (culture.Name) {
case "ckb-IR":
case "lrc":
case "lrc-IR":
case "mzn":
case "mzn-IR":
case "ps":
case "ps-AF":
case "uz-Arab":
case "uz-Arab-AF":
Assert.Equal ($" AP 1349{separator}10{separator}11", df.Text);
Assert.Equal (1, separator.Length);
break;
case "fa":
case "fa-AF":
case "fa-IR":
Assert.Equal ($" 1349{separator}10{separator}11", df.Text);
Assert.Equal (1, separator.Length);
break;
default:
Assert.Equal ($" 1971{separator}01{separator}01", df.Text);
Assert.Equal (1, separator.Length);
break;
}
}
} else {
switch (culture.Name) {
case "hu":
case "hu-HU":
Assert.Equal ($" 1971{separator}01{separator}01.", df.Text);
Assert.Equal (2, separator.Length);
break;
case "ko":
case "ko-KP":
case "ko-KR":
Assert.Equal ($" 1971{separator}01{separator}1.", df.Text);
Assert.Equal (2, separator.Length);
break;
default:
Assert.Equal ($" 1971{separator}01{separator}01", df.Text);
Assert.Equal (1, separator.Length);
break;
}
}
}
CultureInfo.CurrentCulture = cultureBackup;
}
string GetShortFormat (string fmt)
{
string [] splitedFmt = fmt.Split (_sepChar);
string newFmt = fmt;
for (int i = 0; i < splitedFmt.Length; i++) {
if (splitedFmt [i].Contains ("M") && splitedFmt [i].GetRuneCount () < 2) {
newFmt = newFmt.Replace ("M", "MM");
}
if (splitedFmt [i].Contains ("d") && splitedFmt [i].GetRuneCount () < 2) {
newFmt = newFmt.Replace ("d", "dd");
}
if (splitedFmt [i].Contains ("y") && splitedFmt [i].GetRuneCount () < 4) {
newFmt = newFmt.Replace ("yy", "yyyy");
}
}
return newFmt;
}
Then we expose public property Culture and format of date is dependent on it:
Do all this changes. After apply all the changes you can type 1 at start until end and it won't throw any excecption.
public CultureInfo Culture {
get => CultureInfo.CurrentCulture;
set {
if (value is not null) {
CultureInfo.CurrentCulture = value;
_sepChar = value.DateTimeFormat.DateSeparator;
Format = " " + GetShortFormat (value.DateTimeFormat.ShortDatePattern);
Text = Date.ToString (_format);
}
}
}
void SetInitialProperties (DateTime date)
{
_sepChar = Culture.DateTimeFormat.DateSeparator;
_format = $" {GetShortFormat (Culture.DateTimeFormat.ShortDatePattern)}";
void DateField_Changing (object sender, TextChangingEventArgs e)
{
try {
int spaces = 0;
for (int i = 0; i < e.NewText.Length; i++) {
if (e.NewText [i] == ' ') {
spaces++;
} else {
break;
}
}
spaces += _fieldLen;
string trimedText = e.NewText [..spaces];
spaces -= _fieldLen;
trimedText = trimedText.Replace (new string (' ', spaces), " ");
var date = Convert.ToDateTime (trimedText).ToString (_format.Trim ());
if ($" {date}" != e.NewText) {
e.NewText = $" {date}";
}
AdjCursorPosition (CursorPosition, true);
} catch (Exception) {
e.Cancel = true;
}
}
bool SetText (string text)
{
if (string.IsNullOrEmpty (text)) {
return false;
}
text = NormalizeFormat (text);
string [] vals = text.Split (_sepChar);
string [] frm = _format.Split (_sepChar);
int year;
int month;
int day;
int idx = GetFormatIndex (frm, "y");
if (Int32.Parse (vals [idx]) < 1) {
year = 1;
vals [idx] = "1";
} else {
year = Int32.Parse (vals [idx]);
}
idx = GetFormatIndex (frm, "M");
if (Int32.Parse (vals [idx]) < 1) {
month = 1;
vals [idx] = "1";
} else if (Int32.Parse (vals [idx]) > 12) {
month = 12;
vals [idx] = "12";
} else {
month = Int32.Parse (vals [idx]);
}
idx = GetFormatIndex (frm, "d");
if (Int32.Parse (vals [idx]) < 1) {
day = 1;
vals [idx] = "1";
} else if (Int32.Parse (vals [idx]) > 31) {
day = DateTime.DaysInMonth (year, month);
vals [idx] = day.ToString ();
} else {
day = Int32.Parse (vals [idx]);
}
string d = GetDate (month, day, year, frm);
DateTime date;
try {
date = Convert.ToDateTime (d);
} catch (Exception) {
return false;
}
Date = date;
return true;
}
With this approach I don't like that Culture needs to be set to 10 characters and this can be misleading. What are your thoughts?
Well this is the more complex because a fixed size allow to control better the typed text, but it may also be possible to control with dynamic width with much more work, of course.
If the start white space is confusing then it may me removed. The creator of this code wanted to display the text with a start white space because there is always a white space at the final and thus be looking symmetric. But with the new Adornment
feature it's possible to set the Padding
on the DateField
with a new Thickness (1, 0, 0, 0)
with the same color scheme. Thus the start white space can be removed from the format. Instead of using 1 as the start valid cursor position for typing, now it will be 0. But also it can be without the same look as now. It's up to you.
I hope you understand all this 😃
@MaciekWin3 to increase complexity some of the separators has a length of 2. See above the changed unit tests which prove it. More challenges 😃
@MaciekWin3 to increase complexity some of the separators has a length of 2. See above the changed unit tests which prove it. More challenges 😃
Yeah, already discovered it when working on changes as i wanted separator to be char 😁. And thank you for your input, impressive work!
The test is passing, the component works, but the only issue arises when we use a 2-character separator (U+200F), as you mentioned. In this case, the length of the component becomes longer, and when I start typing, I encounter an exception: System.FormatException: 'The input string '01' was not in a correct format.'
.
Your StandardizeDateFormat
method is better because covers and forces almost all cultures. See the changed unit test bellow.
public void Using_All_Culture_StandardizeDateFormat ()
{
CultureInfo cultureBackup = CultureInfo.CurrentCulture;
DateTime date = DateTime.Parse ("1/1/1971");
foreach (var culture in CultureInfo.GetCultures (CultureTypes.AllCultures)) {
CultureInfo.CurrentCulture = culture;
var separator = culture.DateTimeFormat.DateSeparator.Trim ();
if (separator.Length > 1 && separator.Contains ('\u200f')) {
separator = separator.Replace ("\u200f", "");
}
var format = culture.DateTimeFormat.ShortDatePattern;
DateField df = new DateField (date);
if ((!culture.TextInfo.IsRightToLeft || (culture.TextInfo.IsRightToLeft && !df.Text.Contains ('\u200f')))
&& (format.StartsWith ('d') || format.StartsWith ('M'))) {
switch (culture.Name) {
case "th":
case "th-TH":
Assert.Equal ($" 01{separator}01{separator}2514", df.Text);
break;
default:
Assert.Equal ($" 01{separator}01{separator}1971", df.Text);
break;
}
} else if (culture.TextInfo.IsRightToLeft) {
if (df.Text.Contains ('\u200f')) {
// It's a Unicode Character (U+200F) - Right-to-Left Mark (RLM)
Assert.True (df.Text.Contains ('\u200f'));
switch (culture.Name) {
case "ar-SA":
Assert.Equal ($" 04{separator}11{separator}1390", df.Text);
break;
default:
Assert.Equal ($" 01{separator}01{separator}1971", df.Text);
break;
}
} else {
switch (culture.Name) {
case "ckb-IR":
case "fa":
case "fa-AF":
case "fa-IR":
case "lrc":
case "lrc-IR":
case "mzn":
case "mzn-IR":
case "ps":
case "ps-AF":
case "uz-Arab":
case "uz-Arab-AF":
Assert.Equal ($" 1349{separator}10{separator}11", df.Text);
break;
default:
Assert.Equal ($" 1971{separator}01{separator}01", df.Text);
break;
}
}
} else {
switch (culture.Name) {
default:
Assert.Equal ($" 1971{separator}01{separator}01", df.Text);
break;
}
}
}
CultureInfo.CurrentCulture = cultureBackup;
}
One workaround to fix the Arabic culture and others that have a separator length of 2 is only use the significant separator char and remove the (U+200F) and whitespaces.
void SetInitialProperties (DateTime date)
{
_sepChar = GetDataSeparator (Culture.DateTimeFormat.DateSeparator);
_format = $" {StandardizeDateFormat (Culture.DateTimeFormat.ShortDatePattern)}";
string GetDataSeparator (string separator)
{
var sepChar = separator.Trim ();
if (sepChar.Length > 1 && sepChar.Contains ('\u200f')) {
sepChar = sepChar.Replace ("\u200f", "");
}
return sepChar;
}
public CultureInfo Culture {
get => CultureInfo.CurrentCulture;
set {
if (value is not null) {
CultureInfo.CurrentCulture = value;
_sepChar = GetDataSeparator (value.DateTimeFormat.DateSeparator);
Format = " " + StandardizeDateFormat (value.DateTimeFormat.ShortDatePattern);
Text = Date.ToString (_format);
}
}
}
The test is passing, the component works, but the only issue arises when we use a 2-character separator (U+200F), as you mentioned. In this case, the length of the component becomes longer, and when I start typing, I encounter an exception:
System.FormatException: 'The input string '01' was not in a correct format.'
.
Actually the Terminal.Gui
doesn't yet handles the right-to-left mark properly and I fixed only the exception with the follow code changing, but the will go to the left when the separator is hit.
bool SetText (string text)
{
if (string.IsNullOrEmpty (text)) {
return false;
}
text = NormalizeFormat (text);
string [] vals = text.Split (_sepChar);
for (var i = 0; i < vals.Length; i++) {
if (vals [i].Contains ('\u200f')) {
vals [i] = vals [i].Replace ("\u200f", "");
}
}
I have created a draft for this, allowing you to review the current code. However, even after implementing your suggestions and making adjustments, it still doesn't properly handle Arabic dates with the presence of \u200F
.
I have created a draft for this, allowing you to review the current code. However, even after implementing your suggestions and making adjustments, it still doesn't properly handle Arabic dates with the presence of
\u200F
.
I said that I only fixed the exception from throwing because right-to-left mark isn't yet properly supported 😄
@MaciekWin3 see PR https://github.com/MaciekWin3/Terminal.Gui/pull/1 please. Thanks.
@BDisp Done
Describe the bug While working on DatePicker improvements, I discovered that DateField is not working as intended. Specifically, the date is not updating when entered via typing:
https://github.com/gui-cs/Terminal.Gui/assets/57466073/32b7bf28-7f58-4f19-9466-9780926ef848
To Reproduce Steps to reproduce the behavior: 1) Navigate to UICatalog and open the DateTime/DatePicker example. 2) Attempt to change the date using the provided interface. 3) After continuing typing beyond the last digit of the year, the application crashes.
Expected behavior Date should be changed to what user typed