Received: from mail.netlandish.com (mail.netlandish.com [174.136.98.166])
	by code.netlandish.com (Postfix) with ESMTP id 5798C214
	for <~netlandish/links-dev@lists.code.netlandish.com>; Wed, 25 Mar 2026 00:13:56 +0000 (UTC)
Received-SPF: Pass (mailfrom) identity=mailfrom; client-ip=209.85.128.175; helo=mail-yw1-f175.google.com; envelope-from=peter@netlandish.com; receiver=<UNKNOWN> 
Authentication-Results: mail.netlandish.com;
	dkim=pass (1024-bit key; unprotected) header.d=netlandish.com header.i=@netlandish.com header.b=Kp+BIm8t
Received: from mail-yw1-f175.google.com (mail-yw1-f175.google.com [209.85.128.175])
	by mail.netlandish.com (Postfix) with ESMTP id 6C0431D642C
	for <~netlandish/links-dev@lists.code.netlandish.com>; Wed, 25 Mar 2026 00:13:54 +0000 (UTC)
Received: by mail-yw1-f175.google.com with SMTP id 00721157ae682-78fc4425b6bso21665867b3.1
        for <~netlandish/links-dev@lists.code.netlandish.com>; Tue, 24 Mar 2026 17:13:54 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=netlandish.com; s=google; t=1774397633; x=1775002433; darn=lists.code.netlandish.com;
        h=content-transfer-encoding:mime-version:message-id:date:subject:cc
         :to:from:from:to:cc:subject:date:message-id:reply-to;
        bh=a2iAiG7GdXDEksRgIXr8qFhGmBaW9FYpzE/0HqaLehU=;
        b=Kp+BIm8tqAyfqqogt2nwrYPGzwHJBtcFFhzACVS6bj1HMI+JEJ8hRtMmEujHJGzgtX
         PIQ519CFFr21Fh1dmdvRdB93MxfwNlVbGw4r5k9Cmi6d2tKSrKeHoAmZAwz6QsiJvZbc
         nUmznpfY2huaaka3CuOqhMSMEB9vo0gjkXBjY=
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20251104; t=1774397633; x=1775002433;
        h=content-transfer-encoding:mime-version:message-id:date:subject:cc
         :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date
         :message-id:reply-to;
        bh=a2iAiG7GdXDEksRgIXr8qFhGmBaW9FYpzE/0HqaLehU=;
        b=g+nwtPQs62twCX+c3bOvdl/VgJGqeKwLPQu3BdL6WwpJkRHjVVzdd1bRf0BZOjDycn
         k+X/xDNRcZR9I0knOIyRuW98bh5y38A7WQyq5gXViO+QcKKIvhPh00DeIECqKB33JDAB
         VoBWF4rJSq6DnALG8L39yP/OOi5DNYcBtmwBxjB5z5Zkjg7q762gHz79QUaQTXyy5eLt
         eqYcgFh/pysY57xYjFFophmfpxMweGtYilrLObhL1jvu6jGPlNLdDWlqvXcPTsIqPgtz
         gv2ZQpRs6p5IfhSvNcPiL6xtxFUQiuABoThI6+d5gvmLCUDJIZkDygqphkvZGPDPNe6x
         oY9Q==
X-Gm-Message-State: AOJu0YxZSnuzWzYARxJw9gqv6StnNueO7ksr8xEIZR/f/gfztspJ24wP
	YCFWgdVITPG0l4jzSSvqW8eUYX6hVb2tOROrryfFh1pxcZTrpvrGnH3lEd+HYPrPXRnuTmqxbiM
	wWe5/akg=
X-Gm-Gg: ATEYQzzri3FirlMnlVOUGdijxoMSpuym2RIyJIx9Zaj9IpM0ONAaK9JsRFzuAqYCm+n
	4gLvvjWG5YdoXYpnNJufBZ+N9Wa2M/KfDfxj5LgWt9fNlkxIlcHMCXm5OyWn5LsJc4T+hw0TSCM
	5gLG3oYOnTT6GgkoUfFYX77hXqJHVzGluGbt3tziDPDp+GufX8pc1ZmdIlJPcKzZzIGAZbzG7cf
	We2dF2fdp8O0k7e6bdByueICGWzFS5CnZjkzPQm2H4yRIDrpQtzrpjKYNy3NRjlhxNE3BHRbl2M
	26sn9EU/RFbx4fL/ZZb7u/P+xJSP162u4JPOgbGN7QuHjjarH+WL++irXUeN+pkPFMvZ1WVGxRo
	zeL2t6wPc1vChom7V30cEHGPoqaakrCTi4mygyktLUqpICmPnIt6svOzL3mbHkLNGniduf4v0+Y
	DhFKBgWJutCetEiq5m3tBiGg==
X-Received: by 2002:a05:690c:c50a:b0:79a:c50f:d5ba with SMTP id 00721157ae682-79acf374ca6mr18481237b3.13.1774397633416;
        Tue, 24 Mar 2026 17:13:53 -0700 (PDT)
Received: from localhost ([186.77.197.122])
        by smtp.gmail.com with ESMTPSA id 00721157ae682-79a903e0b8csm80284677b3.12.2026.03.24.17.13.52
        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);
        Tue, 24 Mar 2026 17:13:52 -0700 (PDT)
From: Peter Sanchez <peter@netlandish.com>
To: ~netlandish/links-dev@lists.code.netlandish.com
Cc: Peter Sanchez <peter@netlandish.com>
Subject: [PATCH links] import: respect bookmark creation date when importing
Date: Tue, 24 Mar 2026 18:13:47 -0600
Message-ID: <20260325001350.7627-1-peter@netlandish.com>
X-Mailer: git-send-email 2.52.0
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit

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

