Skip to content
Maintenance & updates

Week 53 and Year End: Avoiding Date Errors in Your Shop

January 1, 2027 still falls in week 53 of 2026. How to check week formats, time zones, number ranges, holidays and promotions in your shop before New Year.

22 min read JahreswechselShop-BetriebCronjobsWartung

In short and in plain language

A new year starts on January 1, 2027. But the new week only starts on January 4. The days before that still belong to week 53 of 2026.

Some shops then put the wrong year next to the week. Then reports and file names are wrong.

The time of day matters too. The server often uses a different time than Berlin. Then a bill from the night of January 1 can end up in the old year.

Also check holidays, sales and gift cards. Sales and gift cards often end at the end of the year.

Test this early. To do that, you set the clock forward in a copy of the shop.

January 1, 2027 starts a new calendar year, but not a new calendar week. Under ISO 8601, week 53 of 2026 runs from Monday, December 28, 2026 to Sunday, January 3, 2027. That gives 2026 a total of 53 ISO weeks instead of the usual 52 (strftime(3)). Days like these reveal how a shop handles dates: weekly reports jump into the wrong year, export files get names that already exist, an invoice from the first hour of the year carries the date of December 31, and the delivery estimate counts New Year's Day as if it were a working day. None of these errors is new, but at the turn of 2026/2027 they come together, and they hit a week when many teams are thinly staffed between the holidays. This article shows where a shop's date logic breaks at year end, how to check week formats, time zones, number ranges, holidays and promotion end dates, and how a dry run on staging lets you rehearse the year change in advance.

Key takeaways

  • ISO week-based year 2026 has 53 weeks (strftime(3)): week 53 runs from December 28, 2026 to January 3, 2027, so January 1, 2027 still belongs to week 2026-W53.
  • Formatting weeks with the calendar year produces "2027-W53" for New Year's Day. Use the ISO week-based year instead: o rather than Y in PHP (PHP manual), %G rather than %Y in scripts (strftime(3)), YEARWEEK(date, 3) in SQL (MariaDB documentation).
  • Midnight in Berlin is 11 p.m. UTC in winter (§ 4 EinhZeitG). Shopware stores points in time in UTC (Shopware documentation), so documents and reports need a defined business time zone.
  • Retention periods start at the end of the calendar year (§ 147 AO), and so does the standard limitation period of three years (§ 195 BGB, § 199 BGB). Deletion jobs therefore count from year end; voucher validity is a question for legal advice.
  • Shopware calculates delivery periods from the current date (Shopware documentation). If you show specific delivery dates, your calendar needs the 2027 holidays, including the movable ones that follow Easter Sunday on March 28.
  • A dry run on staging with the clock set forward, for example to December 31 at 11:30 p.m., exposes format errors, promotion end dates and cron jobs before the real event. Fixes belong ahead of a December change freeze.

Why 2026 has 53 calendar weeks

Week numbering under ISO 8601 follows three rules: a week starts on Monday, the weeks of a year are numbered from 01 to 52 or 53, and week 01 is the first week with at least four days in the new year (strftime(3)). The same manual page puts it another way: week 01 is the week containing the first Thursday of the year, or the week that contains January 4. That is the heart of the problem. A calendar week does not belong to the year in which its Monday falls, but to the year in which its Thursday falls. That year is called the ISO week-based year, and at the edges it differs from the calendar year.

For the turn of 2026/2027 this means: January 1, 2026 was a Thursday, so ISO week-based year 2026 already began on Monday, December 29, 2025. The week from December 28, 2026 to January 3, 2027 contains Thursday, December 31, 2026, and therefore counts as week 53 of 2026. New Year's Day and January 2 and 3, 2027 thus fall in the new calendar year but in the old week-based year. Week 1 of 2027 only begins on Monday, January 4, 2027. The Python documentation describes the same thing from the calendar side: an ISO year consists of 52 or 53 full weeks that start on Monday and end on Sunday (Python documentation). By the same rules, 2015 and 2020 also had a week 53, and the next one comes in 2032.

The manual page offers an example that reads like a template for 2027: January 1, 2010 was a Friday, only three days of that calendar week fell in the new year, and so they counted as week 53 of 2009 (strftime(3)). The same situation last occurred on January 1, 2021, and it returns on January 1, 2027. If you have rebuilt your shop's date logic or added extensions since then, do not assume it still handles the case. The table shows the affected days.

DateWeekdayCalendar yearISO weekCorrect labelLabel with calendar year
2026-12-28Monday2026532026-W532026-W53
2026-12-31Thursday2026532026-W532026-W53
2027-01-01Friday2027532026-W532027-W53 (wrong)
2027-01-03Sunday2027532026-W532027-W53 (wrong)
2027-01-04Monday202712027-W012027-W01

The format error: calendar year instead of week-based year

Most date libraries know two kinds of year: the calendar year and the ISO week-based year. In PHP they are the format characters Y and o. The manual describes o as the ISO week-numbering year, which has the same value as Y, except that if the ISO week W belongs to the previous or next year, that year is used instead (PHP manual). Formatting a week label as Y-\WW therefore combines the calendar year with the ISO week and produces the label "2027-W53" on January 1, 2027, a week that does not exist in 2027. The correct format is o-\WW, which yields "2026-W53".

For Shopware this is more than a footnote, because the storefront, mail templates and document templates format dates in Twig. Twig's date filter uses the same format characters as the PHP date function (Twig documentation). A template that prints a delivery week, a pickup week or the calendar week in a weekly report inherits the error one to one if it uses Y instead of o. The same applies to extensions that calculate delivery weeks for freight items or the rhythm of subscriptions. The following example shows the four days on which the two variants differ, or just happen to agree.

week-label.php
<?php
foreach (['2025-12-29', '2026-12-31', '2027-01-01', '2027-01-04'] as $day) {
    $d = new DateTimeImmutable($day, new DateTimeZone('Europe/Berlin'));
    echo $day, '  Y-W: ', $d->format('Y-\WW'), '  o-W: ', $d->format('o-\WW'), "\n";
}
// Output with PHP 8.5:
// 2025-12-29  Y-W: 2025-W01  o-W: 2026-W01
// 2026-12-31  Y-W: 2026-W53  o-W: 2026-W53
// 2027-01-01  Y-W: 2027-W53  o-W: 2026-W53
// 2027-01-04  Y-W: 2027-W01  o-W: 2027-W01

The first line shows that the error is not limited to the year change after a week 53. On December 29, 2025, the first week of ISO week-based year 2026 had already begun; formatted with Y, it became "2025-W01", the same label as the first week of January 2025. The calendar year and the week-based year agree on every day only in years with 53 ISO weeks, namely 2015, 2020 and 2026, and not again until 2032. In every other year, the format error hits the first days of January, the last days of December or both, one to three days in total. The fact that nobody has noticed it so far therefore means little: it often appears in a report that nobody reads closely that week.

Scripts and other languages have the same trap with different characters. In date and strftime, %G is the year that goes with the ISO week %V; it has the same value as %Y, except when the ISO week belongs to the previous or next year (strftime(3)). The Python documentation states explicitly that %G and %Y are not interchangeable (Python documentation). In format patterns based on the Unicode standard for date formats, used by many libraries in Java, Swift or JavaScript, uppercase Y stands for the year in the week-based calendar, which may differ from the calendar year y near a year transition (Unicode LDML). There the trap works the other way round: writing YYYY-MM-dd instead of yyyy-MM-dd puts the wrong year into a perfectly ordinary date around the turn of the year. Which days are affected even depends on the locale, because the first day of the week and the minimum number of days in the first week can change the year to which a date is assigned (Unicode LDML).

Week numbers from the database

Many weekly reports are not built in PHP but directly in SQL: revenue per calendar week, returns per week, orders compared with the same week last year. MariaDB calculates the week number with WEEK(), and the result depends on a mode. Modes 0 to 7 differ in whether the week starts on Sunday or Monday, whether counting starts at 0 or 1, and which week counts as the first; only mode 3 numbers weeks according to ISO 8601 (MariaDB documentation). If the second argument is omitted, the value of the system variable default_week_format applies (MariaDB documentation), and its default is 0, meaning weeks start on Sunday and counting starts at week 0 (MariaDB documentation).

week-label.sql
SELECT WEEK('2027-01-01')         AS no_mode,         -- 0
       WEEK('2027-01-01', 3)      AS iso_week,        -- 53
       CONCAT(YEAR('2027-01-01'), '-W', WEEK('2027-01-01', 3))
                                  AS mixed,           -- 2027-W53
       YEARWEEK('2027-01-01', 3)  AS iso_year_week;   -- 202653

Measured on MariaDB 11.8, the call without a mode returns week 0 for January 1, 2027, and the ISO mode returns week 53. Putting YEAR() next to it rebuilds the label "2027-W53". The correct choice is YEARWEEK(date, 3): the function returns year and week together, and the documentation points out that the year in the result may differ from the year of the date in the first and the last week of the year (MariaDB documentation). Here, that is exactly what you want.

For reporting, this leads to a simple rule: calculate the week label in exactly one place, as a pair of ISO week-based year and ISO week, and have every report, export and dashboard take it from there. Where a spreadsheet processes the data further, put the finished label into the export file as text instead of letting the spreadsheet recalculate it from the date. Comparing week 53 with the previous year needs an additional decision, because 2025 had no week 53. You can compare it with week 52 of the previous year or with the same calendar days; the report should state which option applies, so nobody reads a January slump into what is merely a counting convention.

Midnight in Berlin is 11 p.m. UTC

The second source of errors at year end is the time zone. The legal time in Germany in winter is Central European Time, defined as Coordinated Universal Time plus one hour (§ 4 EinhZeitG). The turn of the year at midnight in Berlin is therefore 11 p.m. on December 31 in UTC. An order placed at 12:30 a.m. on January 1, 2027 still carries the date December 31, 2026 in UTC. How time zones affect cron jobs and timestamps at the switch to winter time is covered in the article on the clock change on October 25.

Shopware normalizes stored date and time values to UTC, while different parts of the system choose their display time zone differently (Shopware documentation). The storefront can follow the browser's time zone, the Administration the profile of the logged-in user. With Shopware 6.7.13.0, sales channels gained an optional business time zone (Shopware documentation). When it is set, documents for that sales channel are rendered in this time zone. When it is empty, the result in version 6.7 depends on the entry point: a document created during a storefront request can pick up the customer's browser time zone, while a document from the Administration or the message queue uses Twig's configured default time zone. From Shopware 6.8, documents without a business time zone consistently use Twig's default time zone, which is UTC unless someone changes it (Shopware documentation).

What shifts in the first hour of the year

If an invoice, a delivery note or a daily report is created between midnight and 1 a.m. on New Year's morning, the time zone decides which year it lands in. This affects the invoice date, any year component in the document number, annual revenue in reports and the period an export pulls data for. The business time zone per sales channel and the time zones of server, PHP, database session and cron service should therefore be brought to a known, documented state before December 31, which for a shop in Germany is usually Europe/Berlin.

Number ranges, documents and retention periods

Number ranges are a well-known checkpoint at year end, which is why they are only one of several here. Shopware describes number ranges as series of characters and digits that uniquely identify orders, customers or documents; depending on requirements, it may be necessary to include day, month or year in the number range (Shopware documentation). In advanced mode, a pattern accepts placeholders such as {date} or {date_d.m.Y}, while the incrementing number sits in {n} and starts at the specified start number (Shopware documentation). The documentation does not describe an automatic restart on January 1.

For invoices, the German VAT Act requires a sequential number with one or more series of numbers that the issuer assigns only once (§ 14 Abs. 4 Nr. 4 UStG). A year component in the number is therefore possible, but it needs care at the year boundary: it has to reflect the calendar year, not the week-based year, it has to be generated in the same time zone as the invoice date, and switching the pattern on January 1 must not produce any number a second time. Whether and how a number range restarts at year end is something to agree with your tax advisor. What you can check technically in advance is which number the first document of the new year will get and which date it will show.

Deletion jobs also depend on the year boundary. Under the German Fiscal Code, the retention period starts at the end of the calendar year in which, for example, the accounting voucher was created (§ 147 AO). Books and records must be kept for ten years, accounting vouchers for eight years and other documents for six years, unless other tax laws allow shorter periods (§ 147 AO). A deletion job that calculates "creation date plus eight years" therefore deletes too early: an accounting voucher from January 5, 2026 must not be removed as early as January 2034, because its period ends at the end of 2034 at the earliest. How retention and data protection fit together in a shop is covered in the article on GDPR-compliant backup and retention.

Delivery times and 2027 holidays

Shopware displays delivery times from a unit plus a minimum and a maximum value and can use them to calculate an expected delivery period in the cart. The calculation of that period is based on the current date (Shopware documentation). Of all days, the documentation's example table starts on January 1, 2020: with a delivery time of 1 to 3 days and stock available, the result is the period from January 2 to 4, 2020 (Shopware documentation). Holidays do not appear in this description. If your shop shows a specific delivery date or delivery week, whether through an extension, a shipping integration or custom logic, check where its holiday calendar comes from and whether it already knows 2027.

Fixed holidays are entered quickly, movable ones are not. In 2027, Easter Sunday falls on March 28; Good Friday is therefore on March 26, Easter Monday on March 29, Ascension Day on May 6 and Whit Monday on May 17. If you calculate these dates in PHP, be aware of one quirk: easter_date() uses the TZ environment variable instead of PHP's configured time zone, which can lead to unexpected behavior when combined with other date functions (PHP manual). The manual names easter_days() together with DateTime as the workaround.

Then there are the federal states, because most public holidays in Germany are a matter of state law. In Bavaria, for example, Epiphany is a public holiday throughout the state (Art. 1 BayFTG); in 2027 it falls on the Wednesday of the first week of January. For delivery times, what matters is not only where the shop is based but also where the warehouse and the shipping provider operate. Christmas Eve and New Year's Eve are not public holidays; whether warehouse and parcel service work full days on them is governed by their own operating calendars. The following list sums up what the calendar of your delivery time logic should contain.

  • Complete holiday list for 2027, including the movable dates that follow Easter
  • Federal state of warehouse and shipping origin stored, not just the company headquarters
  • Shutdown days between Christmas and New Year entered as separate blocked days
  • Last shipping day before Christmas announced in the shop and matched with the delivery time logic
  • Cart display for December 30 and January 4 checked on staging

Promotions, vouchers and deadlines at year end

For retail, the turn of the year falls in a high-revenue season. The German Retail Federation puts the share of the last two months in annual retail sales at 18.5 percent (HDE). For online retail it forecast sales of 22.2 billion euros in the 2025 Christmas season (HDE), and vouchers once again topped the list of the most popular Christmas gifts (HDE). For date logic this means many promotions expire shortly before the turn of the year, and many vouchers are redeemed shortly after it.

Shopware promotions have one field each for the start and end of validity, which define whether and for what period a promotion runs (Shopware documentation). At year end, three details are worth a look. First, the time of day: a promotion "until December 31" should run until 11:59 p.m. and not end at midnight at the start of that day. Second, the time zone: check the end in the dry run against Berlin time, not just in the form. Third, the follow-up promotion: a New Year promotion that starts at midnight on January 1 must not overlap with the expiring one if the two are not meant to be combined. How to protect discount codes against abuse during peak season is covered in the article on discount codes in Black Week.

Vouchers are the second point. The standard limitation period is three years (§ 195 BGB) and, unless otherwise provided, begins at the end of the year in which the claim arose and the creditor became aware of it or should have become aware of it without gross negligence (§ 199 BGB). A validity of "three years from purchase" and one of "three years from year end" therefore lead to different end dates, for a voucher bought on December 20, 2026 either December 20, 2029 or December 31, 2029. Which validity applies to your vouchers is a question for legal advice. Maintenance makes sure that the setting in the shop matches what your terms say.

Cron has no notion of a year. Each line of a crontab has five time and date fields followed by the command (crontab(5)): minute, hour, day of month, month and day of week. A job entered for December 31 at 10 p.m. therefore runs again every year, even if it was only meant to run once, for example to end a promotion. The shorthand @yearly stands for 0 0 1 1 * (crontab(5)), which means midnight on January 1 in the time zone of the cron service. If the server runs on UTC, such a yearly job starts at 1 a.m. Berlin time; if the report also calculates the year in UTC, it counts orders from the first Berlin hour of the new year toward the old one. Which jobs typically run in a shop and how to detect silent failures is covered in the article on silent background job failures.

File names that include a year have the same problem as reports. A backup or export script that names its files with the pattern %Y-W%V wrote a file labeled "2025-W01" on December 29, 2025, the same name as in the first week of January 2025. Depending on retention, that either overwrites an older file or lumps two weeks together, and a rotation that sorts by name may delete the wrong backup. On January 1, 2027 the opposite happens: the label "2027-W53" sorts behind every week of 2027 in an ordered list. The right choices are %G-W%V for week labels and %F for anything that does not run weekly; %F is the full date in the format %Y-%m-%d (strftime(3)).

The most visible date in a shop is in the footer. A hard-coded year such as "© 2026" looks outdated on January 2, and customers notice it sooner than a wrong week format. If the year is generated dynamically, it has to come from the current date in the shop's time zone, and a page cache keeps serving the old version until it is refreshed. Whether the footer shows a year at all is a design question; if it does, it should be right on New Year's morning. The same goes for years in legal texts, shipping notes and banners that still say "2026".

Dry run on staging with the clock set forward

Waiting for December 31 is not a test strategy. Most of the errors described here can be triggered in advance if a staging environment sets its clock to the critical points in time: December 31, 2026 at 11:30 p.m., January 1, 2027 at 12:30 a.m., and January 4, 2027 as the first working day of week 1. At code level there are dedicated tools for this. In PHP applications built on Symfony, the Clock component provides a test clock that starts at a fixed point in time and does not move forward on its own (Symfony documentation). Your own code that takes its time from such a clock can be moved across the year change on purpose.

For a dry run of the whole application that is not enough, because the database, the cron service and extensions determine the time themselves. There, the system time of the staging environment is set forward, and only there. A few precautions are part of it: mail delivery and payment integrations in test mode, no connection to live interfaces, and a look at certificates and tokens whose validity may look different with the clock set forward. How to set up a staging environment for this is covered in the article on staging environments for safe updates.

Dec 31, 2026 at 11:30 p.m., Jan 1, 2027 at 12:30 a.m., and Jan 4, 2027 as the first working day of week 1. Write down in advance which outputs you expect for each point in time.

The dry run is also a chance to make the check permanent. An automated test that checks the week label for December 29, 2025, January 1, 2027 and January 3, 2027 costs a few lines and fails on every later change that swaps o back to Y. The same goes for a test that creates an invoice at 12:30 a.m. on New Year's morning and checks its date.

The checklist before January 1

The following list sums up the points. It is ordered by effort: at the top what a search through the code can settle, at the bottom what needs a dry run.

  • Week format: search code, Twig templates, mail templates and scripts for Y-W, %Y-W%V, YYYY and WEEK( without a mode, and switch to the ISO week-based year.
  • Reports and exports: calculate the week label in one place, export it as text and decide how week 53 is compared with the previous year.
  • Time zone: document and deliberately set the business time zone per sales channel and the time zones of PHP, database session and cron service.
  • Number ranges: check patterns with a date component, determine the first number of the new year in advance, agree changes with your tax advisor.
  • Deletion jobs: count periods from year end, not from the creation date.
  • 2027 holidays: add movable dates, state holidays and shutdown days to the calendar of your delivery time logic.
  • Promotions and vouchers: check end dates including the time of day, check follow-up promotions for overlaps, compare voucher validity with your terms.
  • Cron jobs and file names: remove one-off jobs with fixed dates, check yearly jobs for their time zone, switch file names to %G-W%V or the full date.

In terms of timing, the list belongs in November. If you plan a change freeze in December, you will not want to deploy a fix to the week format then, and a second dry run needs time as well. How a change freeze is built into the maintenance plan and which other deadlines fall in December is covered in the article on the change freeze before peak season. For invoices, 2027 brings another changeover, described in the article on e-invoicing from 2027.

What maintenance takes care of at year end

Date errors at year end are rarely spectacular, but they are expensive afterward: reports have to be recalculated, documents checked and exports delivered again, often in a week with thin staffing. Shopware maintenance covers monitoring and maintaining Shopware cron jobs and message queue workers as well as updates that are tested on a staging environment first. The year change can be added there as a fixed checkpoint in the maintenance plan, with a code search, a time zone review and a dry run. Monitoring watches availability and error logs around the clock, including the night before January 1.

Week format

Search code and templates for calendar years used instead of week-based years, add tests for the edge days.

Time zones

Bring business time zone, PHP, database and cron service to a documented state.

Documents and numbers

Check the first number of the year and the invoice date from the first hour in advance.

Delivery times

Add the 2027 holidays and shutdown days to the calendar of the delivery time logic.

Promotions

Run through end dates, times of day and follow-up promotions on staging.

New Year's night

Keep an eye on cron jobs, exports and error logs in the night before January 1.

The year change is not the only date that belongs in the maintenance plan during these weeks. If you review access and responsibilities before year end, also keep calls from fake service providers in mind, and if you are planning a security assessment for 2027, the article on the penetration test for your shop helps with the decision. Questions about your own shop can be raised via the contact page.

Sources

This article is based on data from: Linux man-pages, strftime(3) and crontab(5); PHP manual, DateTimeInterface::format and easter_date; Python documentation, datetime module; Unicode Technical Standard #35 (Unicode LDML), Part 4 Dates; MariaDB documentation, WEEK, YEARWEEK and default_week_format; Twig documentation, date filter; Shopware documentation, number ranges, delivery times, promotions, architecture decision record Sales-channel business timezone and release notes 6.7.13.0; Symfony documentation, Clock component; German Units and Time Act (EinhZeitG), § 4; German VAT Act (UStG), § 14; German Fiscal Code (AO), § 147; German Civil Code (BGB), §§ 195 and 199; Bavarian Public Holidays Act (BayFTG), Art. 1; German Retail Federation (HDE), Christmas forecast of November 5, 2025. Calendar dates and sample outputs were recalculated on October 16, 2026 with PHP 8.5 and MariaDB 11.8.

Related Articles

Maintenance & updates

Clock Change on 25 Oct: Cron Jobs and Shop Timestamps

On 25 October 2026 the hour from 2 to 3 occurs twice. What that means for cron jobs, timestamps and daily reports in your shop, and how to prepare for it.

15 min read
Security

Penetration Test for Your Shop: When It Is Really Worth It

Scan or penetration test? When a test pays off for an online shop, how to set scope and test environment, and how findings move through to the retest.

20 min read
Maintenance & updates

WordPress security updates: what old branches still get

WordPress 7.1.2 closed a critical flaw and the fix was backported to 4.7 as a courtesy. What older branches receive, what they do not, and when to move up.

20 min read