{
  "id": "P173",
  "slug": "cron-scheduler",
  "key": "P173-cron-scheduler",
  "title": "Cron scheduler",
  "summary": "An office on UTC+1 and UTC+2 and a billing job set for 02:30 office time: on the spring change it does not run, on the autumn change it runs twice, and a report over the UTC day is not the day its readers mean.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P173 - Cron scheduler\n\nAn office's billing job set in office time and its daily report set in\nUTC, across the two clock changes of 2027.\n\n- `main.eml` - the menu, the jobs, the billing job on each kind of day,\n  what the daily report covers, and an office time of your own\n- `clock.eml` - the kinds of day, the UTC minutes at which an office time\n  happens on each, and the orders by office hour\n\nHow each part works:\n\n- The office is on UTC+1 in winter and UTC+2 in summer; the clocks go\n  forward at 02:00 on 28 March and back at 03:00 on 31 October.\n- As in the corpus case `local-time-gap-and-overlap`, an office time is a\n  label, not an instant. On 28 March 02:00 to 02:59 never happens, and the\n  billing job set for 02:30 does not run; on 31 October that hour happens\n  twice, and it runs twice. Over the year it still runs 365 times, one for\n  each day. Set for 00:30 UTC instead, it runs once every day.\n- As in `the-crontab-was-utc-and-the-report-was-local`, the scheduler on\n  UTC runs every job once, and the daily report at 00:30 UTC counts the\n  UTC day just ended, which the office reads as yesterday: in winter that\n  is 01:00 to 01:00 office time, and the 3 orders placed after midnight\n  are counted in the day before's report; in summer 5. The totals still\n  add up.\n\nWhat is checked: menu choices 1 to 5; an office time written HH:MM; a day\nfrom 1 to 4. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the jobs, the billing job and the daily report;\n- 02:30 on 28 March (it does not happen) and on 31 October (twice), 00:30\n  on 31 October (the day before in UTC), 08:15 on a winter day and 23:59\n  on a summer day.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- times: a cancel, 2:30, 24:00, 12:60, ab:cd and 12-30; days: a cancel, 0,\n  5 and x;\n- the edges of the changes: 01:59 and 03:00 on 28 March, 02:00 and 03:00\n  on 31 October.\n\nBuilt on the verified corpus cases `local-time-gap-and-overlap` (local\ntimes mapped to instants across a spring-forward and an autumn-back\ntransition) and `the-crontab-was-utc-and-the-report-was-local` (a\nscheduler on UTC and a report of the local yesterday).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P173 cron scheduler: an office's billing job set in local time and its\n# report set in UTC - the jobs, the billing job on each kind of day, what\n# the daily report covers, and an office time of your own.\nimport clock\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef digits(t):\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            return False\n    return len(t) > 0\n\ndef hhmm(m):\n    # m minutes from midnight, 0 to 1439\n    m % 60 => mm\n    int((m - mm) / 60) => h\n    \"\" => s\n    if h < 10:\n        \"0\" => s\n    s + str(h) + \":\" => s\n    if mm < 10:\n        s + \"0\" => s\n    return s + str(mm)\n\ndef utc(m):\n    if m < 0:\n        return hhmm(m + 1440) + \" UTC the day before\"\n    return hhmm(m) + \" UTC\"\n\ndef times(n):\n    if n == 1:\n        return \"once\"\n    return str(n) + \" times\"\n\ndef show_jobs():\n    \"The office keeps local time: UTC+1 in winter, UTC+2 in summer. In 2027 the clocks go forward at 02:00 on 28 March and back at 03:00 on 31 October.\" ^0\n    \"  billing   every day at 02:30 office time, the scheduler on office time\" ^0\n    \"  report    every day at 00:30 UTC, the scheduler on UTC: the orders of the day just ended\" ^0\n\ndef show_billing():\n    \"The billing job, set for 02:30 office time, on each kind of day:\" ^0\n    150 => m\n    0 => d\n    for day in clock.days():\n        clock.utc_of_local(d, m) => us\n        if len(us) == 0:\n            \"02:30 does not happen: the clocks go from 01:59 to 03:00\" => what\n        elif len(us) == 2:\n            \"02:30 happens at \" + utc(us[0]) + \" and again at \" + utc(us[1]) => what\n        else:\n            \"02:30 is \" + utc(us[0]) => what\n        (\"  \" + pad(day[0], 14) + pad(what, 58) + \"runs \" + times(len(us))) ^0\n        d + 1 => d\n    147 * len(clock.utc_of_local(2, m)) + 216 * len(clock.utc_of_local(3, m)) + len(clock.utc_of_local(0, m)) + len(clock.utc_of_local(1, m)) => runs\n    (\"Over 2027 - 147 winter days, 216 summer days and the two changes - it runs \" + str(runs) + \" times, one for each day, and still skips 28 March and bills 31 October's hour twice.\") ^0\n    \"Set for 00:30 UTC instead, it runs once every day: at 01:30 office time in winter and at 02:30 in summer.\" ^0\n\ndef show_report():\n    \"The report runs at 00:30 UTC and counts the orders of the UTC day just ended; the office reads it as yesterday. Orders by office hour, on any ordinary day:\" ^0\n    0 => h\n    while h < 24:\n        \"  \" + hhmm(h * 60) + \"-\" + hhmm(h * 60 + 300 + 59) + \"  \" => line\n        0 => k\n        while k < 6:\n            if k > 0:\n                line + \", \" => line\n            line + str(clock.orders(h + k)) => line\n            k + 1 => k\n        line ^0\n        h + 6 => h\n    (\"A day's \" + str(clock.day_orders()) + \" orders. In winter a UTC day is 01:00 to 01:00 office time: the report leaves out the \" + str(clock.first_hours(1)) + \" orders placed between midnight and 01:00, counted in the day before's report, and takes in the next day's first \" + str(clock.first_hours(1)) + \".\") ^0\n    (\"In summer it is 02:00 to 02:00: \" + str(clock.first_hours(2)) + \" orders a day are counted in the wrong day's report. The totals still add up, so no reconciliation notices.\") ^0\n\ndef own():\n    plain(input(\"office time (HH:MM)> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return\n    if len(t) != 5 or t[2] != \":\" or not digits(t[0:2]) or not digits(t[3:5]) or int(t[0:2]) > 23 or int(t[3:5]) > 59:\n        \"Type a time such as 02:30.\" ^0\n        return\n    int(t[0:2]) * 60 + int(t[3:5]) => m\n    plain(input(\"which day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> \")) => d\n    if d == \"\":\n        \"Cancelled.\" ^0\n        return\n    if len(d) != 1 or not digits(d) or int(d) < 1 or int(d) > 4:\n        \"Type a number from 1 to 4.\" ^0\n        return\n    int(d) - 1 => day\n    clock.days()[day][0] => name\n    clock.utc_of_local(day, m) => us\n    if len(us) == 0:\n        (\"On \" + name + \" \" + t + \" does not happen: the clocks go from 01:59 to 03:00. A job set for it does not run that day.\") ^0\n    elif len(us) == 2:\n        (\"On \" + name + \" \" + t + \" happens twice, at \" + utc(us[0]) + \" and at \" + utc(us[1]) + \". A job set for it runs twice.\") ^0\n    else:\n        (\"On \" + name + \" \" + t + \" is \" + utc(us[0]) + \".\") ^0\n\n\"== Cron scheduler ==\" ^0\n\"A billing job on office time and a report on UTC, across the year's two clock changes.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show_jobs()\n    elif choice == \"2\":\n        show_billing()\n    elif choice == \"3\":\n        show_report()\n    elif choice == \"4\":\n        own()\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import clock\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef digits(t):\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            return False\n    return len(t) > 0\n\ndef hhmm(m):\n    mm = m % 60\n    h = int((m - mm) / 60)\n    s = \"\"\n    if h < 10:\n        s = \"0\"\n    s = s + str(h) + \":\"\n    if mm < 10:\n        s = s + \"0\"\n    return s + str(mm)\n\ndef utc(m):\n    if m < 0:\n        return hhmm(m + 1440) + \" UTC the day before\"\n    return hhmm(m) + \" UTC\"\n\ndef times(n):\n    if n == 1:\n        return \"once\"\n    return str(n) + \" times\"\n\ndef show_jobs():\n    print(\"The office keeps local time: UTC+1 in winter, UTC+2 in summer. In 2027 the clocks go forward at 02:00 on 28 March and back at 03:00 on 31 October.\")\n    print(\"  billing   every day at 02:30 office time, the scheduler on office time\")\n    print(\"  report    every day at 00:30 UTC, the scheduler on UTC: the orders of the day just ended\")\n\ndef show_billing():\n    print(\"The billing job, set for 02:30 office time, on each kind of day:\")\n    m = 150\n    d = 0\n    for day in clock.days():\n        us = clock.utc_of_local(d, m)\n        if len(us) == 0:\n            what = \"02:30 does not happen: the clocks go from 01:59 to 03:00\"\n        elif len(us) == 2:\n            what = \"02:30 happens at \" + utc(us[0]) + \" and again at \" + utc(us[1])\n        else:\n            what = \"02:30 is \" + utc(us[0])\n        print(\"  \" + pad(day[0], 14) + pad(what, 58) + \"runs \" + times(len(us)))\n        d = d + 1\n    runs = 147 * len(clock.utc_of_local(2, m)) + 216 * len(clock.utc_of_local(3, m)) + len(clock.utc_of_local(0, m)) + len(clock.utc_of_local(1, m))\n    print(\"Over 2027 - 147 winter days, 216 summer days and the two changes - it runs \" + str(runs) + \" times, one for each day, and still skips 28 March and bills 31 October's hour twice.\")\n    print(\"Set for 00:30 UTC instead, it runs once every day: at 01:30 office time in winter and at 02:30 in summer.\")\n\ndef show_report():\n    print(\"The report runs at 00:30 UTC and counts the orders of the UTC day just ended; the office reads it as yesterday. Orders by office hour, on any ordinary day:\")\n    h = 0\n    while h < 24:\n        line = \"  \" + hhmm(h * 60) + \"-\" + hhmm(h * 60 + 300 + 59) + \"  \"\n        k = 0\n        while k < 6:\n            if k > 0:\n                line = line + \", \"\n            line = line + str(clock.orders(h + k))\n            k = k + 1\n        print(line)\n        h = h + 6\n    print(\"A day's \" + str(clock.day_orders()) + \" orders. In winter a UTC day is 01:00 to 01:00 office time: the report leaves out the \" + str(clock.first_hours(1)) + \" orders placed between midnight and 01:00, counted in the day before's report, and takes in the next day's first \" + str(clock.first_hours(1)) + \".\")\n    print(\"In summer it is 02:00 to 02:00: \" + str(clock.first_hours(2)) + \" orders a day are counted in the wrong day's report. The totals still add up, so no reconciliation notices.\")\n\ndef own():\n    t = plain(input(\"office time (HH:MM)> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return\n    if len(t) != 5 or t[2] != \":\" or not digits(t[0:2]) or not digits(t[3:5]) or int(t[0:2]) > 23 or int(t[3:5]) > 59:\n        print(\"Type a time such as 02:30.\")\n        return\n    m = int(t[0:2]) * 60 + int(t[3:5])\n    d = plain(input(\"which day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> \"))\n    if d == \"\":\n        print(\"Cancelled.\")\n        return\n    if len(d) != 1 or not digits(d) or int(d) < 1 or int(d) > 4:\n        print(\"Type a number from 1 to 4.\")\n        return\n    day = int(d) - 1\n    name = clock.days()[day][0]\n    us = clock.utc_of_local(day, m)\n    if len(us) == 0:\n        print(\"On \" + name + \" \" + t + \" does not happen: the clocks go from 01:59 to 03:00. A job set for it does not run that day.\")\n    elif len(us) == 2:\n        print(\"On \" + name + \" \" + t + \" happens twice, at \" + utc(us[0]) + \" and at \" + utc(us[1]) + \". A job set for it runs twice.\")\n    else:\n        print(\"On \" + name + \" \" + t + \" is \" + utc(us[0]) + \".\")\n\nprint(\"== Cron scheduler ==\")\nprint(\"A billing job on office time and a report on UTC, across the year's two clock changes.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show_jobs()\n    elif choice == \"2\":\n        show_billing()\n    elif choice == \"3\":\n        show_report()\n    elif choice == \"4\":\n        own()\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "clock.eml",
      "eml": "# P173 cron scheduler - an office on UTC+1 in winter and UTC+2 in summer,\n# with the clocks going forward at 02:00 on 28 March 2027 and back at 03:00\n# on 31 October. As in the corpus case local-time-gap-and-overlap, a local\n# time is a label, not an instant: on the spring day 02:00 to 02:59 never\n# happens, and on the autumn day it happens twice, so a job set for 02:30\n# local does not run, or runs twice. And as in\n# the-crontab-was-utc-and-the-report-was-local, a scheduler on UTC runs\n# every job exactly once, and a daily report over the UTC day is not the\n# day its readers mean. Times are minutes from midnight.\n\ndef days():\n    # the kinds of day: [name, what happens to the clocks]\n    return [[\"28 March\", \"forward an hour at 02:00\"], [\"31 October\", \"back an hour at 03:00\"], [\"a winter day\", \"UTC+1 all day\"], [\"a summer day\", \"UTC+2 all day\"]]\n\ndef utc_of_local(day, m):\n    # the UTC minutes at which local minute m happens on that day (0, 1 or\n    # 2 of them); a negative minute is on the day before\n    if day == 0:\n        if m < 120:\n            return [m - 60]\n        if m < 180:\n            return []\n        return [m - 120]\n    if day == 1:\n        if m < 120:\n            return [m - 120]\n        if m < 180:\n            return [m - 120, m - 60]\n        return [m - 60]\n    if day == 2:\n        return [m - 60]\n    return [m - 120]\n\ndef offset(day):\n    # minutes local time is ahead of UTC, all day long (days 2 and 3)\n    if day == 2:\n        return 60\n    return 120\n\ndef orders(h):\n    # orders placed in the local hour h, on any ordinary day\n    return [3, 2, 1, 1, 0, 1, 2, 5, 9, 14, 18, 20, 24, 22, 19, 18, 20, 23, 26, 22, 15, 10, 7, 5][h]\n\ndef day_orders():\n    0 => t\n    0 => h\n    while h < 24:\n        t + orders(h) => t\n        h + 1 => h\n    return t\n\ndef first_hours(n):\n    # orders in the first n local hours of a day\n    0 => t\n    0 => h\n    while h < n:\n        t + orders(h) => t\n        h + 1 => h\n    return t\n",
      "python": "def days():\n    return [[\"28 March\", \"forward an hour at 02:00\"], [\"31 October\", \"back an hour at 03:00\"], [\"a winter day\", \"UTC+1 all day\"], [\"a summer day\", \"UTC+2 all day\"]]\n\ndef utc_of_local(day, m):\n    if day == 0:\n        if m < 120:\n            return [m - 60]\n        if m < 180:\n            return []\n        return [m - 120]\n    if day == 1:\n        if m < 120:\n            return [m - 120]\n        if m < 180:\n            return [m - 120, m - 60]\n        return [m - 60]\n    if day == 2:\n        return [m - 60]\n    return [m - 120]\n\ndef offset(day):\n    if day == 2:\n        return 60\n    return 120\n\ndef orders(h):\n    return [3, 2, 1, 1, 0, 1, 2, 5, 9, 14, 18, 20, 24, 22, 19, 18, 20, 23, 26, 22, 15, 10, 7, 5][h]\n\ndef day_orders():\n    t = 0\n    h = 0\n    while h < 24:\n        t = t + orders(h)\n        h = h + 1\n    return t\n\ndef first_hours(n):\n    t = 0\n    h = 0\n    while h < n:\n        t = t + orders(h)\n        h = h + 1\n    return t\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n4\n\n4\n2:30\n4\n24:00\n4\n12:60\n4\nab:cd\n4\n12-30\n4\n02:30\n\n4\n02:30\n0\n4\n02:30\n5\n4\n02:30\nx\n4\n01:59\n1\n4\n03:00\n1\n4\n02:00\n2\n4\n03:00\n2\n5\n",
      "screen": "== Cron scheduler ==\nA billing job on office time and a report on UTC, across the year's two clock changes.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> \nCancelled.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 2:30\nType a time such as 02:30.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 24:00\nType a time such as 02:30.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 12:60\nType a time such as 02:30.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> ab:cd\nType a time such as 02:30.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 12-30\nType a time such as 02:30.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 02:30\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> \nCancelled.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 02:30\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 0\nType a number from 1 to 4.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 02:30\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 5\nType a number from 1 to 4.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 02:30\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> x\nType a number from 1 to 4.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 01:59\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 1\nOn 28 March 01:59 is 00:59 UTC.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 03:00\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 1\nOn 28 March 03:00 is 01:00 UTC.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 02:00\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 2\nOn 31 October 02:00 happens twice, at 00:00 UTC and at 01:00 UTC. A job set for it runs twice.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 03:00\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 2\nOn 31 October 03:00 is 02:00 UTC.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n4\n02:30\n1\n4\n02:30\n2\n4\n00:30\n2\n4\n08:15\n3\n4\n23:59\n4\n5\n",
      "screen": "== Cron scheduler ==\nA billing job on office time and a report on UTC, across the year's two clock changes.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 1\nThe office keeps local time: UTC+1 in winter, UTC+2 in summer. In 2027 the clocks go forward at 02:00 on 28 March and back at 03:00 on 31 October.\n  billing   every day at 02:30 office time, the scheduler on office time\n  report    every day at 00:30 UTC, the scheduler on UTC: the orders of the day just ended\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 2\nThe billing job, set for 02:30 office time, on each kind of day:\n  28 March      02:30 does not happen: the clocks go from 01:59 to 03:00  runs 0 times\n  31 October    02:30 happens at 00:30 UTC and again at 01:30 UTC         runs 2 times\n  a winter day  02:30 is 01:30 UTC                                        runs once\n  a summer day  02:30 is 00:30 UTC                                        runs once\nOver 2027 - 147 winter days, 216 summer days and the two changes - it runs 365 times, one for each day, and still skips 28 March and bills 31 October's hour twice.\nSet for 00:30 UTC instead, it runs once every day: at 01:30 office time in winter and at 02:30 in summer.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 3\nThe report runs at 00:30 UTC and counts the orders of the UTC day just ended; the office reads it as yesterday. Orders by office hour, on any ordinary day:\n  00:00-05:59  3, 2, 1, 1, 0, 1\n  06:00-11:59  2, 5, 9, 14, 18, 20\n  12:00-17:59  24, 22, 19, 18, 20, 23\n  18:00-23:59  26, 22, 15, 10, 7, 5\nA day's 287 orders. In winter a UTC day is 01:00 to 01:00 office time: the report leaves out the 3 orders placed between midnight and 01:00, counted in the day before's report, and takes in the next day's first 3.\nIn summer it is 02:00 to 02:00: 5 orders a day are counted in the wrong day's report. The totals still add up, so no reconciliation notices.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 02:30\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 1\nOn 28 March 02:30 does not happen: the clocks go from 01:59 to 03:00. A job set for it does not run that day.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 02:30\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 2\nOn 31 October 02:30 happens twice, at 00:30 UTC and at 01:30 UTC. A job set for it runs twice.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 00:30\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 2\nOn 31 October 00:30 is 22:30 UTC the day before.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 08:15\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 3\nOn a winter day 08:15 is 07:15 UTC.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 4\noffice time (HH:MM)> 23:59\nwhich day (1 28 March, 2 31 October, 3 a winter day, 4 a summer day)> 4\nOn a summer day 23:59 is 21:59 UTC.\n\n1) the jobs  2) the billing job  3) the daily report  4) an office time of your own  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "local-time-gap-and-overlap",
      "caseId": "253-local-time-gap-and-overlap",
      "title": "Local time gap and overlap — a label, not an instant"
    },
    {
      "slug": "the-crontab-was-utc-and-the-report-was-local",
      "caseId": "691-the-crontab-was-utc-and-the-report-was-local",
      "title": "The crontab was utc and the report was local"
    }
  ],
  "updated": "2026-10-19"
}
