Subject: [PATCH links] import: respect bookmark creation date when importing

From: Peter Sanchez <[email-masked]>
Date: Tue, 24 Mar 2026 18:13:47 -0600

Thanks to Joshua Wagner for flagging this issue.

Fixes: https://todo.code.netlandish.com/~netlandish/links/135
Changelog-updated: importers now respect creation date in order of
 imported bookmarks.
---
 core/import.go | 92 ++++++++++++++++++++++++--------------------------
 1 file changed, 44 insertions(+), 48 deletions(-)

diff --git a/core/import.go b/core/import.go
index 648daa6..54ab0d9 100644
--- a/core/import.go
+++ b/core/import.go
@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
+	"sort"
 	"links"
 	"links/models"
 	"net/url"
@@ -439,60 +440,50 @@ func ImportFromPinBoard(ctx context.Context, path string,
 		return fmt.Errorf("Error parsing json: %w", err)
 	}
 
-	var totalCount int
-	step := 100
 	srv := server.ForContext(ctx)
+
+	var pinBoardList []*pinBoardObj
+	for dcode.More() {
+		var pbObj *pinBoardObj
+		err := dcode.Decode(&pbObj)
+		if err != nil {
+			srv.Logger().Printf("Error decoding json object in pinboard import: %v", err)
+			continue
+		}
+		pinBoardList = append(pinBoardList, pbObj)
+	}
+
+	// Sort oldest-first so auto-increment IDs match chronological order
+	sort.Slice(pinBoardList, func(i, j int) bool {
+		return pinBoardList[i].CreatedOn().Before(pinBoardList[j].CreatedOn())
+	})
+
+	totalCount := len(pinBoardList)
+	step := 100
 	billEnabled := links.BillingEnabled(srv.Config)
 
-	for {
-		var (
-			pinBoardList []*pinBoardObj
-			count        int
-		)
-		for dcode.More() {
-			var pbObj *pinBoardObj
-			err := dcode.Decode(&pbObj)
-			if err != nil {
-				srv.Logger().Printf("Error decoding json object in pinboard import: %v", err)
-				continue
-			}
-			pinBoardList = append(pinBoardList, pbObj)
-			count++
-			if count == step {
-				break
-			}
+	for start := 0; start < totalCount; start += step {
+		end := start + step
+		if end > totalCount {
+			end = totalCount
+		}
+		batch := pinBoardList[start:end]
+
+		adapter := &importAdapter{
+			elementType: pinBoardType,
+			start:       0,
+			end:         len(batch),
+			pinBoards:   batch,
 		}
 
-		listlen := count
-		if listlen > 0 {
-			adapter := &importAdapter{
-				elementType: pinBoardType,
-				start:       0,
-				end:         listlen,
-				pinBoards:   pinBoardList,
-			}
+		baseURLMap, err := importBaseURLs(ctx, adapter)
+		if err != nil {
+			return err
+		}
 
-			baseURLMap, err := importBaseURLs(ctx, adapter)
-			if err != nil {
-				return err
-			}
-
-			err = importOrgLinks(
-				ctx,
-				adapter,
-				baseURLMap,
-				org,
-				user,
-				billEnabled,
-			)
-			if err != nil {
-				return err
-			}
-
-			totalCount += listlen
-			//time.Sleep(3 * time.Second) // Let the parse url workers catch up
-		} else {
-			break // No more items to process
+		err = importOrgLinks(ctx, adapter, baseURLMap, org, user, billEnabled)
+		if err != nil {
+			return err
 		}
 	}
 
@@ -616,6 +607,11 @@ func ImportFromHTML(ctx context.Context, path string,
 		htmlList = append(htmlList, l)
 	}
 
+	// Sort oldest-first so auto-increment IDs match chronological order
+	sort.Slice(htmlList, func(i, j int) bool {
+		return htmlList[i].CreatedOn().Before(htmlList[j].CreatedOn())
+	})
+
 	var listlen, start, end int
 	step := 100
 
-- 
2.52.0