Repository: lvgl/lvgl (C)

Issue:
Calendar breaks when years are numbered in ascending order
### LVGL version

9.2.2

### Platform

ESP32S3

### What happened?

**Issue**:
 When years are set in ascending order (e.g., `2025, 2026, ...`) using `lv_calendar_header_dropdown_set_year_list`, the calendar displays dates incorrectly because it assumes the list is in descending order

**Proposed fix**:
File: lv_calendar_header_dropdown.c:


```c
static void year_event_cb(lv_event_t *e)
{
    lv_obj_t *dropdown = lv_event_get_current_target(e);
    lv_obj_t *calendar = lv_event_get_user_data(e);

    uint32_t sel = lv_dropdown_get_selected(dropdown);

    const lv_calendar_date_t *d;
    d = lv_calendar_get_showed_date(calendar);

    /* Get the actual year on the options list
     * NOTE: Assumes all years are formatted as YYYY */
    const char *year_p  = lv_dropdown_get_options(dropdown);
    const uint32_t year = (year_p[0 + sel * 5] - '0') * 1000 + (year_p[1 + sel * 5] - '0') * 100 +
                          (year_p[2 + sel * 5] - '0') * 10 + (year_p[3 + sel * 5] - '0');

    lv_calendar_date_t newd = *d;
    newd.year               = year;

    lv_calendar_set_showed_date(calendar, newd.year, newd.month);
}

static void value_changed_event_cb(lv_event_t *e)
{
    lv_obj_t *header                   = lv_event_get_current_target(e);
    lv_obj_t *calendar                 = lv_obj_get_parent(header);
    const lv_calendar_date_t *cur_date = lv_calendar_get_showed_date(calendar);

    lv_obj_t *year_dd = lv_obj_get_child(header, 0);

    /* Get the first year on the options list
     * NOTE: Assumes the first 4 digits in the option list are numbers */
    const char *year_p = lv_dropdown_get_options(year_dd);
    const uint32_t year =
      (year_p[0] - '0') * 1000 + (year_p[1] - '0') * 100 + (year_p[2] - '0') * 10 + (year_p[3] - '0');

    lv_dropdown_set_selected(year_dd,
                             ((int32_t) year - cur_date->year) > 0 ? year - cur_date->year : cur_date->year - year);

    lv_obj_t *month_dd = lv_obj_get_child(header, 1);
    lv_dropdown_set_selected(month_dd, cur_date->month - 1);
}

```

### How to reproduce?

Set up the calendar with the following options:
```c
lv_obj_t *calendar = lv_calendar_create(parent);
lv_calendar_header_dropdown_create(calendar);
    const char *years = "2025\n2026\n2027\n2028\n2029\n2030\n2031\n2032\n2033\n2034\n2035\n2036\n2037\n"
                        "2038\n2039\n2040\n2041\n2042\n2043\n2044\n2045\n2046\n2047\n2048\n2049\n2050";
lv_calendar_header_dropdown_set_year_list(calendar, years);
```
And change the year of the dropdown to any year besides 2025.


Discussion: We need some feedback on this issue.

Now we mark this as "Abandoned" because there was no activity here for 14 days.

Remove the "Stale" label or comment else this will be closed in 7 days.
Hi, thanks for opening this issue. I took the liberty to open a PR for you #8315 so we can get this fixed for v9.3
Feel free to test and review it :100:



Fix (patch):
diff --git a/src/widgets/calendar/lv_calendar_header_dropdown.c b/src/widgets/calendar/lv_calendar_header_dropdown.c
index 6a696958d0ba..766f0d945004 100644
--- a/src/widgets/calendar/lv_calendar_header_dropdown.c
+++ b/src/widgets/calendar/lv_calendar_header_dropdown.c
@@ -145,17 +145,20 @@ static void year_event_cb(lv_event_t * e)
 
     uint32_t sel = lv_dropdown_get_selected(dropdown);
 
-    const lv_calendar_date_t * d;
-    d = lv_calendar_get_showed_date(calendar);
-
-    /* Get the first year on the options list
-     * NOTE: Assumes the first 4 digits in the option list are numbers */
     const char * year_p = lv_dropdown_get_options(dropdown);
-    const uint32_t year = (year_p[0] - '0') * 1000 + (year_p[1] - '0') * 100 + (year_p[2] - '0') * 10 +
-                          (year_p[3] - '0');
 
+    LV_ASSERT(3 + sel * 5 < lv_strlen(year_p));
+
+    /* NOTE: Assumes YYYY format */
+    const uint32_t year = (year_p[0 + sel * 5] - '0') * 1000 +
+                          (year_p[1 + sel * 5] - '0') * 100 +
+                          (year_p[2 + sel * 5] - '0') * 10 +
+                          (year_p[3 + sel * 5] - '0');
+
+    const lv_calendar_date_t * d;
+    d = lv_calendar_get_showed_date(calendar);
     lv_calendar_date_t newd = *d;
-    newd.year = year - sel;
+    newd.year = year;
 
     lv_calendar_set_month_shown(calendar, newd.year, newd.month);
 }
@@ -165,18 +168,14 @@ static void value_changed_event_cb(lv_event_t * e)
     lv_obj_t * header = lv_event_get_current_target(e);
     lv_obj_t * calendar = lv_obj_get_parent(header);
     const lv_calendar_date_t * cur_date = lv_calendar_get_showed_date(calendar);
-
     lv_obj_t * year_dd = lv_obj_get_child(header, 0);
-
-    /* Get the first year on the options list
-     * NOTE: Assumes the first 4 digits in the option list are numbers */
-    const char * year_p = lv_dropdown_get_options(year_dd);
-    const uint32_t year = (year_p[0] - '0') * 1000 + (year_p[1] - '0') * 100 + (year_p[2] - '0') * 10 +
-                          (year_p[3] - '0');
-
-    lv_dropdown_set_selected(year_dd, year - cur_date->year);
-
     lv_obj_t * month_dd = lv_obj_get_child(header, 1);
+    const char * year_p = lv_dropdown_get_options(year_dd);
+    /* NOTE: Assumes YYYY format */
+    const int32_t first_year = (year_p[0] - '0') * 1000 +
+                               (year_p[1] - '0') * 100 +
+                               (year_p[2] - '0') * 10 + (year_p[3] - '0');
+    lv_dropdown_set_selected(year_dd, LV_ABS(first_year - cur_date->year));
     lv_dropdown_set_selected(month_dd, cur_date->month - 1);
 }