2

Using Godot 3.6 on Windows: How to calculate the ISO week number of a date given the following parameters: day, month, year?

I could not find a way to calculate the week number from the built-in class Time

I've also looked into the Calendar Button Plugin for Godot Engine 3.2.3 repo by Ivan Skodje but could not find any answer or inspiration.

One way I managed to achieve this is by using PowerShell by using the following self-defined methods:

# Executes the [ script ] in Windows PowerShell
static func execute(script, open_console: bool = false, freeze_loop: bool = true) -> Array:
    if OS.get_name() != "Windows":
        return []
    var output: Array = []
    var arguments: PoolStringArray = PoolStringArray([])
    match typeof(script):
        TYPE_STRING:
            arguments.append(script as String)
        TYPE_ARRAY, TYPE_STRING_ARRAY:
            for line in script:
                arguments.append("\n%s" % line)
        _:
            return []
    OS.execute("powershell.exe", arguments, freeze_loop, output, false, open_console)
    return output

Inspired by the following answer: Get the ISO 8601 Week of Year of a given date in Powershell

# Returns the ISO week number of the specified date
static func get_iso_week(day: int, month: int, year: int) -> int:
    var script: PoolStringArray = PoolStringArray([
        "$date = [DateTime] '%d-%d-%d'" % [year, month, day],
        ("(Get-Culture).Calendar.GetWeekOfYear(($date).AddDays(-[Int]"
        + "(($date).AddDays(-1)).DayOfWeek+3), 2, 1)")
        ])
    var result: Array = execute(script)
    if result.empty():
        return 0
    return result[0].to_int()

Using the above methods ^ the result is accurate and as expected, but the execution time is quite slow especially when calling this function 6 times (once for each week row in a standard calendar), and I don't know how to program a method in PowerShell that can return a list of all week numbers by the specified month and year (this was, only one PowerShell execution will be performed instead of 6, but I still find this suboptimal)

ChatGPT also failed to find an answer suitable for Godot 3.6 no matter how many times I need to correct it. (Always suggesting classes and methods that do not exist)

KanaszM
  • 151
  • 9
  • Once you get the first day of the year you do not need to repeat the calculation for every date. The week number is simply (current Day - First Day of year) / 7. – jdweng Mar 17 '23 at 12:04
  • @jdweng: No, it is not. Please read up on the ISO 8601 standard. – Gustav Mar 17 '23 at 12:34
  • @Gustav : A week is 7 days not matter what specification you use. The only difference is where the first day of the year starts. It could be the last few days of the previous year. Once you get the first day of the year the week number always changes 7 days later. – jdweng Mar 17 '23 at 13:04
  • @jdweng: Yes, but for most years, the first week does _not_ start the the first day of year - as you wrote. – Gustav Mar 17 '23 at 13:56
  • 1
    @Gustav : I meant the first day of week at beginning of the year. – jdweng Mar 17 '23 at 14:12
  • I don't know if you want to look into calling .net directly. – js2010 Mar 17 '23 at 16:17

2 Answers2

1

The following is based on the Wikipedia article ISO week date:

func get_iso_week_number(day: int, month: int, year: int) -> int:
    var start: int = OS.get_unix_time_from_datetime({"year": year, "month": 1, "day": 1})
    var current: int = OS.get_unix_time_from_datetime({"year": year, "month": month, "day": day})
    var days_since_year_start: int = int((current - start) / 86400.0)
    var iso_day_of_year: int = days_since_year_start + 1

    var prior_year: int = year - 1
    var prior_year_last_weekday : int = last_weekday(prior_year)
    var iso_weekday: int = (prior_year_last_weekday + days_since_year_start) % 7 + 1

    # warning-ignore:integer_division
    var iso_week_number: int = int((iso_day_of_year - iso_weekday + 10) / 7)

    if iso_week_number < 1:
        iso_week_number = weeks(prior_year)
    elif iso_week_number > weeks(year):
        iso_week_number = 1

    return iso_week_number


func last_weekday(year:int) -> int:
    # warning-ignore:integer_division
    # warning-ignore:integer_division
    # warning-ignore:integer_division
    return (year + (year / 4) - (year / 100) + (year / 400)) % 7


func weeks(year:int) -> int:
    return 52 + (1 if last_weekday(year) == 4 or last_weekday(year - 1) == 3 else 0)

Note that we use a function which I called last_weekday which gives us the weekday of December 31 of a given year. First we need it to compute the weekday of arbitrary days, and second for the weeks function which tells us if a year has 52 or 53 weeks.

Theraot
  • 31,890
  • 5
  • 57
  • 86
0

So I have managed to come-up with the following solution:

static func get_iso_week_number(day: int, month: int, year: int) -> int:
    var start: int = OS.get_unix_time_from_datetime({"year": year, "month": 1, "day": 1})
    var current: int = OS.get_unix_time_from_datetime({"year": year, "month": month, "day": day})
    var days_since_year_start: float = (current - start) / 86400.0
    var iso_day_of_year: int = int(days_since_year_start) + 1
    var iso_weekday: int = (int(days_since_year_start) - 1) % 7
    var iso_week_start: int = current - (86400 * iso_weekday) - (86400 * (iso_day_of_year - 1))
    var iso_week_number: int = int((iso_day_of_year - iso_weekday + 10) / 7)
    if iso_week_number < 1:
        iso_week_number = get_iso_week_number(31, 12, year - 1)
    elif iso_week_number > 52:
        if get_iso_week_number(1, 1, year + 1) == 1:
            iso_week_number = 1
    return iso_week_number

While testing with dates from the year 2023, every week number seems to be ok except for 1-January-2023 where the result is returned as 1 instead of 52

KanaszM
  • 151
  • 9